blob: b2640edcc3a8a178b12ecc8137a047c3d7ab3398 [file] [log] [blame]
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001/*
2 * This file is part of wl1271
3 *
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02004 * Copyright (C) 2008-2010 Nokia Corporation
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03005 *
6 * Contact: Luciano Coelho <luciano.coelho@nokia.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 *
22 */
23
24#include <linux/module.h>
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030025#include <linux/firmware.h>
26#include <linux/delay.h>
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030027#include <linux/spi/spi.h>
28#include <linux/crc32.h>
29#include <linux/etherdevice.h>
Juuso Oikarinen1fba4972009-10-08 21:56:32 +030030#include <linux/vmalloc.h>
Juuso Oikarinena1dd8182010-03-18 12:26:31 +020031#include <linux/platform_device.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090032#include <linux/slab.h>
Ido Yariv341b7cd2011-03-31 10:07:01 +020033#include <linux/wl12xx.h>
Ido Yariv95dac04f2011-06-06 14:57:06 +030034#include <linux/sched.h>
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030035
Shahar Levi00d20102010-11-08 11:20:10 +000036#include "wl12xx.h"
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030037#include "wl12xx_80211.h"
Shahar Levi00d20102010-11-08 11:20:10 +000038#include "reg.h"
39#include "io.h"
40#include "event.h"
41#include "tx.h"
42#include "rx.h"
43#include "ps.h"
44#include "init.h"
45#include "debugfs.h"
46#include "cmd.h"
47#include "boot.h"
48#include "testmode.h"
49#include "scan.h"
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030050
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +020051#define WL1271_BOOT_RETRIES 3
52
Juuso Oikarinen8a080482009-10-13 12:47:44 +030053static struct conf_drv_settings default_conf = {
54 .sg = {
Eliad Peller3be41122011-08-14 13:17:19 +030055 .params = {
56 [CONF_SG_ACL_BT_MASTER_MIN_BR] = 10,
57 [CONF_SG_ACL_BT_MASTER_MAX_BR] = 180,
58 [CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10,
59 [CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180,
60 [CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10,
61 [CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80,
62 [CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10,
63 [CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80,
64 [CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8,
65 [CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8,
66 [CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20,
67 [CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20,
68 [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20,
69 [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35,
70 [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16,
71 [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35,
72 [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32,
73 [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50,
74 [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28,
75 [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50,
76 [CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10,
77 [CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20,
78 [CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75,
79 [CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15,
80 [CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27,
81 [CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17,
82 /* active scan params */
83 [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
84 [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
85 [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
86 /* passive scan params */
87 [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800,
88 [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200,
89 [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
90 /* passive scan in dual antenna params */
91 [CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0,
92 [CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0,
93 [CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0,
94 /* general params */
95 [CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1,
96 [CONF_SG_ANTENNA_CONFIGURATION] = 0,
97 [CONF_SG_BEACON_MISS_PERCENT] = 60,
98 [CONF_SG_DHCP_TIME] = 5000,
99 [CONF_SG_RXT] = 1200,
100 [CONF_SG_TXT] = 1000,
101 [CONF_SG_ADAPTIVE_RXT_TXT] = 1,
102 [CONF_SG_GENERAL_USAGE_BIT_MAP] = 3,
103 [CONF_SG_HV3_MAX_SERVED] = 6,
104 [CONF_SG_PS_POLL_TIMEOUT] = 10,
105 [CONF_SG_UPSD_TIMEOUT] = 10,
106 [CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2,
107 [CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5,
108 [CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30,
109 /* AP params */
110 [CONF_AP_BEACON_MISS_TX] = 3,
111 [CONF_AP_RX_WINDOW_AFTER_BEACON] = 10,
112 [CONF_AP_BEACON_WINDOW_INTERVAL] = 2,
113 [CONF_AP_CONNECTION_PROTECTION_TIME] = 0,
114 [CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25,
115 [CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25,
Arik Nemtsov801f8702011-04-18 14:15:20 +0300116 },
Juuso Oikarinen1b00f542010-03-18 12:26:30 +0200117 .state = CONF_SG_PROTECTIVE,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300118 },
119 .rx = {
120 .rx_msdu_life_time = 512000,
121 .packet_detection_threshold = 0,
122 .ps_poll_timeout = 15,
123 .upsd_timeout = 15,
Arik Nemtsov5f704d12011-04-18 14:15:21 +0300124 .rts_threshold = IEEE80211_MAX_RTS_THRESHOLD,
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200125 .rx_cca_threshold = 0,
126 .irq_blk_threshold = 0xFFFF,
127 .irq_pkt_threshold = 0,
128 .irq_timeout = 600,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300129 .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY,
130 },
131 .tx = {
132 .tx_energy_detection = 0,
Arik Nemtsov1e05a812010-10-16 17:44:51 +0200133 .sta_rc_conf = {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +0300134 .enabled_rates = 0,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300135 .short_retry_limit = 10,
136 .long_retry_limit = 10,
Arik Nemtsov1e05a812010-10-16 17:44:51 +0200137 .aflags = 0,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300138 },
139 .ac_conf_count = 4,
140 .ac_conf = {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200141 [CONF_TX_AC_BE] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300142 .ac = CONF_TX_AC_BE,
143 .cw_min = 15,
144 .cw_max = 63,
145 .aifsn = 3,
146 .tx_op_limit = 0,
147 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200148 [CONF_TX_AC_BK] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300149 .ac = CONF_TX_AC_BK,
150 .cw_min = 15,
151 .cw_max = 63,
152 .aifsn = 7,
153 .tx_op_limit = 0,
154 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200155 [CONF_TX_AC_VI] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300156 .ac = CONF_TX_AC_VI,
157 .cw_min = 15,
158 .cw_max = 63,
159 .aifsn = CONF_TX_AIFS_PIFS,
160 .tx_op_limit = 3008,
161 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200162 [CONF_TX_AC_VO] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300163 .ac = CONF_TX_AC_VO,
164 .cw_min = 15,
165 .cw_max = 63,
166 .aifsn = CONF_TX_AIFS_PIFS,
167 .tx_op_limit = 1504,
168 },
169 },
Arik Nemtsov3618f302011-06-26 10:36:03 +0300170 .max_tx_retries = 100,
171 .ap_aging_period = 300,
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200172 .tid_conf_count = 4,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300173 .tid_conf = {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200174 [CONF_TX_AC_BE] = {
175 .queue_id = CONF_TX_AC_BE,
176 .channel_type = CONF_CHANNEL_TYPE_EDCF,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300177 .tsid = CONF_TX_AC_BE,
178 .ps_scheme = CONF_PS_SCHEME_LEGACY,
179 .ack_policy = CONF_ACK_POLICY_LEGACY,
180 .apsd_conf = {0, 0},
181 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200182 [CONF_TX_AC_BK] = {
183 .queue_id = CONF_TX_AC_BK,
184 .channel_type = CONF_CHANNEL_TYPE_EDCF,
185 .tsid = CONF_TX_AC_BK,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300186 .ps_scheme = CONF_PS_SCHEME_LEGACY,
187 .ack_policy = CONF_ACK_POLICY_LEGACY,
188 .apsd_conf = {0, 0},
189 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200190 [CONF_TX_AC_VI] = {
191 .queue_id = CONF_TX_AC_VI,
192 .channel_type = CONF_CHANNEL_TYPE_EDCF,
193 .tsid = CONF_TX_AC_VI,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300194 .ps_scheme = CONF_PS_SCHEME_LEGACY,
195 .ack_policy = CONF_ACK_POLICY_LEGACY,
196 .apsd_conf = {0, 0},
197 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200198 [CONF_TX_AC_VO] = {
199 .queue_id = CONF_TX_AC_VO,
200 .channel_type = CONF_CHANNEL_TYPE_EDCF,
201 .tsid = CONF_TX_AC_VO,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300202 .ps_scheme = CONF_PS_SCHEME_LEGACY,
203 .ack_policy = CONF_ACK_POLICY_LEGACY,
204 .apsd_conf = {0, 0},
205 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300206 },
207 .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD,
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200208 .tx_compl_timeout = 700,
Juuso Oikarinenebba60c2010-04-01 11:38:20 +0300209 .tx_compl_threshold = 4,
210 .basic_rate = CONF_HW_BIT_RATE_1MBPS,
211 .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS,
Arik Nemtsov1e05a812010-10-16 17:44:51 +0200212 .tmpl_short_retry_limit = 10,
213 .tmpl_long_retry_limit = 10,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300214 },
215 .conn = {
216 .wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
Juuso Oikarinen50c500a2010-04-01 11:38:22 +0300217 .listen_interval = 1,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300218 .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED,
Shahar Levibc76b942011-05-11 11:14:22 +0300219 .bcn_filt_ie_count = 2,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300220 .bcn_filt_ie = {
221 [0] = {
222 .ie = WLAN_EID_CHANNEL_SWITCH,
223 .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE,
Shahar Levibc76b942011-05-11 11:14:22 +0300224 },
225 [1] = {
226 .ie = WLAN_EID_HT_INFORMATION,
227 .rule = CONF_BCN_RULE_PASS_ON_CHANGE,
228 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300229 },
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200230 .synch_fail_thold = 10,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300231 .bss_lose_timeout = 100,
232 .beacon_rx_timeout = 10000,
233 .broadcast_timeout = 20000,
234 .rx_broadcast_in_ps = 1,
Juuso Oikarinen90494a92010-07-08 17:50:00 +0300235 .ps_poll_threshold = 10,
236 .ps_poll_recovery_period = 700,
Juuso Oikarinen11f70f92009-10-13 12:47:46 +0300237 .bet_enable = CONF_BET_MODE_ENABLE,
Ohad Ben-Cohen958b20e2011-03-14 18:53:10 +0200238 .bet_max_consecutive = 50,
Eliad Pellera879ed72011-08-23 16:37:02 +0300239 .psm_entry_retries = 8,
Shahar Levi23708412011-04-13 14:52:50 +0300240 .psm_exit_retries = 16,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +0200241 .psm_entry_nullfunc_retries = 3,
Juuso Oikarinen50c500a2010-04-01 11:38:22 +0300242 .keep_alive_interval = 55000,
243 .max_listen_interval = 20,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300244 },
Luciano Coelho6e92b412009-12-11 15:40:50 +0200245 .itrim = {
246 .enable = false,
247 .timeout = 50000,
Juuso Oikarinen38ad2d82009-12-11 15:41:08 +0200248 },
249 .pm_config = {
250 .host_clk_settling_time = 5000,
251 .host_fast_wakeup_support = false
Juuso Oikarinen00236aed2010-04-09 11:07:30 +0300252 },
253 .roam_trigger = {
Juuso Oikarinen00236aed2010-04-09 11:07:30 +0300254 .trigger_pacing = 1,
255 .avg_weight_rssi_beacon = 20,
256 .avg_weight_rssi_data = 10,
257 .avg_weight_snr_beacon = 20,
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100258 .avg_weight_snr_data = 10,
Juuso Oikarinenbea39d62010-09-21 08:14:31 +0200259 },
260 .scan = {
261 .min_dwell_time_active = 7500,
262 .max_dwell_time_active = 30000,
Juuso Oikarinenea45b2c2011-01-24 07:01:54 +0100263 .min_dwell_time_passive = 100000,
264 .max_dwell_time_passive = 100000,
Juuso Oikarinenbea39d62010-09-21 08:14:31 +0200265 .num_probe_reqs = 2,
266 },
Luciano Coelho3a9d60e2011-05-10 14:06:31 +0300267 .sched_scan = {
268 /* sched_scan requires dwell times in TU instead of TU/1000 */
Luciano Coelho221737d2011-09-02 14:28:22 +0300269 .min_dwell_time_active = 30,
270 .max_dwell_time_active = 60,
Luciano Coelho3a9d60e2011-05-10 14:06:31 +0300271 .dwell_time_passive = 100,
Luciano Coelho50a66d72011-05-27 15:34:47 +0300272 .dwell_time_dfs = 150,
Luciano Coelho3a9d60e2011-05-10 14:06:31 +0300273 .num_probe_reqs = 2,
274 .rssi_threshold = -90,
275 .snr_threshold = 0,
276 },
Juuso Oikarinen644a4862010-10-05 13:11:56 +0200277 .rf = {
278 .tx_per_channel_power_compensation_2 = {
279 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
280 },
281 .tx_per_channel_power_compensation_5 = {
282 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
283 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
284 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
285 },
286 },
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100287 .ht = {
Arik Nemtsov0f9c8252011-08-17 10:45:49 +0300288 .rx_ba_win_size = 8,
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100289 .tx_ba_win_size = 64,
290 .inactivity_timeout = 10000,
Arik Nemtsov0f9c8252011-08-17 10:45:49 +0300291 .tx_ba_tid_bitmap = CONF_TX_BA_ENABLED_TID_BITMAP,
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100292 },
Shahar Levi13b107d2011-03-06 16:32:12 +0200293 .mem_wl127x = {
Eliad Pellerfe5ef092011-02-02 09:59:36 +0200294 .num_stations = 1,
295 .ssid_profiles = 1,
296 .rx_block_num = 70,
297 .tx_min_block_num = 40,
Ido Yariv4cf557f2011-04-18 16:45:10 +0300298 .dynamic_memory = 1,
Ido Yarivb16d4b62011-03-01 15:14:44 +0200299 .min_req_tx_blocks = 100,
Eliad Pellerc8bde242011-02-02 09:59:35 +0200300 .min_req_rx_blocks = 22,
301 .tx_min = 27,
Shahar Levi13b107d2011-03-06 16:32:12 +0200302 },
303 .mem_wl128x = {
304 .num_stations = 1,
305 .ssid_profiles = 1,
306 .rx_block_num = 40,
307 .tx_min_block_num = 40,
308 .dynamic_memory = 1,
309 .min_req_tx_blocks = 45,
310 .min_req_rx_blocks = 22,
311 .tx_min = 27,
312 },
Shahar Leviff868432011-04-11 15:41:46 +0300313 .fm_coex = {
314 .enable = true,
315 .swallow_period = 5,
316 .n_divider_fref_set_1 = 0xff, /* default */
317 .n_divider_fref_set_2 = 12,
318 .m_divider_fref_set_1 = 148,
319 .m_divider_fref_set_2 = 0xffff, /* default */
320 .coex_pll_stabilization_time = 0xffffffff, /* default */
321 .ldo_stabilization_time = 0xffff, /* default */
322 .fm_disturbed_band_margin = 0xff, /* default */
323 .swallow_clk_diff = 0xff, /* default */
324 },
Eliad Pellerf84673d2011-05-15 11:10:28 +0300325 .rx_streaming = {
326 .duration = 150,
327 .queues = 0x1,
328 .interval = 20,
Eliad Peller77ddaa12011-05-15 11:10:29 +0300329 .always = 0,
Eliad Pellerf84673d2011-05-15 11:10:28 +0300330 },
Ido Yariv95dac04f2011-06-06 14:57:06 +0300331 .fwlog = {
332 .mode = WL12XX_FWLOG_ON_DEMAND,
333 .mem_blocks = 2,
334 .severity = 0,
335 .timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED,
336 .output = WL12XX_FWLOG_OUTPUT_HOST,
337 .threshold = 0,
338 },
Luciano Coelhoafb7d3c2011-04-01 20:48:02 +0300339 .hci_io_ds = HCI_IO_DS_6MA,
Eliad Pellerfa6ad9f2011-08-14 13:17:14 +0300340 .rate = {
341 .rate_retry_score = 32000,
342 .per_add = 8192,
343 .per_th1 = 2048,
344 .per_th2 = 4096,
345 .max_per = 8100,
346 .inverse_curiosity_factor = 5,
347 .tx_fail_low_th = 4,
348 .tx_fail_high_th = 10,
349 .per_alpha_shift = 4,
350 .per_add_shift = 13,
351 .per_beta1_shift = 10,
352 .per_beta2_shift = 8,
353 .rate_check_up = 2,
354 .rate_check_down = 12,
355 .rate_retry_policy = {
356 0x00, 0x00, 0x00, 0x00, 0x00,
357 0x00, 0x00, 0x00, 0x00, 0x00,
358 0x00, 0x00, 0x00,
359 },
360 },
Eliad Peller94877752011-08-28 15:11:56 +0300361 .hangover = {
362 .recover_time = 0,
363 .hangover_period = 20,
364 .dynamic_mode = 1,
365 .early_termination_mode = 1,
366 .max_period = 20,
367 .min_period = 1,
368 .increase_delta = 1,
369 .decrease_delta = 2,
370 .quiet_time = 4,
371 .increase_time = 1,
372 .window_size = 16,
373 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300374};
375
Ido Yariv95dac04f2011-06-06 14:57:06 +0300376static char *fwlog_param;
Eliad Peller2a5bff02011-08-25 18:10:59 +0300377static bool bug_on_recovery;
Ido Yariv95dac04f2011-06-06 14:57:06 +0300378
Arik Nemtsov7dece1c2011-04-18 14:15:28 +0300379static void __wl1271_op_remove_interface(struct wl1271 *wl,
Eliad Peller536129c2011-10-05 11:55:45 +0200380 struct ieee80211_vif *vif,
Arik Nemtsov7dece1c2011-04-18 14:15:28 +0300381 bool reset_tx_queues);
Eliad Peller170d0e62011-10-05 11:56:06 +0200382static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200383
384
Juuso Oikarinena1dd8182010-03-18 12:26:31 +0200385static void wl1271_device_release(struct device *dev)
386{
387
388}
389
390static struct platform_device wl1271_device = {
391 .name = "wl1271",
392 .id = -1,
393
394 /* device model insists to have a release function */
395 .dev = {
396 .release = wl1271_device_release,
397 },
398};
399
Juuso Oikarinenf9f774c2011-03-21 10:43:36 +0200400static DEFINE_MUTEX(wl_list_mutex);
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300401static LIST_HEAD(wl_list);
402
Eliad Pellerba8447f2011-10-10 10:13:00 +0200403static int wl1271_check_operstate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
404 unsigned char operstate)
Eliad Pelleref4b29e2011-06-06 13:03:12 +0300405{
406 int ret;
Eliad Peller0603d892011-10-05 11:55:51 +0200407
Eliad Pelleref4b29e2011-06-06 13:03:12 +0300408 if (operstate != IF_OPER_UP)
409 return 0;
410
Eliad Peller8181aec2011-10-10 10:13:04 +0200411 if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags))
Eliad Pelleref4b29e2011-06-06 13:03:12 +0300412 return 0;
413
Eliad Peller154da672011-10-05 11:55:53 +0200414 ret = wl12xx_cmd_set_peer_state(wl, wlvif->sta.hlid);
Eliad Pelleref4b29e2011-06-06 13:03:12 +0300415 if (ret < 0)
416 return ret;
417
Eliad Peller0603d892011-10-05 11:55:51 +0200418 wl12xx_croc(wl, wlvif->role_id);
Eliad Peller251c1772011-08-14 13:17:17 +0300419
Eliad Pelleref4b29e2011-06-06 13:03:12 +0300420 wl1271_info("Association completed.");
421 return 0;
422}
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300423static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
424 void *arg)
425{
426 struct net_device *dev = arg;
427 struct wireless_dev *wdev;
428 struct wiphy *wiphy;
429 struct ieee80211_hw *hw;
430 struct wl1271 *wl;
431 struct wl1271 *wl_temp;
Eliad Pellerba8447f2011-10-10 10:13:00 +0200432 struct wl12xx_vif *wlvif;
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300433 int ret = 0;
434
435 /* Check that this notification is for us. */
436 if (what != NETDEV_CHANGE)
437 return NOTIFY_DONE;
438
439 wdev = dev->ieee80211_ptr;
440 if (wdev == NULL)
441 return NOTIFY_DONE;
442
443 wiphy = wdev->wiphy;
444 if (wiphy == NULL)
445 return NOTIFY_DONE;
446
447 hw = wiphy_priv(wiphy);
448 if (hw == NULL)
449 return NOTIFY_DONE;
450
451 wl_temp = hw->priv;
Juuso Oikarinenf9f774c2011-03-21 10:43:36 +0200452 mutex_lock(&wl_list_mutex);
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300453 list_for_each_entry(wl, &wl_list, list) {
454 if (wl == wl_temp)
455 break;
456 }
Juuso Oikarinenf9f774c2011-03-21 10:43:36 +0200457 mutex_unlock(&wl_list_mutex);
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300458 if (wl != wl_temp)
459 return NOTIFY_DONE;
460
461 mutex_lock(&wl->mutex);
462
463 if (wl->state == WL1271_STATE_OFF)
464 goto out;
465
Eliad Pellerba8447f2011-10-10 10:13:00 +0200466 wl12xx_for_each_wlvif_sta(wl, wlvif) {
467 if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
468 continue;
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300469
Eliad Pellerba8447f2011-10-10 10:13:00 +0200470 ret = wl1271_ps_elp_wakeup(wl);
471 if (ret < 0)
472 goto out;
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300473
Eliad Pellerba8447f2011-10-10 10:13:00 +0200474 wl1271_check_operstate(wl, wlvif, dev->operstate);
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300475
Eliad Pellerba8447f2011-10-10 10:13:00 +0200476 wl1271_ps_elp_sleep(wl);
477 }
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300478out:
479 mutex_unlock(&wl->mutex);
480
481 return NOTIFY_OK;
482}
483
Juuso Oikarinenb7417d92010-11-10 11:27:19 +0100484static int wl1271_reg_notify(struct wiphy *wiphy,
Luciano Coelho573c67c2010-11-26 13:44:59 +0200485 struct regulatory_request *request)
486{
Juuso Oikarinenb7417d92010-11-10 11:27:19 +0100487 struct ieee80211_supported_band *band;
488 struct ieee80211_channel *ch;
489 int i;
490
491 band = wiphy->bands[IEEE80211_BAND_5GHZ];
492 for (i = 0; i < band->n_channels; i++) {
493 ch = &band->channels[i];
494 if (ch->flags & IEEE80211_CHAN_DISABLED)
495 continue;
496
497 if (ch->flags & IEEE80211_CHAN_RADAR)
498 ch->flags |= IEEE80211_CHAN_NO_IBSS |
499 IEEE80211_CHAN_PASSIVE_SCAN;
500
501 }
502
503 return 0;
504}
505
Eliad Peller9eb599e2011-10-10 10:12:59 +0200506static int wl1271_set_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
507 bool enable)
Eliad Peller77ddaa12011-05-15 11:10:29 +0300508{
509 int ret = 0;
510
511 /* we should hold wl->mutex */
Eliad Peller9eb599e2011-10-10 10:12:59 +0200512 ret = wl1271_acx_ps_rx_streaming(wl, wlvif, enable);
Eliad Peller77ddaa12011-05-15 11:10:29 +0300513 if (ret < 0)
514 goto out;
515
516 if (enable)
Eliad Peller0744bdb2011-10-10 10:13:05 +0200517 set_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags);
Eliad Peller77ddaa12011-05-15 11:10:29 +0300518 else
Eliad Peller0744bdb2011-10-10 10:13:05 +0200519 clear_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags);
Eliad Peller77ddaa12011-05-15 11:10:29 +0300520out:
521 return ret;
522}
523
524/*
525 * this function is being called when the rx_streaming interval
526 * has beed changed or rx_streaming should be disabled
527 */
Eliad Peller9eb599e2011-10-10 10:12:59 +0200528int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Eliad Peller77ddaa12011-05-15 11:10:29 +0300529{
530 int ret = 0;
531 int period = wl->conf.rx_streaming.interval;
532
533 /* don't reconfigure if rx_streaming is disabled */
Eliad Peller0744bdb2011-10-10 10:13:05 +0200534 if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
Eliad Peller77ddaa12011-05-15 11:10:29 +0300535 goto out;
536
537 /* reconfigure/disable according to new streaming_period */
538 if (period &&
Eliad Pellerba8447f2011-10-10 10:13:00 +0200539 test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
Eliad Peller77ddaa12011-05-15 11:10:29 +0300540 (wl->conf.rx_streaming.always ||
541 test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
Eliad Peller9eb599e2011-10-10 10:12:59 +0200542 ret = wl1271_set_rx_streaming(wl, wlvif, true);
Eliad Peller77ddaa12011-05-15 11:10:29 +0300543 else {
Eliad Peller9eb599e2011-10-10 10:12:59 +0200544 ret = wl1271_set_rx_streaming(wl, wlvif, false);
Eliad Peller77ddaa12011-05-15 11:10:29 +0300545 /* don't cancel_work_sync since we might deadlock */
Eliad Peller9eb599e2011-10-10 10:12:59 +0200546 del_timer_sync(&wlvif->rx_streaming_timer);
Eliad Peller77ddaa12011-05-15 11:10:29 +0300547 }
548out:
549 return ret;
550}
551
552static void wl1271_rx_streaming_enable_work(struct work_struct *work)
553{
554 int ret;
Eliad Peller9eb599e2011-10-10 10:12:59 +0200555 struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif,
556 rx_streaming_enable_work);
557 struct wl1271 *wl = wlvif->wl;
Eliad Peller77ddaa12011-05-15 11:10:29 +0300558
559 mutex_lock(&wl->mutex);
560
Eliad Peller0744bdb2011-10-10 10:13:05 +0200561 if (test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags) ||
Eliad Pellerba8447f2011-10-10 10:13:00 +0200562 !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
Eliad Peller77ddaa12011-05-15 11:10:29 +0300563 (!wl->conf.rx_streaming.always &&
564 !test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
565 goto out;
566
567 if (!wl->conf.rx_streaming.interval)
568 goto out;
569
570 ret = wl1271_ps_elp_wakeup(wl);
571 if (ret < 0)
572 goto out;
573
Eliad Peller9eb599e2011-10-10 10:12:59 +0200574 ret = wl1271_set_rx_streaming(wl, wlvif, true);
Eliad Peller77ddaa12011-05-15 11:10:29 +0300575 if (ret < 0)
576 goto out_sleep;
577
578 /* stop it after some time of inactivity */
Eliad Peller9eb599e2011-10-10 10:12:59 +0200579 mod_timer(&wlvif->rx_streaming_timer,
Eliad Peller77ddaa12011-05-15 11:10:29 +0300580 jiffies + msecs_to_jiffies(wl->conf.rx_streaming.duration));
581
582out_sleep:
583 wl1271_ps_elp_sleep(wl);
584out:
585 mutex_unlock(&wl->mutex);
586}
587
588static void wl1271_rx_streaming_disable_work(struct work_struct *work)
589{
590 int ret;
Eliad Peller9eb599e2011-10-10 10:12:59 +0200591 struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif,
592 rx_streaming_disable_work);
593 struct wl1271 *wl = wlvif->wl;
Eliad Peller77ddaa12011-05-15 11:10:29 +0300594
595 mutex_lock(&wl->mutex);
596
Eliad Peller0744bdb2011-10-10 10:13:05 +0200597 if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
Eliad Peller77ddaa12011-05-15 11:10:29 +0300598 goto out;
599
600 ret = wl1271_ps_elp_wakeup(wl);
601 if (ret < 0)
602 goto out;
603
Eliad Peller9eb599e2011-10-10 10:12:59 +0200604 ret = wl1271_set_rx_streaming(wl, wlvif, false);
Eliad Peller77ddaa12011-05-15 11:10:29 +0300605 if (ret)
606 goto out_sleep;
607
608out_sleep:
609 wl1271_ps_elp_sleep(wl);
610out:
611 mutex_unlock(&wl->mutex);
612}
613
614static void wl1271_rx_streaming_timer(unsigned long data)
615{
Eliad Peller9eb599e2011-10-10 10:12:59 +0200616 struct wl12xx_vif *wlvif = (struct wl12xx_vif *)data;
617 struct wl1271 *wl = wlvif->wl;
618 ieee80211_queue_work(wl->hw, &wlvif->rx_streaming_disable_work);
Eliad Peller77ddaa12011-05-15 11:10:29 +0300619}
620
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300621static void wl1271_conf_init(struct wl1271 *wl)
622{
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300623
624 /*
625 * This function applies the default configuration to the driver. This
626 * function is invoked upon driver load (spi probe.)
627 *
628 * The configuration is stored in a run-time structure in order to
629 * facilitate for run-time adjustment of any of the parameters. Making
630 * changes to the configuration structure will apply the new values on
631 * the next interface up (wl1271_op_start.)
632 */
633
634 /* apply driver default configuration */
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300635 memcpy(&wl->conf, &default_conf, sizeof(default_conf));
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300636
Ido Yariv95dac04f2011-06-06 14:57:06 +0300637 /* Adjust settings according to optional module parameters */
638 if (fwlog_param) {
639 if (!strcmp(fwlog_param, "continuous")) {
640 wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
641 } else if (!strcmp(fwlog_param, "ondemand")) {
642 wl->conf.fwlog.mode = WL12XX_FWLOG_ON_DEMAND;
643 } else if (!strcmp(fwlog_param, "dbgpins")) {
644 wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
645 wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_DBG_PINS;
646 } else if (!strcmp(fwlog_param, "disable")) {
647 wl->conf.fwlog.mem_blocks = 0;
648 wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_NONE;
649 } else {
650 wl1271_error("Unknown fwlog parameter %s", fwlog_param);
651 }
652 }
653}
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300654
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300655static int wl1271_plt_init(struct wl1271 *wl)
656{
Luciano Coelho12419cc2010-02-18 13:25:44 +0200657 struct conf_tx_ac_category *conf_ac;
658 struct conf_tx_tid *conf_tid;
659 int ret, i;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300660
Shahar Levi49d750ca2011-03-06 16:32:09 +0200661 if (wl->chip.id == CHIP_ID_1283_PG20)
662 ret = wl128x_cmd_general_parms(wl);
663 else
664 ret = wl1271_cmd_general_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200665 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200666 return ret;
667
Shahar Levi49d750ca2011-03-06 16:32:09 +0200668 if (wl->chip.id == CHIP_ID_1283_PG20)
669 ret = wl128x_cmd_radio_parms(wl);
670 else
671 ret = wl1271_cmd_radio_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200672 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200673 return ret;
674
Shahar Levi49d750ca2011-03-06 16:32:09 +0200675 if (wl->chip.id != CHIP_ID_1283_PG20) {
676 ret = wl1271_cmd_ext_radio_parms(wl);
677 if (ret < 0)
678 return ret;
679 }
Juuso Oikarinen644a4862010-10-05 13:11:56 +0200680 if (ret < 0)
681 return ret;
682
Shahar Levi48a61472011-03-06 16:32:08 +0200683 /* Chip-specific initializations */
684 ret = wl1271_chip_specific_init(wl);
685 if (ret < 0)
686 return ret;
687
Eliad Peller92c77c72011-10-05 11:55:40 +0200688 ret = wl1271_init_templates_config(wl);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200689 if (ret < 0)
690 return ret;
691
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300692 ret = wl1271_acx_init_mem_config(wl);
693 if (ret < 0)
694 return ret;
695
Luciano Coelho12419cc2010-02-18 13:25:44 +0200696 /* PHY layer config */
697 ret = wl1271_init_phy_config(wl);
698 if (ret < 0)
699 goto out_free_memmap;
700
701 ret = wl1271_acx_dco_itrim_params(wl);
702 if (ret < 0)
703 goto out_free_memmap;
704
705 /* Initialize connection monitoring thresholds */
Eliad Peller0603d892011-10-05 11:55:51 +0200706 ret = wl1271_acx_conn_monit_params(wl, NULL, false); /* TODO: fix */
Luciano Coelho12419cc2010-02-18 13:25:44 +0200707 if (ret < 0)
708 goto out_free_memmap;
709
710 /* Bluetooth WLAN coexistence */
711 ret = wl1271_init_pta(wl);
712 if (ret < 0)
713 goto out_free_memmap;
714
Shahar Leviff868432011-04-11 15:41:46 +0300715 /* FM WLAN coexistence */
716 ret = wl1271_acx_fm_coex(wl);
717 if (ret < 0)
718 goto out_free_memmap;
719
Luciano Coelho12419cc2010-02-18 13:25:44 +0200720 /* Energy detection */
721 ret = wl1271_init_energy_detection(wl);
722 if (ret < 0)
723 goto out_free_memmap;
724
Eliad Peller7f0979882011-08-14 13:17:06 +0300725 ret = wl12xx_acx_mem_cfg(wl);
Gery Kahn1ec610e2011-02-01 03:03:08 -0600726 if (ret < 0)
727 goto out_free_memmap;
728
Luciano Coelho12419cc2010-02-18 13:25:44 +0200729 /* Default fragmentation threshold */
Arik Nemtsov68d069c2010-11-08 10:51:07 +0100730 ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200731 if (ret < 0)
732 goto out_free_memmap;
733
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200734 /* Default TID/AC configuration */
735 BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200736 for (i = 0; i < wl->conf.tx.tid_conf_count; i++) {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200737 conf_ac = &wl->conf.tx.ac_conf[i];
Eliad Peller0603d892011-10-05 11:55:51 +0200738 /* TODO: fix */
739 ret = wl1271_acx_ac_cfg(wl, NULL, conf_ac->ac, conf_ac->cw_min,
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200740 conf_ac->cw_max, conf_ac->aifsn,
741 conf_ac->tx_op_limit);
742 if (ret < 0)
743 goto out_free_memmap;
744
Luciano Coelho12419cc2010-02-18 13:25:44 +0200745 conf_tid = &wl->conf.tx.tid_conf[i];
Eliad Peller0603d892011-10-05 11:55:51 +0200746 /* TODO: fix */
747 ret = wl1271_acx_tid_cfg(wl, NULL, conf_tid->queue_id,
Luciano Coelho12419cc2010-02-18 13:25:44 +0200748 conf_tid->channel_type,
749 conf_tid->tsid,
750 conf_tid->ps_scheme,
751 conf_tid->ack_policy,
752 conf_tid->apsd_conf[0],
753 conf_tid->apsd_conf[1]);
754 if (ret < 0)
755 goto out_free_memmap;
756 }
757
Luciano Coelho12419cc2010-02-18 13:25:44 +0200758 /* Enable data path */
Luciano Coelho94210892009-12-11 15:40:55 +0200759 ret = wl1271_cmd_data_path(wl, 1);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300760 if (ret < 0)
Luciano Coelho12419cc2010-02-18 13:25:44 +0200761 goto out_free_memmap;
762
763 /* Configure for CAM power saving (ie. always active) */
764 ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
765 if (ret < 0)
766 goto out_free_memmap;
767
768 /* configure PM */
769 ret = wl1271_acx_pm_config(wl);
770 if (ret < 0)
771 goto out_free_memmap;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300772
773 return 0;
Luciano Coelho12419cc2010-02-18 13:25:44 +0200774
775 out_free_memmap:
776 kfree(wl->target_mem_map);
777 wl->target_mem_map = NULL;
778
779 return ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300780}
781
Arik Nemtsov9b17f1b2011-08-14 13:17:35 +0300782static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_pkts)
Arik Nemtsovb622d992011-02-23 00:22:31 +0200783{
Arik Nemtsovda032092011-08-25 12:43:15 +0300784 bool fw_ps, single_sta;
Arik Nemtsovb622d992011-02-23 00:22:31 +0200785
Arik Nemtsovb622d992011-02-23 00:22:31 +0200786 fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
Arik Nemtsovda032092011-08-25 12:43:15 +0300787 single_sta = (wl->active_sta_count == 1);
Arik Nemtsovb622d992011-02-23 00:22:31 +0200788
789 /*
790 * Wake up from high level PS if the STA is asleep with too little
Arik Nemtsov9b17f1b2011-08-14 13:17:35 +0300791 * packets in FW or if the STA is awake.
Arik Nemtsovb622d992011-02-23 00:22:31 +0200792 */
Arik Nemtsov9b17f1b2011-08-14 13:17:35 +0300793 if (!fw_ps || tx_pkts < WL1271_PS_STA_MAX_PACKETS)
Arik Nemtsovb622d992011-02-23 00:22:31 +0200794 wl1271_ps_link_end(wl, hlid);
795
Arik Nemtsovda032092011-08-25 12:43:15 +0300796 /*
797 * Start high-level PS if the STA is asleep with enough blocks in FW.
798 * Make an exception if this is the only connected station. In this
799 * case FW-memory congestion is not a problem.
800 */
801 else if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
Arik Nemtsovb622d992011-02-23 00:22:31 +0200802 wl1271_ps_link_start(wl, hlid, true);
803}
804
Arik Nemtsov9b17f1b2011-08-14 13:17:35 +0300805static void wl12xx_irq_update_links_status(struct wl1271 *wl,
Eliad Pellerc7ffb902011-10-05 11:56:05 +0200806 struct wl12xx_vif *wlvif,
Arik Nemtsov9b17f1b2011-08-14 13:17:35 +0300807 struct wl12xx_fw_status *status)
Arik Nemtsovb622d992011-02-23 00:22:31 +0200808{
Eliad Pellerc7ffb902011-10-05 11:56:05 +0200809 struct wl1271_link *lnk;
Arik Nemtsovb622d992011-02-23 00:22:31 +0200810 u32 cur_fw_ps_map;
Arik Nemtsov9b17f1b2011-08-14 13:17:35 +0300811 u8 hlid, cnt;
812
813 /* TODO: also use link_fast_bitmap here */
Arik Nemtsovb622d992011-02-23 00:22:31 +0200814
815 cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
816 if (wl->ap_fw_ps_map != cur_fw_ps_map) {
817 wl1271_debug(DEBUG_PSM,
818 "link ps prev 0x%x cur 0x%x changed 0x%x",
819 wl->ap_fw_ps_map, cur_fw_ps_map,
820 wl->ap_fw_ps_map ^ cur_fw_ps_map);
821
822 wl->ap_fw_ps_map = cur_fw_ps_map;
823 }
824
Eliad Pellerc7ffb902011-10-05 11:56:05 +0200825 for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS) {
826 lnk = &wl->links[hlid];
827 cnt = status->tx_lnk_free_pkts[hlid] - lnk->prev_freed_pkts;
Arik Nemtsovb622d992011-02-23 00:22:31 +0200828
Eliad Pellerc7ffb902011-10-05 11:56:05 +0200829 lnk->prev_freed_pkts = status->tx_lnk_free_pkts[hlid];
830 lnk->allocated_pkts -= cnt;
Arik Nemtsovb622d992011-02-23 00:22:31 +0200831
Eliad Pellerc7ffb902011-10-05 11:56:05 +0200832 wl12xx_irq_ps_regulate_link(wl, hlid, lnk->allocated_pkts);
Arik Nemtsovb622d992011-02-23 00:22:31 +0200833 }
834}
835
Eliad Peller4d56ad92011-08-14 13:17:05 +0300836static void wl12xx_fw_status(struct wl1271 *wl,
837 struct wl12xx_fw_status *status)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300838{
Eliad Peller536129c2011-10-05 11:55:45 +0200839 struct ieee80211_vif *vif = wl->vif; /* TODO: get as param */
840 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Juuso Oikarinenac5e1e32010-02-22 08:38:38 +0200841 struct timespec ts;
Shahar Levi13b107d2011-03-06 16:32:12 +0200842 u32 old_tx_blk_count = wl->tx_blocks_available;
Eliad Peller4d56ad92011-08-14 13:17:05 +0300843 int avail, freed_blocks;
Arik Nemtsovbf54e302011-08-14 13:17:32 +0300844 int i;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300845
Eliad Peller4d56ad92011-08-14 13:17:05 +0300846 wl1271_raw_read(wl, FW_STATUS_ADDR, status, sizeof(*status), false);
Shahar Levi13b107d2011-03-06 16:32:12 +0200847
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300848 wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
849 "drv_rx_counter = %d, tx_results_counter = %d)",
850 status->intr,
851 status->fw_rx_counter,
852 status->drv_rx_counter,
853 status->tx_results_counter);
854
Arik Nemtsovbf54e302011-08-14 13:17:32 +0300855 for (i = 0; i < NUM_TX_QUEUES; i++) {
856 /* prevent wrap-around in freed-packets counter */
Arik Nemtsov742246f2011-08-14 13:17:33 +0300857 wl->tx_allocated_pkts[i] -=
Arik Nemtsovbf54e302011-08-14 13:17:32 +0300858 (status->tx_released_pkts[i] -
859 wl->tx_pkts_freed[i]) & 0xff;
860
861 wl->tx_pkts_freed[i] = status->tx_released_pkts[i];
862 }
863
Arik Nemtsovbdf91cf2011-08-14 13:17:34 +0300864 /* prevent wrap-around in total blocks counter */
865 if (likely(wl->tx_blocks_freed <=
866 le32_to_cpu(status->total_released_blks)))
867 freed_blocks = le32_to_cpu(status->total_released_blks) -
868 wl->tx_blocks_freed;
869 else
870 freed_blocks = 0x100000000LL - wl->tx_blocks_freed +
871 le32_to_cpu(status->total_released_blks);
872
Eliad Peller4d56ad92011-08-14 13:17:05 +0300873 wl->tx_blocks_freed = le32_to_cpu(status->total_released_blks);
Shahar Levi13b107d2011-03-06 16:32:12 +0200874
Arik Nemtsov7bb5d6c2011-08-14 13:17:00 +0300875 wl->tx_allocated_blocks -= freed_blocks;
876
Eliad Peller4d56ad92011-08-14 13:17:05 +0300877 avail = le32_to_cpu(status->tx_total) - wl->tx_allocated_blocks;
Ido Yarivd2f4d472011-03-31 10:07:00 +0200878
Eliad Peller4d56ad92011-08-14 13:17:05 +0300879 /*
880 * The FW might change the total number of TX memblocks before
881 * we get a notification about blocks being released. Thus, the
882 * available blocks calculation might yield a temporary result
883 * which is lower than the actual available blocks. Keeping in
884 * mind that only blocks that were allocated can be moved from
885 * TX to RX, tx_blocks_available should never decrease here.
886 */
887 wl->tx_blocks_available = max((int)wl->tx_blocks_available,
888 avail);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300889
Ido Yariva5225502010-10-12 14:49:10 +0200890 /* if more blocks are available now, tx work can be scheduled */
Shahar Levi13b107d2011-03-06 16:32:12 +0200891 if (wl->tx_blocks_available > old_tx_blk_count)
Ido Yariva5225502010-10-12 14:49:10 +0200892 clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300893
Eliad Peller4d56ad92011-08-14 13:17:05 +0300894 /* for AP update num of allocated TX blocks per link and ps status */
Eliad Peller536129c2011-10-05 11:55:45 +0200895 if (wlvif->bss_type == BSS_TYPE_AP_BSS)
Eliad Pellerc7ffb902011-10-05 11:56:05 +0200896 wl12xx_irq_update_links_status(wl, wlvif, status);
Eliad Peller4d56ad92011-08-14 13:17:05 +0300897
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300898 /* update the host-chipset time offset */
Juuso Oikarinenac5e1e32010-02-22 08:38:38 +0200899 getnstimeofday(&ts);
900 wl->time_offset = (timespec_to_ns(&ts) >> 10) -
901 (s64)le32_to_cpu(status->fw_localtime);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300902}
903
Ido Yariva6208652011-03-01 15:14:41 +0200904static void wl1271_flush_deferred_work(struct wl1271 *wl)
905{
906 struct sk_buff *skb;
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200907
Ido Yariva6208652011-03-01 15:14:41 +0200908 /* Pass all received frames to the network stack */
909 while ((skb = skb_dequeue(&wl->deferred_rx_queue)))
910 ieee80211_rx_ni(wl->hw, skb);
911
912 /* Return sent skbs to the network stack */
913 while ((skb = skb_dequeue(&wl->deferred_tx_queue)))
Eliad Pellerc27d3ac2011-06-07 10:40:39 +0300914 ieee80211_tx_status_ni(wl->hw, skb);
Ido Yariva6208652011-03-01 15:14:41 +0200915}
916
917static void wl1271_netstack_work(struct work_struct *work)
918{
919 struct wl1271 *wl =
920 container_of(work, struct wl1271, netstack_work);
921
922 do {
923 wl1271_flush_deferred_work(wl);
924 } while (skb_queue_len(&wl->deferred_rx_queue));
925}
926
927#define WL1271_IRQ_MAX_LOOPS 256
928
929irqreturn_t wl1271_irq(int irq, void *cookie)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300930{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300931 int ret;
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300932 u32 intr;
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200933 int loopcount = WL1271_IRQ_MAX_LOOPS;
Ido Yariva6208652011-03-01 15:14:41 +0200934 struct wl1271 *wl = (struct wl1271 *)cookie;
935 bool done = false;
936 unsigned int defer_count;
Ido Yarivb07d4032011-03-01 15:14:43 +0200937 unsigned long flags;
938
939 /* TX might be handled here, avoid redundant work */
940 set_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
941 cancel_work_sync(&wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300942
Ido Yariv341b7cd2011-03-31 10:07:01 +0200943 /*
944 * In case edge triggered interrupt must be used, we cannot iterate
945 * more than once without introducing race conditions with the hardirq.
946 */
947 if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
948 loopcount = 1;
949
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300950 mutex_lock(&wl->mutex);
951
952 wl1271_debug(DEBUG_IRQ, "IRQ work");
953
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200954 if (unlikely(wl->state == WL1271_STATE_OFF))
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300955 goto out;
956
Ido Yariva6208652011-03-01 15:14:41 +0200957 ret = wl1271_ps_elp_wakeup(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300958 if (ret < 0)
959 goto out;
960
Ido Yariva6208652011-03-01 15:14:41 +0200961 while (!done && loopcount--) {
962 /*
963 * In order to avoid a race with the hardirq, clear the flag
964 * before acknowledging the chip. Since the mutex is held,
965 * wl1271_ps_elp_wakeup cannot be called concurrently.
966 */
967 clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
968 smp_mb__after_clear_bit();
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200969
Eliad Peller4d56ad92011-08-14 13:17:05 +0300970 wl12xx_fw_status(wl, wl->fw_status);
971 intr = le32_to_cpu(wl->fw_status->intr);
Ido Yariva6208652011-03-01 15:14:41 +0200972 intr &= WL1271_INTR_MASK;
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200973 if (!intr) {
Ido Yariva6208652011-03-01 15:14:41 +0200974 done = true;
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200975 continue;
976 }
977
Eliad Pellerccc83b02010-10-27 14:09:57 +0200978 if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) {
979 wl1271_error("watchdog interrupt received! "
980 "starting recovery.");
Ido Yarivbaacb9a2011-06-06 14:57:05 +0300981 wl12xx_queue_recovery_work(wl);
Eliad Pellerccc83b02010-10-27 14:09:57 +0200982
983 /* restarting the chip. ignore any other interrupt. */
984 goto out;
985 }
986
Ido Yariva6208652011-03-01 15:14:41 +0200987 if (likely(intr & WL1271_ACX_INTR_DATA)) {
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200988 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
989
Eliad Peller4d56ad92011-08-14 13:17:05 +0300990 wl12xx_rx(wl, wl->fw_status);
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200991
Ido Yariva5225502010-10-12 14:49:10 +0200992 /* Check if any tx blocks were freed */
Ido Yarivb07d4032011-03-01 15:14:43 +0200993 spin_lock_irqsave(&wl->wl_lock, flags);
Ido Yariva5225502010-10-12 14:49:10 +0200994 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
Arik Nemtsovf1a46382011-07-07 14:25:23 +0300995 wl1271_tx_total_queue_count(wl) > 0) {
Ido Yarivb07d4032011-03-01 15:14:43 +0200996 spin_unlock_irqrestore(&wl->wl_lock, flags);
Ido Yariva5225502010-10-12 14:49:10 +0200997 /*
998 * In order to avoid starvation of the TX path,
999 * call the work function directly.
1000 */
Eliad Pellera32d0cd2011-10-10 10:12:55 +02001001 wl1271_tx_work_locked(wl);
Ido Yarivb07d4032011-03-01 15:14:43 +02001002 } else {
1003 spin_unlock_irqrestore(&wl->wl_lock, flags);
Ido Yariva5225502010-10-12 14:49:10 +02001004 }
1005
Ido Yariv8aad2462011-03-01 15:14:38 +02001006 /* check for tx results */
Eliad Peller4d56ad92011-08-14 13:17:05 +03001007 if (wl->fw_status->tx_results_counter !=
Ido Yariv8aad2462011-03-01 15:14:38 +02001008 (wl->tx_results_count & 0xff))
1009 wl1271_tx_complete(wl);
Ido Yariva6208652011-03-01 15:14:41 +02001010
1011 /* Make sure the deferred queues don't get too long */
1012 defer_count = skb_queue_len(&wl->deferred_tx_queue) +
1013 skb_queue_len(&wl->deferred_rx_queue);
1014 if (defer_count > WL1271_DEFERRED_QUEUE_LIMIT)
1015 wl1271_flush_deferred_work(wl);
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +02001016 }
1017
1018 if (intr & WL1271_ACX_INTR_EVENT_A) {
1019 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
1020 wl1271_event_handle(wl, 0);
1021 }
1022
1023 if (intr & WL1271_ACX_INTR_EVENT_B) {
1024 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
1025 wl1271_event_handle(wl, 1);
1026 }
1027
1028 if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
1029 wl1271_debug(DEBUG_IRQ,
1030 "WL1271_ACX_INTR_INIT_COMPLETE");
1031
1032 if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
1033 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001034 }
1035
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001036 wl1271_ps_elp_sleep(wl);
1037
1038out:
Ido Yarivb07d4032011-03-01 15:14:43 +02001039 spin_lock_irqsave(&wl->wl_lock, flags);
1040 /* In case TX was not handled here, queue TX work */
1041 clear_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
1042 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
Arik Nemtsovf1a46382011-07-07 14:25:23 +03001043 wl1271_tx_total_queue_count(wl) > 0)
Ido Yarivb07d4032011-03-01 15:14:43 +02001044 ieee80211_queue_work(wl->hw, &wl->tx_work);
1045 spin_unlock_irqrestore(&wl->wl_lock, flags);
1046
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001047 mutex_unlock(&wl->mutex);
Ido Yariva6208652011-03-01 15:14:41 +02001048
1049 return IRQ_HANDLED;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001050}
Ido Yariva6208652011-03-01 15:14:41 +02001051EXPORT_SYMBOL_GPL(wl1271_irq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001052
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001053static int wl1271_fetch_firmware(struct wl1271 *wl)
1054{
1055 const struct firmware *fw;
Arik Nemtsov166d5042010-10-16 21:44:57 +02001056 const char *fw_name;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001057 int ret;
1058
Arik Nemtsovc302b2c2011-08-17 10:45:48 +03001059 if (wl->chip.id == CHIP_ID_1283_PG20)
1060 fw_name = WL128X_FW_NAME;
1061 else
1062 fw_name = WL127X_FW_NAME;
Arik Nemtsov166d5042010-10-16 21:44:57 +02001063
1064 wl1271_debug(DEBUG_BOOT, "booting firmware %s", fw_name);
1065
1066 ret = request_firmware(&fw, fw_name, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001067
1068 if (ret < 0) {
1069 wl1271_error("could not get firmware: %d", ret);
1070 return ret;
1071 }
1072
1073 if (fw->size % 4) {
1074 wl1271_error("firmware size is not multiple of 32 bits: %zu",
1075 fw->size);
1076 ret = -EILSEQ;
1077 goto out;
1078 }
1079
Arik Nemtsov166d5042010-10-16 21:44:57 +02001080 vfree(wl->fw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001081 wl->fw_len = fw->size;
Juuso Oikarinen1fba4972009-10-08 21:56:32 +03001082 wl->fw = vmalloc(wl->fw_len);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001083
1084 if (!wl->fw) {
1085 wl1271_error("could not allocate memory for the firmware");
1086 ret = -ENOMEM;
1087 goto out;
1088 }
1089
1090 memcpy(wl->fw, fw->data, wl->fw_len);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001091 ret = 0;
1092
1093out:
1094 release_firmware(fw);
1095
1096 return ret;
1097}
1098
1099static int wl1271_fetch_nvs(struct wl1271 *wl)
1100{
1101 const struct firmware *fw;
1102 int ret;
1103
Shahar Levi5aa42342011-03-06 16:32:07 +02001104 ret = request_firmware(&fw, WL12XX_NVS_NAME, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001105
1106 if (ret < 0) {
1107 wl1271_error("could not get nvs file: %d", ret);
1108 return ret;
1109 }
1110
Shahar Levibc765bf2011-03-06 16:32:10 +02001111 wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001112
1113 if (!wl->nvs) {
1114 wl1271_error("could not allocate memory for the nvs file");
1115 ret = -ENOMEM;
1116 goto out;
1117 }
1118
Juuso Oikarinen02fabb02010-08-19 04:41:15 +02001119 wl->nvs_len = fw->size;
1120
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001121out:
1122 release_firmware(fw);
1123
1124 return ret;
1125}
1126
Ido Yarivbaacb9a2011-06-06 14:57:05 +03001127void wl12xx_queue_recovery_work(struct wl1271 *wl)
1128{
1129 if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
1130 ieee80211_queue_work(wl->hw, &wl->recovery_work);
1131}
1132
Ido Yariv95dac04f2011-06-06 14:57:06 +03001133size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
1134{
1135 size_t len = 0;
1136
1137 /* The FW log is a length-value list, find where the log end */
1138 while (len < maxlen) {
1139 if (memblock[len] == 0)
1140 break;
1141 if (len + memblock[len] + 1 > maxlen)
1142 break;
1143 len += memblock[len] + 1;
1144 }
1145
1146 /* Make sure we have enough room */
1147 len = min(len, (size_t)(PAGE_SIZE - wl->fwlog_size));
1148
1149 /* Fill the FW log file, consumed by the sysfs fwlog entry */
1150 memcpy(wl->fwlog + wl->fwlog_size, memblock, len);
1151 wl->fwlog_size += len;
1152
1153 return len;
1154}
1155
1156static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
1157{
1158 u32 addr;
1159 u32 first_addr;
1160 u8 *block;
1161
1162 if ((wl->quirks & WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED) ||
1163 (wl->conf.fwlog.mode != WL12XX_FWLOG_ON_DEMAND) ||
1164 (wl->conf.fwlog.mem_blocks == 0))
1165 return;
1166
1167 wl1271_info("Reading FW panic log");
1168
1169 block = kmalloc(WL12XX_HW_BLOCK_SIZE, GFP_KERNEL);
1170 if (!block)
1171 return;
1172
1173 /*
1174 * Make sure the chip is awake and the logger isn't active.
1175 * This might fail if the firmware hanged.
1176 */
1177 if (!wl1271_ps_elp_wakeup(wl))
1178 wl12xx_cmd_stop_fwlog(wl);
1179
1180 /* Read the first memory block address */
Eliad Peller4d56ad92011-08-14 13:17:05 +03001181 wl12xx_fw_status(wl, wl->fw_status);
1182 first_addr = le32_to_cpu(wl->fw_status->log_start_addr);
Ido Yariv95dac04f2011-06-06 14:57:06 +03001183 if (!first_addr)
1184 goto out;
1185
1186 /* Traverse the memory blocks linked list */
1187 addr = first_addr;
1188 do {
1189 memset(block, 0, WL12XX_HW_BLOCK_SIZE);
1190 wl1271_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE,
1191 false);
1192
1193 /*
1194 * Memory blocks are linked to one another. The first 4 bytes
1195 * of each memory block hold the hardware address of the next
1196 * one. The last memory block points to the first one.
1197 */
Eliad Peller4d56ad92011-08-14 13:17:05 +03001198 addr = le32_to_cpup((__le32 *)block);
Ido Yariv95dac04f2011-06-06 14:57:06 +03001199 if (!wl12xx_copy_fwlog(wl, block + sizeof(addr),
1200 WL12XX_HW_BLOCK_SIZE - sizeof(addr)))
1201 break;
1202 } while (addr && (addr != first_addr));
1203
1204 wake_up_interruptible(&wl->fwlog_waitq);
1205
1206out:
1207 kfree(block);
1208}
1209
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001210static void wl1271_recovery_work(struct work_struct *work)
1211{
1212 struct wl1271 *wl =
1213 container_of(work, struct wl1271, recovery_work);
Eliad Peller48e93e42011-10-10 10:12:58 +02001214 struct wl12xx_vif *wlvif;
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001215
1216 mutex_lock(&wl->mutex);
1217
1218 if (wl->state != WL1271_STATE_ON)
1219 goto out;
1220
Ido Yarivbaacb9a2011-06-06 14:57:05 +03001221 /* Avoid a recursive recovery */
1222 set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
1223
Ido Yariv95dac04f2011-06-06 14:57:06 +03001224 wl12xx_read_fwlog_panic(wl);
1225
Arik Nemtsov52dcaf52011-04-18 14:15:24 +03001226 wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x",
1227 wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4));
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001228
Eliad Peller2a5bff02011-08-25 18:10:59 +03001229 BUG_ON(bug_on_recovery);
1230
Oz Krakowskib992c682011-06-26 10:36:02 +03001231 /*
1232 * Advance security sequence number to overcome potential progress
1233 * in the firmware during recovery. This doens't hurt if the network is
1234 * not encrypted.
1235 */
Eliad Peller48e93e42011-10-10 10:12:58 +02001236 wl12xx_for_each_wlvif(wl, wlvif) {
Eliad Pellerba8447f2011-10-10 10:13:00 +02001237 if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
Eliad Peller53d40d02011-10-10 10:13:02 +02001238 test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
Eliad Peller48e93e42011-10-10 10:12:58 +02001239 wlvif->tx_security_seq +=
1240 WL1271_TX_SQN_POST_RECOVERY_PADDING;
1241 }
Oz Krakowskib992c682011-06-26 10:36:02 +03001242
Arik Nemtsov7dece1c2011-04-18 14:15:28 +03001243 /* Prevent spurious TX during FW restart */
1244 ieee80211_stop_queues(wl->hw);
1245
Luciano Coelho33c2c062011-05-10 14:46:02 +03001246 if (wl->sched_scanning) {
1247 ieee80211_sched_scan_stopped(wl->hw);
1248 wl->sched_scanning = false;
1249 }
1250
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001251 /* reboot the chipset */
Eliad Peller536129c2011-10-05 11:55:45 +02001252 __wl1271_op_remove_interface(wl, wl->vif, false);
Ido Yarivbaacb9a2011-06-06 14:57:05 +03001253
1254 clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
1255
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001256 ieee80211_restart_hw(wl->hw);
1257
Arik Nemtsov7dece1c2011-04-18 14:15:28 +03001258 /*
1259 * Its safe to enable TX now - the queues are stopped after a request
1260 * to restart the HW.
1261 */
1262 ieee80211_wake_queues(wl->hw);
1263
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001264out:
1265 mutex_unlock(&wl->mutex);
1266}
1267
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001268static void wl1271_fw_wakeup(struct wl1271 *wl)
1269{
1270 u32 elp_reg;
1271
1272 elp_reg = ELPCTRL_WAKE_UP;
Juuso Oikarinen74621412009-10-12 15:08:54 +03001273 wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001274}
1275
1276static int wl1271_setup(struct wl1271 *wl)
1277{
1278 wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL);
1279 if (!wl->fw_status)
1280 return -ENOMEM;
1281
1282 wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
1283 if (!wl->tx_res_if) {
1284 kfree(wl->fw_status);
1285 return -ENOMEM;
1286 }
1287
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001288 return 0;
1289}
1290
1291static int wl1271_chip_wakeup(struct wl1271 *wl)
1292{
Juuso Oikarinen451de972009-10-12 15:08:46 +03001293 struct wl1271_partition_set partition;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001294 int ret = 0;
1295
Juuso Oikarinen01ac17e2009-12-11 15:41:02 +02001296 msleep(WL1271_PRE_POWER_ON_SLEEP);
Ohad Ben-Cohen2cc78ff2010-09-16 01:22:04 +02001297 ret = wl1271_power_on(wl);
1298 if (ret < 0)
1299 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001300 msleep(WL1271_POWER_ON_SLEEP);
Teemu Paasikivi9b280722010-02-18 13:25:56 +02001301 wl1271_io_reset(wl);
1302 wl1271_io_init(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001303
1304 /* We don't need a real memory partition here, because we only want
1305 * to use the registers at this point. */
Juuso Oikarinen451de972009-10-12 15:08:46 +03001306 memset(&partition, 0, sizeof(partition));
1307 partition.reg.start = REGISTERS_BASE;
1308 partition.reg.size = REGISTERS_DOWN_SIZE;
1309 wl1271_set_partition(wl, &partition);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001310
1311 /* ELP module wake up */
1312 wl1271_fw_wakeup(wl);
1313
1314 /* whal_FwCtrl_BootSm() */
1315
1316 /* 0. read chip id from CHIP_ID */
Teemu Paasikivi7b048c52010-02-18 13:25:55 +02001317 wl->chip.id = wl1271_read32(wl, CHIP_ID_B);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001318
1319 /* 1. check if chip id is valid */
1320
1321 switch (wl->chip.id) {
1322 case CHIP_ID_1271_PG10:
1323 wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
1324 wl->chip.id);
1325
1326 ret = wl1271_setup(wl);
1327 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001328 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001329 break;
1330 case CHIP_ID_1271_PG20:
1331 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
1332 wl->chip.id);
1333
1334 ret = wl1271_setup(wl);
1335 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001336 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001337 break;
Shahar Levi0830cee2011-03-06 16:32:20 +02001338 case CHIP_ID_1283_PG20:
1339 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",
1340 wl->chip.id);
1341
1342 ret = wl1271_setup(wl);
1343 if (ret < 0)
1344 goto out;
Shahar Levi0c005042011-06-12 10:34:43 +03001345
Ido Yariv0da13da2011-03-31 10:06:58 +02001346 if (wl1271_set_block_size(wl))
1347 wl->quirks |= WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT;
Shahar Levi0830cee2011-03-06 16:32:20 +02001348 break;
1349 case CHIP_ID_1283_PG10:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001350 default:
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001351 wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001352 ret = -ENODEV;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001353 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001354 }
1355
Arik Nemtsovc302b2c2011-08-17 10:45:48 +03001356 if (wl->fw == NULL) {
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001357 ret = wl1271_fetch_firmware(wl);
1358 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001359 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001360 }
1361
1362 /* No NVS from netlink, try to get it from the filesystem */
1363 if (wl->nvs == NULL) {
1364 ret = wl1271_fetch_nvs(wl);
1365 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001366 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001367 }
1368
1369out:
1370 return ret;
1371}
1372
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001373int wl1271_plt_start(struct wl1271 *wl)
1374{
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001375 int retries = WL1271_BOOT_RETRIES;
Gery Kahn6f07b722011-07-18 14:21:49 +03001376 struct wiphy *wiphy = wl->hw->wiphy;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001377 int ret;
1378
1379 mutex_lock(&wl->mutex);
1380
1381 wl1271_notice("power up");
1382
1383 if (wl->state != WL1271_STATE_OFF) {
1384 wl1271_error("cannot go into PLT state because not "
1385 "in off state: %d", wl->state);
1386 ret = -EBUSY;
1387 goto out;
1388 }
1389
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001390 while (retries) {
1391 retries--;
1392 ret = wl1271_chip_wakeup(wl);
1393 if (ret < 0)
1394 goto power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001395
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001396 ret = wl1271_boot(wl);
1397 if (ret < 0)
1398 goto power_off;
1399
1400 ret = wl1271_plt_init(wl);
1401 if (ret < 0)
1402 goto irq_disable;
1403
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001404 wl->state = WL1271_STATE_PLT;
1405 wl1271_notice("firmware booted in PLT mode (%s)",
Levi, Shahar4b7fac72011-01-23 07:27:22 +01001406 wl->chip.fw_ver_str);
Luciano Coelhoe7ddf542011-03-10 15:24:57 +02001407
Gery Kahn6f07b722011-07-18 14:21:49 +03001408 /* update hw/fw version info in wiphy struct */
1409 wiphy->hw_version = wl->chip.id;
1410 strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
1411 sizeof(wiphy->fw_version));
1412
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001413 goto out;
1414
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001415irq_disable:
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001416 mutex_unlock(&wl->mutex);
1417 /* Unlocking the mutex in the middle of handling is
1418 inherently unsafe. In this case we deem it safe to do,
1419 because we need to let any possibly pending IRQ out of
1420 the system (and while we are WL1271_STATE_OFF the IRQ
1421 work function will not do anything.) Also, any other
1422 possible concurrent operations will fail due to the
1423 current state, hence the wl1271 struct should be safe. */
Ido Yariva6208652011-03-01 15:14:41 +02001424 wl1271_disable_interrupts(wl);
1425 wl1271_flush_deferred_work(wl);
1426 cancel_work_sync(&wl->netstack_work);
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001427 mutex_lock(&wl->mutex);
1428power_off:
1429 wl1271_power_off(wl);
1430 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001431
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001432 wl1271_error("firmware boot in PLT mode failed despite %d retries",
1433 WL1271_BOOT_RETRIES);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001434out:
1435 mutex_unlock(&wl->mutex);
1436
1437 return ret;
1438}
1439
Luciano Coelho4623ec72011-03-21 19:26:41 +02001440static int __wl1271_plt_stop(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001441{
1442 int ret = 0;
1443
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001444 wl1271_notice("power down");
1445
1446 if (wl->state != WL1271_STATE_PLT) {
1447 wl1271_error("cannot power down because not in PLT "
1448 "state: %d", wl->state);
1449 ret = -EBUSY;
1450 goto out;
1451 }
1452
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001453 wl1271_power_off(wl);
1454
1455 wl->state = WL1271_STATE_OFF;
Luciano Coelhobd5ea182009-10-13 12:47:58 +03001456 wl->rx_counter = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001457
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001458 mutex_unlock(&wl->mutex);
Ido Yariva6208652011-03-01 15:14:41 +02001459 wl1271_disable_interrupts(wl);
1460 wl1271_flush_deferred_work(wl);
1461 cancel_work_sync(&wl->netstack_work);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001462 cancel_work_sync(&wl->recovery_work);
Juuso Oikarinen4ae3fa82011-01-14 12:48:46 +01001463 mutex_lock(&wl->mutex);
1464out:
1465 return ret;
1466}
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +02001467
Juuso Oikarinen4ae3fa82011-01-14 12:48:46 +01001468int wl1271_plt_stop(struct wl1271 *wl)
1469{
1470 int ret;
1471
1472 mutex_lock(&wl->mutex);
1473 ret = __wl1271_plt_stop(wl);
1474 mutex_unlock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001475 return ret;
1476}
1477
Johannes Berg7bb45682011-02-24 14:42:06 +01001478static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001479{
1480 struct wl1271 *wl = hw->priv;
Eliad Pellera8ab39a2011-10-05 11:55:54 +02001481 struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
1482 struct ieee80211_vif *vif = info->control.vif;
1483 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001484 unsigned long flags;
Arik Nemtsov708bb3c2011-06-24 13:03:37 +03001485 int q, mapping;
Eliad Pellerd6a3cc22011-10-10 10:12:51 +02001486 u8 hlid;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001487
Arik Nemtsov708bb3c2011-06-24 13:03:37 +03001488 mapping = skb_get_queue_mapping(skb);
1489 q = wl1271_tx_get_queue(mapping);
Ido Yarivb07d4032011-03-01 15:14:43 +02001490
Eliad Pellerd6a3cc22011-10-10 10:12:51 +02001491 hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
Ido Yarivb07d4032011-03-01 15:14:43 +02001492
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001493 spin_lock_irqsave(&wl->wl_lock, flags);
Ido Yarivb07d4032011-03-01 15:14:43 +02001494
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001495 /* queue the packet */
Eliad Pellerd6a3cc22011-10-10 10:12:51 +02001496 if (hlid == WL12XX_INVALID_LINK_ID ||
1497 !test_bit(hlid, wlvif->links_map)) {
1498 wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q);
1499 dev_kfree_skb(skb);
1500 goto out;
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02001501 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001502
Eliad Pellerd6a3cc22011-10-10 10:12:51 +02001503 wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q);
1504 skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
1505
Arik Nemtsov04b4d692011-08-14 13:17:39 +03001506 wl->tx_queue_count[q]++;
1507
1508 /*
1509 * The workqueue is slow to process the tx_queue and we need stop
1510 * the queue here, otherwise the queue will get too long.
1511 */
1512 if (wl->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
1513 wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q);
1514 ieee80211_stop_queue(wl->hw, mapping);
1515 set_bit(q, &wl->stopped_queues_map);
1516 }
1517
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001518 /*
1519 * The chip specific setup must run before the first TX packet -
1520 * before that, the tx_work will not be initialized!
1521 */
1522
Ido Yarivb07d4032011-03-01 15:14:43 +02001523 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
1524 !test_bit(WL1271_FLAG_TX_PENDING, &wl->flags))
Ido Yariva5225502010-10-12 14:49:10 +02001525 ieee80211_queue_work(wl->hw, &wl->tx_work);
Ido Yarivb07d4032011-03-01 15:14:43 +02001526
Arik Nemtsov04216da2011-08-14 13:17:38 +03001527out:
Ido Yarivb07d4032011-03-01 15:14:43 +02001528 spin_unlock_irqrestore(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001529}
1530
Shahar Leviae47c452011-03-06 16:32:14 +02001531int wl1271_tx_dummy_packet(struct wl1271 *wl)
1532{
Ido Yariv990f5de2011-03-31 10:06:59 +02001533 unsigned long flags;
Arik Nemtsov14623782011-08-28 15:11:57 +03001534 int q;
1535
1536 /* no need to queue a new dummy packet if one is already pending */
1537 if (test_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags))
1538 return 0;
1539
1540 q = wl1271_tx_get_queue(skb_get_queue_mapping(wl->dummy_packet));
Shahar Leviae47c452011-03-06 16:32:14 +02001541
Ido Yariv990f5de2011-03-31 10:06:59 +02001542 spin_lock_irqsave(&wl->wl_lock, flags);
1543 set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
Arik Nemtsovf1a46382011-07-07 14:25:23 +03001544 wl->tx_queue_count[q]++;
Ido Yariv990f5de2011-03-31 10:06:59 +02001545 spin_unlock_irqrestore(&wl->wl_lock, flags);
1546
1547 /* The FW is low on RX memory blocks, so send the dummy packet asap */
1548 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
Eliad Pellera32d0cd2011-10-10 10:12:55 +02001549 wl1271_tx_work_locked(wl);
Ido Yariv990f5de2011-03-31 10:06:59 +02001550
1551 /*
1552 * If the FW TX is busy, TX work will be scheduled by the threaded
1553 * interrupt handler function
1554 */
1555 return 0;
1556}
1557
1558/*
1559 * The size of the dummy packet should be at least 1400 bytes. However, in
1560 * order to minimize the number of bus transactions, aligning it to 512 bytes
1561 * boundaries could be beneficial, performance wise
1562 */
1563#define TOTAL_TX_DUMMY_PACKET_SIZE (ALIGN(1400, 512))
1564
Luciano Coelhocf27d862011-04-01 21:08:23 +03001565static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
Ido Yariv990f5de2011-03-31 10:06:59 +02001566{
1567 struct sk_buff *skb;
1568 struct ieee80211_hdr_3addr *hdr;
1569 unsigned int dummy_packet_size;
1570
1571 dummy_packet_size = TOTAL_TX_DUMMY_PACKET_SIZE -
1572 sizeof(struct wl1271_tx_hw_descr) - sizeof(*hdr);
1573
1574 skb = dev_alloc_skb(TOTAL_TX_DUMMY_PACKET_SIZE);
Shahar Leviae47c452011-03-06 16:32:14 +02001575 if (!skb) {
Ido Yariv990f5de2011-03-31 10:06:59 +02001576 wl1271_warning("Failed to allocate a dummy packet skb");
1577 return NULL;
Shahar Leviae47c452011-03-06 16:32:14 +02001578 }
1579
1580 skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr));
1581
1582 hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr));
1583 memset(hdr, 0, sizeof(*hdr));
1584 hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
Ido Yariv990f5de2011-03-31 10:06:59 +02001585 IEEE80211_STYPE_NULLFUNC |
1586 IEEE80211_FCTL_TODS);
Shahar Leviae47c452011-03-06 16:32:14 +02001587
Ido Yariv990f5de2011-03-31 10:06:59 +02001588 memset(skb_put(skb, dummy_packet_size), 0, dummy_packet_size);
Shahar Leviae47c452011-03-06 16:32:14 +02001589
Luciano Coelho18b92ff2011-03-21 16:35:21 +02001590 /* Dummy packets require the TID to be management */
1591 skb->priority = WL1271_TID_MGMT;
Ido Yariv990f5de2011-03-31 10:06:59 +02001592
1593 /* Initialize all fields that might be used */
Hauke Mehrtens86c438f2011-04-26 23:27:44 +02001594 skb_set_queue_mapping(skb, 0);
Ido Yariv990f5de2011-03-31 10:06:59 +02001595 memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info));
Shahar Leviae47c452011-03-06 16:32:14 +02001596
Ido Yariv990f5de2011-03-31 10:06:59 +02001597 return skb;
Shahar Leviae47c452011-03-06 16:32:14 +02001598}
1599
Ido Yariv990f5de2011-03-31 10:06:59 +02001600
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03001601static struct notifier_block wl1271_dev_notifier = {
1602 .notifier_call = wl1271_dev_notify,
1603};
1604
Luciano Coelhof634a4e2011-05-18 16:51:26 -04001605#ifdef CONFIG_PM
Eliad Pellerd2d66c52011-10-05 11:55:43 +02001606static int wl1271_configure_suspend_sta(struct wl1271 *wl,
1607 struct wl12xx_vif *wlvif)
Eliad Peller94390642011-05-13 11:57:13 +03001608{
Eliad Pellere85d1622011-06-27 13:06:43 +03001609 int ret = 0;
Eliad Peller94390642011-05-13 11:57:13 +03001610
Eliad Peller94390642011-05-13 11:57:13 +03001611 mutex_lock(&wl->mutex);
1612
Eliad Pellerba8447f2011-10-10 10:13:00 +02001613 if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
Eliad Pellere85d1622011-06-27 13:06:43 +03001614 goto out_unlock;
1615
Eliad Peller94390642011-05-13 11:57:13 +03001616 ret = wl1271_ps_elp_wakeup(wl);
1617 if (ret < 0)
1618 goto out_unlock;
1619
1620 /* enter psm if needed*/
Eliad Pellerc29bb002011-10-10 10:13:03 +02001621 if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) {
Eliad Peller94390642011-05-13 11:57:13 +03001622 DECLARE_COMPLETION_ONSTACK(compl);
1623
Eliad Peller6ec45dc2011-10-05 11:56:01 +02001624 wlvif->ps_compl = &compl;
Eliad Peller0603d892011-10-05 11:55:51 +02001625 ret = wl1271_ps_set_mode(wl, wlvif, STATION_POWER_SAVE_MODE,
Eliad Pellerd2d66c52011-10-05 11:55:43 +02001626 wlvif->basic_rate, true);
Eliad Peller94390642011-05-13 11:57:13 +03001627 if (ret < 0)
1628 goto out_sleep;
1629
1630 /* we must unlock here so we will be able to get events */
1631 wl1271_ps_elp_sleep(wl);
1632 mutex_unlock(&wl->mutex);
1633
1634 ret = wait_for_completion_timeout(
1635 &compl, msecs_to_jiffies(WL1271_PS_COMPLETE_TIMEOUT));
1636 if (ret <= 0) {
1637 wl1271_warning("couldn't enter ps mode!");
1638 ret = -EBUSY;
1639 goto out;
1640 }
1641
1642 /* take mutex again, and wakeup */
1643 mutex_lock(&wl->mutex);
1644
1645 ret = wl1271_ps_elp_wakeup(wl);
1646 if (ret < 0)
1647 goto out_unlock;
1648 }
1649out_sleep:
1650 wl1271_ps_elp_sleep(wl);
1651out_unlock:
1652 mutex_unlock(&wl->mutex);
1653out:
1654 return ret;
1655
1656}
1657
Eliad Peller0603d892011-10-05 11:55:51 +02001658static int wl1271_configure_suspend_ap(struct wl1271 *wl,
1659 struct wl12xx_vif *wlvif)
Eliad Peller94390642011-05-13 11:57:13 +03001660{
Eliad Pellere85d1622011-06-27 13:06:43 +03001661 int ret = 0;
Eliad Peller94390642011-05-13 11:57:13 +03001662
Eliad Peller8a7cf3f2011-06-06 12:21:54 +03001663 mutex_lock(&wl->mutex);
1664
Eliad Peller53d40d02011-10-10 10:13:02 +02001665 if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
Eliad Pellere85d1622011-06-27 13:06:43 +03001666 goto out_unlock;
1667
Eliad Peller8a7cf3f2011-06-06 12:21:54 +03001668 ret = wl1271_ps_elp_wakeup(wl);
1669 if (ret < 0)
1670 goto out_unlock;
1671
Eliad Peller0603d892011-10-05 11:55:51 +02001672 ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
Eliad Peller8a7cf3f2011-06-06 12:21:54 +03001673
1674 wl1271_ps_elp_sleep(wl);
1675out_unlock:
1676 mutex_unlock(&wl->mutex);
1677 return ret;
1678
1679}
1680
Eliad Pellerd2d66c52011-10-05 11:55:43 +02001681static int wl1271_configure_suspend(struct wl1271 *wl,
1682 struct wl12xx_vif *wlvif)
Eliad Peller8a7cf3f2011-06-06 12:21:54 +03001683{
Eliad Peller536129c2011-10-05 11:55:45 +02001684 if (wlvif->bss_type == BSS_TYPE_STA_BSS)
Eliad Pellerd2d66c52011-10-05 11:55:43 +02001685 return wl1271_configure_suspend_sta(wl, wlvif);
Eliad Peller536129c2011-10-05 11:55:45 +02001686 if (wlvif->bss_type == BSS_TYPE_AP_BSS)
Eliad Peller0603d892011-10-05 11:55:51 +02001687 return wl1271_configure_suspend_ap(wl, wlvif);
Eliad Peller8a7cf3f2011-06-06 12:21:54 +03001688 return 0;
1689}
1690
Eliad Pellerd2d66c52011-10-05 11:55:43 +02001691static void wl1271_configure_resume(struct wl1271 *wl,
1692 struct wl12xx_vif *wlvif)
Eliad Peller8a7cf3f2011-06-06 12:21:54 +03001693{
1694 int ret;
Eliad Peller536129c2011-10-05 11:55:45 +02001695 bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
1696 bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
Eliad Peller8a7cf3f2011-06-06 12:21:54 +03001697
1698 if (!is_sta && !is_ap)
Eliad Peller94390642011-05-13 11:57:13 +03001699 return;
1700
1701 mutex_lock(&wl->mutex);
1702 ret = wl1271_ps_elp_wakeup(wl);
1703 if (ret < 0)
1704 goto out;
1705
Eliad Peller8a7cf3f2011-06-06 12:21:54 +03001706 if (is_sta) {
1707 /* exit psm if it wasn't configured */
Eliad Pellerc29bb002011-10-10 10:13:03 +02001708 if (!test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags))
Eliad Peller0603d892011-10-05 11:55:51 +02001709 wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE,
Eliad Pellerd2d66c52011-10-05 11:55:43 +02001710 wlvif->basic_rate, true);
Eliad Peller8a7cf3f2011-06-06 12:21:54 +03001711 } else if (is_ap) {
Eliad Peller0603d892011-10-05 11:55:51 +02001712 wl1271_acx_beacon_filter_opt(wl, wlvif, false);
Eliad Peller8a7cf3f2011-06-06 12:21:54 +03001713 }
Eliad Peller94390642011-05-13 11:57:13 +03001714
1715 wl1271_ps_elp_sleep(wl);
1716out:
1717 mutex_unlock(&wl->mutex);
1718}
1719
Eliad Peller402e48612011-05-13 11:57:09 +03001720static int wl1271_op_suspend(struct ieee80211_hw *hw,
1721 struct cfg80211_wowlan *wow)
1722{
1723 struct wl1271 *wl = hw->priv;
Eliad Pellerd2d66c52011-10-05 11:55:43 +02001724 struct ieee80211_vif *vif = wl->vif; /* TODO: get as param */
1725 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Eliad Peller4a859df2011-06-06 12:21:52 +03001726 int ret;
1727
Eliad Peller402e48612011-05-13 11:57:09 +03001728 wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
Eliad Peller4a859df2011-06-06 12:21:52 +03001729 WARN_ON(!wow || !wow->any);
Eliad Pellerf44e5862011-05-13 11:57:11 +03001730
Eliad Peller4a859df2011-06-06 12:21:52 +03001731 wl->wow_enabled = true;
Eliad Pellerd2d66c52011-10-05 11:55:43 +02001732 ret = wl1271_configure_suspend(wl, wlvif);
Eliad Peller4a859df2011-06-06 12:21:52 +03001733 if (ret < 0) {
1734 wl1271_warning("couldn't prepare device to suspend");
1735 return ret;
Eliad Pellerf44e5862011-05-13 11:57:11 +03001736 }
Eliad Peller4a859df2011-06-06 12:21:52 +03001737 /* flush any remaining work */
1738 wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
Eliad Peller4a859df2011-06-06 12:21:52 +03001739
1740 /*
1741 * disable and re-enable interrupts in order to flush
1742 * the threaded_irq
1743 */
1744 wl1271_disable_interrupts(wl);
1745
1746 /*
1747 * set suspended flag to avoid triggering a new threaded_irq
1748 * work. no need for spinlock as interrupts are disabled.
1749 */
1750 set_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
1751
1752 wl1271_enable_interrupts(wl);
1753 flush_work(&wl->tx_work);
Eliad Peller252efa42011-10-05 11:56:00 +02001754 flush_delayed_work(&wlvif->pspoll_work);
Eliad Peller4a859df2011-06-06 12:21:52 +03001755 flush_delayed_work(&wl->elp_work);
1756
Eliad Peller402e48612011-05-13 11:57:09 +03001757 return 0;
1758}
1759
1760static int wl1271_op_resume(struct ieee80211_hw *hw)
1761{
1762 struct wl1271 *wl = hw->priv;
Eliad Pellerd2d66c52011-10-05 11:55:43 +02001763 struct ieee80211_vif *vif = wl->vif; /* TODO: get as param */
1764 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Eliad Peller4a859df2011-06-06 12:21:52 +03001765 unsigned long flags;
1766 bool run_irq_work = false;
1767
Eliad Peller402e48612011-05-13 11:57:09 +03001768 wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d",
1769 wl->wow_enabled);
Eliad Peller4a859df2011-06-06 12:21:52 +03001770 WARN_ON(!wl->wow_enabled);
Eliad Pellerf44e5862011-05-13 11:57:11 +03001771
1772 /*
1773 * re-enable irq_work enqueuing, and call irq_work directly if
1774 * there is a pending work.
1775 */
Eliad Peller4a859df2011-06-06 12:21:52 +03001776 spin_lock_irqsave(&wl->wl_lock, flags);
1777 clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
1778 if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags))
1779 run_irq_work = true;
1780 spin_unlock_irqrestore(&wl->wl_lock, flags);
Eliad Pellerf44e5862011-05-13 11:57:11 +03001781
Eliad Peller4a859df2011-06-06 12:21:52 +03001782 if (run_irq_work) {
1783 wl1271_debug(DEBUG_MAC80211,
1784 "run postponed irq_work directly");
1785 wl1271_irq(0, wl);
1786 wl1271_enable_interrupts(wl);
Eliad Pellerf44e5862011-05-13 11:57:11 +03001787 }
Eliad Pellerd2d66c52011-10-05 11:55:43 +02001788 wl1271_configure_resume(wl, wlvif);
Eliad Pellerff91afc2011-06-06 12:21:53 +03001789 wl->wow_enabled = false;
Eliad Pellerf44e5862011-05-13 11:57:11 +03001790
Eliad Peller402e48612011-05-13 11:57:09 +03001791 return 0;
1792}
Luciano Coelhof634a4e2011-05-18 16:51:26 -04001793#endif
Eliad Peller402e48612011-05-13 11:57:09 +03001794
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001795static int wl1271_op_start(struct ieee80211_hw *hw)
1796{
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001797 wl1271_debug(DEBUG_MAC80211, "mac80211 start");
1798
1799 /*
1800 * We have to delay the booting of the hardware because
1801 * we need to know the local MAC address before downloading and
1802 * initializing the firmware. The MAC address cannot be changed
1803 * after boot, and without the proper MAC address, the firmware
1804 * will not function properly.
1805 *
1806 * The MAC address is first known when the corresponding interface
1807 * is added. That is where we will initialize the hardware.
1808 */
1809
1810 return 0;
1811}
1812
1813static void wl1271_op_stop(struct ieee80211_hw *hw)
1814{
Eliad Pellerbaf62772011-10-10 10:12:52 +02001815 struct wl1271 *wl = hw->priv;
1816 int i;
1817
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001818 wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
Eliad Pellerbaf62772011-10-10 10:12:52 +02001819
Eliad Peller10c8cd02011-10-10 10:13:06 +02001820 mutex_lock(&wl->mutex);
1821 if (wl->state == WL1271_STATE_OFF) {
1822 mutex_unlock(&wl->mutex);
1823 return;
1824 }
Eliad Pellerbaf62772011-10-10 10:12:52 +02001825 /*
1826 * this must be before the cancel_work calls below, so that the work
1827 * functions don't perform further work.
1828 */
1829 wl->state = WL1271_STATE_OFF;
Eliad Peller10c8cd02011-10-10 10:13:06 +02001830 mutex_unlock(&wl->mutex);
1831
1832 mutex_lock(&wl_list_mutex);
1833 list_del(&wl->list);
Eliad Pellerbaf62772011-10-10 10:12:52 +02001834 mutex_unlock(&wl_list_mutex);
1835
1836 wl1271_disable_interrupts(wl);
1837 wl1271_flush_deferred_work(wl);
1838 cancel_delayed_work_sync(&wl->scan_complete_work);
1839 cancel_work_sync(&wl->netstack_work);
1840 cancel_work_sync(&wl->tx_work);
Eliad Pellerbaf62772011-10-10 10:12:52 +02001841 cancel_delayed_work_sync(&wl->elp_work);
1842
1843 /* let's notify MAC80211 about the remaining pending TX frames */
1844 wl12xx_tx_reset(wl, true);
1845 mutex_lock(&wl->mutex);
1846
1847 wl1271_power_off(wl);
1848
1849 wl->band = IEEE80211_BAND_2GHZ;
1850
1851 wl->rx_counter = 0;
1852 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
1853 wl->tx_blocks_available = 0;
1854 wl->tx_allocated_blocks = 0;
1855 wl->tx_results_count = 0;
1856 wl->tx_packets_count = 0;
1857 wl->time_offset = 0;
1858 wl->vif = NULL;
1859 wl->tx_spare_blocks = TX_HW_BLOCK_SPARE_DEFAULT;
1860 wl->ap_fw_ps_map = 0;
1861 wl->ap_ps_map = 0;
1862 wl->sched_scanning = false;
1863 memset(wl->roles_map, 0, sizeof(wl->roles_map));
1864 memset(wl->links_map, 0, sizeof(wl->links_map));
1865 memset(wl->roc_map, 0, sizeof(wl->roc_map));
1866 wl->active_sta_count = 0;
1867
1868 /* The system link is always allocated */
1869 __set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
1870
1871 /*
1872 * this is performed after the cancel_work calls and the associated
1873 * mutex_lock, so that wl1271_op_add_interface does not accidentally
1874 * get executed before all these vars have been reset.
1875 */
1876 wl->flags = 0;
1877
1878 wl->tx_blocks_freed = 0;
1879
1880 for (i = 0; i < NUM_TX_QUEUES; i++) {
1881 wl->tx_pkts_freed[i] = 0;
1882 wl->tx_allocated_pkts[i] = 0;
1883 }
1884
1885 wl1271_debugfs_reset(wl);
1886
1887 kfree(wl->fw_status);
1888 wl->fw_status = NULL;
1889 kfree(wl->tx_res_if);
1890 wl->tx_res_if = NULL;
1891 kfree(wl->target_mem_map);
1892 wl->target_mem_map = NULL;
1893
1894 mutex_unlock(&wl->mutex);
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001895}
1896
Eliad Peller536129c2011-10-05 11:55:45 +02001897static u8 wl12xx_get_role_type(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Eliad Pellerb78b47e2011-08-14 13:17:08 +03001898{
Eliad Peller536129c2011-10-05 11:55:45 +02001899 switch (wlvif->bss_type) {
Eliad Pellerb78b47e2011-08-14 13:17:08 +03001900 case BSS_TYPE_AP_BSS:
Eliad Pellerfb0e7072011-10-05 11:55:47 +02001901 if (wlvif->p2p)
Eliad Peller045c7452011-08-28 15:23:01 +03001902 return WL1271_ROLE_P2P_GO;
1903 else
1904 return WL1271_ROLE_AP;
Eliad Pellerb78b47e2011-08-14 13:17:08 +03001905
1906 case BSS_TYPE_STA_BSS:
Eliad Pellerfb0e7072011-10-05 11:55:47 +02001907 if (wlvif->p2p)
Eliad Peller045c7452011-08-28 15:23:01 +03001908 return WL1271_ROLE_P2P_CL;
1909 else
1910 return WL1271_ROLE_STA;
Eliad Pellerb78b47e2011-08-14 13:17:08 +03001911
Eliad Peller227e81e2011-08-14 13:17:26 +03001912 case BSS_TYPE_IBSS:
1913 return WL1271_ROLE_IBSS;
1914
Eliad Pellerb78b47e2011-08-14 13:17:08 +03001915 default:
Eliad Peller536129c2011-10-05 11:55:45 +02001916 wl1271_error("invalid bss_type: %d", wlvif->bss_type);
Eliad Pellerb78b47e2011-08-14 13:17:08 +03001917 }
1918 return WL12XX_INVALID_ROLE_TYPE;
1919}
1920
Eliad Peller83587502011-10-10 10:12:53 +02001921static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
Eliad Peller87fbcb02011-10-05 11:55:41 +02001922{
Eliad Pellere936bbe2011-10-05 11:55:56 +02001923 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
1924
Eliad Peller48e93e42011-10-10 10:12:58 +02001925 /* clear everything but the persistent data */
1926 memset(wlvif, 0, offsetof(struct wl12xx_vif, persistent));
Eliad Pellere936bbe2011-10-05 11:55:56 +02001927
1928 switch (ieee80211_vif_type_p2p(vif)) {
1929 case NL80211_IFTYPE_P2P_CLIENT:
1930 wlvif->p2p = 1;
1931 /* fall-through */
1932 case NL80211_IFTYPE_STATION:
1933 wlvif->bss_type = BSS_TYPE_STA_BSS;
1934 break;
1935 case NL80211_IFTYPE_ADHOC:
1936 wlvif->bss_type = BSS_TYPE_IBSS;
1937 break;
1938 case NL80211_IFTYPE_P2P_GO:
1939 wlvif->p2p = 1;
1940 /* fall-through */
1941 case NL80211_IFTYPE_AP:
1942 wlvif->bss_type = BSS_TYPE_AP_BSS;
1943 break;
1944 default:
1945 wlvif->bss_type = MAX_BSS_TYPE;
1946 return -EOPNOTSUPP;
1947 }
1948
Eliad Peller0603d892011-10-05 11:55:51 +02001949 wlvif->role_id = WL12XX_INVALID_ROLE_ID;
Eliad Peller7edebf52011-10-05 11:55:52 +02001950 wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID;
Eliad Pellerafaf8bd2011-10-05 11:55:57 +02001951 wlvif->dev_hlid = WL12XX_INVALID_LINK_ID;
Eliad Pellera8ab39a2011-10-05 11:55:54 +02001952
Eliad Pellere936bbe2011-10-05 11:55:56 +02001953 if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
1954 wlvif->bss_type == BSS_TYPE_IBSS) {
1955 /* init sta/ibss data */
1956 wlvif->sta.hlid = WL12XX_INVALID_LINK_ID;
1957
1958 } else {
1959 /* init ap data */
1960 wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID;
1961 wlvif->ap.global_hlid = WL12XX_INVALID_LINK_ID;
1962 }
Eliad Pellera8ab39a2011-10-05 11:55:54 +02001963
Eliad Peller83587502011-10-10 10:12:53 +02001964 wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate;
1965 wlvif->bitrate_masks[IEEE80211_BAND_5GHZ] = wl->conf.tx.basic_rate_5;
Eliad Peller87fbcb02011-10-05 11:55:41 +02001966 wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
Eliad Pellerd2d66c52011-10-05 11:55:43 +02001967 wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC;
Eliad Peller30d0c8f2011-10-05 11:55:42 +02001968 wlvif->rate_set = CONF_TX_RATE_MASK_BASIC;
Eliad Peller6a899792011-10-05 11:55:58 +02001969 wlvif->beacon_int = WL1271_DEFAULT_BEACON_INT;
1970
Eliad Peller1b92f152011-10-10 10:13:09 +02001971 /*
1972 * mac80211 configures some values globally, while we treat them
1973 * per-interface. thus, on init, we have to copy them from wl
1974 */
1975 wlvif->band = wl->band;
Eliad Peller61f845f2011-10-10 10:13:10 +02001976 wlvif->channel = wl->channel;
Eliad Peller6bd65022011-10-10 10:13:11 +02001977 wlvif->power_level = wl->power_level;
Eliad Peller1b92f152011-10-10 10:13:09 +02001978
Eliad Peller9eb599e2011-10-10 10:12:59 +02001979 INIT_WORK(&wlvif->rx_streaming_enable_work,
1980 wl1271_rx_streaming_enable_work);
1981 INIT_WORK(&wlvif->rx_streaming_disable_work,
1982 wl1271_rx_streaming_disable_work);
Eliad Peller252efa42011-10-05 11:56:00 +02001983 INIT_DELAYED_WORK(&wlvif->pspoll_work, wl1271_pspoll_work);
Eliad Peller87627212011-10-10 10:12:54 +02001984 INIT_LIST_HEAD(&wlvif->list);
Eliad Peller252efa42011-10-05 11:56:00 +02001985
Eliad Peller9eb599e2011-10-10 10:12:59 +02001986 setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
1987 (unsigned long) wlvif);
Eliad Pellere936bbe2011-10-05 11:55:56 +02001988 return 0;
Eliad Peller87fbcb02011-10-05 11:55:41 +02001989}
1990
Eliad Peller1d095472011-10-10 10:12:49 +02001991static bool wl12xx_init_fw(struct wl1271 *wl)
1992{
1993 int retries = WL1271_BOOT_RETRIES;
1994 bool booted = false;
1995 struct wiphy *wiphy = wl->hw->wiphy;
1996 int ret;
1997
1998 while (retries) {
1999 retries--;
2000 ret = wl1271_chip_wakeup(wl);
2001 if (ret < 0)
2002 goto power_off;
2003
2004 ret = wl1271_boot(wl);
2005 if (ret < 0)
2006 goto power_off;
2007
2008 ret = wl1271_hw_init(wl);
2009 if (ret < 0)
2010 goto irq_disable;
2011
2012 booted = true;
2013 break;
2014
2015irq_disable:
2016 mutex_unlock(&wl->mutex);
2017 /* Unlocking the mutex in the middle of handling is
2018 inherently unsafe. In this case we deem it safe to do,
2019 because we need to let any possibly pending IRQ out of
2020 the system (and while we are WL1271_STATE_OFF the IRQ
2021 work function will not do anything.) Also, any other
2022 possible concurrent operations will fail due to the
2023 current state, hence the wl1271 struct should be safe. */
2024 wl1271_disable_interrupts(wl);
2025 wl1271_flush_deferred_work(wl);
2026 cancel_work_sync(&wl->netstack_work);
2027 mutex_lock(&wl->mutex);
2028power_off:
2029 wl1271_power_off(wl);
2030 }
2031
2032 if (!booted) {
2033 wl1271_error("firmware boot failed despite %d retries",
2034 WL1271_BOOT_RETRIES);
2035 goto out;
2036 }
2037
2038 wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str);
2039
2040 /* update hw/fw version info in wiphy struct */
2041 wiphy->hw_version = wl->chip.id;
2042 strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
2043 sizeof(wiphy->fw_version));
2044
2045 /*
2046 * Now we know if 11a is supported (info from the NVS), so disable
2047 * 11a channels if not supported
2048 */
2049 if (!wl->enable_11a)
2050 wiphy->bands[IEEE80211_BAND_5GHZ]->n_channels = 0;
2051
2052 wl1271_debug(DEBUG_MAC80211, "11a is %ssupported",
2053 wl->enable_11a ? "" : "not ");
2054
2055 wl->state = WL1271_STATE_ON;
2056out:
2057 return booted;
2058}
2059
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002060static int wl1271_op_add_interface(struct ieee80211_hw *hw,
2061 struct ieee80211_vif *vif)
2062{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002063 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02002064 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002065 int ret = 0;
Eliad Pellerb78b47e2011-08-14 13:17:08 +03002066 u8 role_type;
Eliad Peller71125ab2010-10-28 21:46:43 +02002067 bool booted = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002068
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002069 wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
Eliad Peller045c7452011-08-28 15:23:01 +03002070 ieee80211_vif_type_p2p(vif), vif->addr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002071
2072 mutex_lock(&wl->mutex);
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002073 if (wl->vif) {
Eliad Peller71125ab2010-10-28 21:46:43 +02002074 wl1271_debug(DEBUG_MAC80211,
2075 "multiple vifs are not supported yet");
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002076 ret = -EBUSY;
2077 goto out;
2078 }
2079
Juuso Oikarinen13026de2011-03-29 16:43:50 +03002080 /*
2081 * in some very corner case HW recovery scenarios its possible to
2082 * get here before __wl1271_op_remove_interface is complete, so
2083 * opt out if that is the case.
2084 */
Eliad Peller10c8cd02011-10-10 10:13:06 +02002085 if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags) ||
2086 test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)) {
Juuso Oikarinen13026de2011-03-29 16:43:50 +03002087 ret = -EBUSY;
2088 goto out;
2089 }
2090
Eliad Peller83587502011-10-10 10:12:53 +02002091 ret = wl12xx_init_vif_data(wl, vif);
Eliad Pellere936bbe2011-10-05 11:55:56 +02002092 if (ret < 0)
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002093 goto out;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002094
Eliad Peller252efa42011-10-05 11:56:00 +02002095 wlvif->wl = wl;
Eliad Peller536129c2011-10-05 11:55:45 +02002096 role_type = wl12xx_get_role_type(wl, wlvif);
Eliad Pellerb78b47e2011-08-14 13:17:08 +03002097 if (role_type == WL12XX_INVALID_ROLE_TYPE) {
2098 ret = -EINVAL;
2099 goto out;
2100 }
Eliad Peller1d095472011-10-10 10:12:49 +02002101
Eliad Peller784f6942011-10-05 11:55:39 +02002102 /*
Eliad Peller1d095472011-10-10 10:12:49 +02002103 * TODO: after the nvs issue will be solved, move this block
2104 * to start(), and make sure here the driver is ON.
Eliad Peller784f6942011-10-05 11:55:39 +02002105 */
Eliad Peller1d095472011-10-10 10:12:49 +02002106 if (wl->state == WL1271_STATE_OFF) {
2107 /*
2108 * we still need this in order to configure the fw
2109 * while uploading the nvs
2110 */
2111 memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002112
Eliad Peller1d095472011-10-10 10:12:49 +02002113 booted = wl12xx_init_fw(wl);
2114 if (!booted) {
2115 ret = -EINVAL;
2116 goto out;
Eliad Peller04e80792011-08-14 13:17:09 +03002117 }
Eliad Peller1d095472011-10-10 10:12:49 +02002118 }
Eliad Peller04e80792011-08-14 13:17:09 +03002119
Eliad Peller1d095472011-10-10 10:12:49 +02002120 if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
2121 wlvif->bss_type == BSS_TYPE_IBSS) {
2122 /*
2123 * The device role is a special role used for
2124 * rx and tx frames prior to association (as
2125 * the STA role can get packets only from
2126 * its associated bssid)
2127 */
Eliad Peller784f6942011-10-05 11:55:39 +02002128 ret = wl12xx_cmd_role_enable(wl, vif->addr,
Eliad Peller1d095472011-10-10 10:12:49 +02002129 WL1271_ROLE_DEVICE,
2130 &wlvif->dev_role_id);
Eliad Pellerb78b47e2011-08-14 13:17:08 +03002131 if (ret < 0)
Eliad Peller1d095472011-10-10 10:12:49 +02002132 goto out;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02002133 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002134
Eliad Peller1d095472011-10-10 10:12:49 +02002135 ret = wl12xx_cmd_role_enable(wl, vif->addr,
2136 role_type, &wlvif->role_id);
2137 if (ret < 0)
Eliad Peller71125ab2010-10-28 21:46:43 +02002138 goto out;
Eliad Peller1d095472011-10-10 10:12:49 +02002139
2140 ret = wl1271_init_vif_specific(wl, vif);
2141 if (ret < 0)
2142 goto out;
Eliad Peller71125ab2010-10-28 21:46:43 +02002143
2144 wl->vif = vif;
Eliad Peller87627212011-10-10 10:12:54 +02002145 list_add(&wlvif->list, &wl->wlvif_list);
Eliad Peller10c8cd02011-10-10 10:13:06 +02002146 set_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags);
Eliad Pellera4e41302011-10-11 11:49:15 +02002147
2148 if (wlvif->bss_type == BSS_TYPE_AP_BSS)
2149 wl->ap_count++;
2150 else
2151 wl->sta_count++;
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +03002152out:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002153 mutex_unlock(&wl->mutex);
2154
Juuso Oikarinenf9f774c2011-03-21 10:43:36 +02002155 mutex_lock(&wl_list_mutex);
Juuso Oikarineneb887df2010-07-08 17:49:58 +03002156 if (!ret)
Juuso Oikarinen01c09162009-10-13 12:47:55 +03002157 list_add(&wl->list, &wl_list);
Juuso Oikarinenf9f774c2011-03-21 10:43:36 +02002158 mutex_unlock(&wl_list_mutex);
Juuso Oikarinen01c09162009-10-13 12:47:55 +03002159
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002160 return ret;
2161}
2162
Arik Nemtsov7dece1c2011-04-18 14:15:28 +03002163static void __wl1271_op_remove_interface(struct wl1271 *wl,
Eliad Peller536129c2011-10-05 11:55:45 +02002164 struct ieee80211_vif *vif,
Arik Nemtsov7dece1c2011-04-18 14:15:28 +03002165 bool reset_tx_queues)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002166{
Eliad Peller536129c2011-10-05 11:55:45 +02002167 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Eliad Pellerbaf62772011-10-10 10:12:52 +02002168 int ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002169
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002170 wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002171
Eliad Peller10c8cd02011-10-10 10:13:06 +02002172 if (!test_and_clear_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
2173 return;
2174
Juuso Oikarinen13026de2011-03-29 16:43:50 +03002175 /* because of hardware recovery, we may get here twice */
2176 if (wl->state != WL1271_STATE_ON)
2177 return;
2178
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002179 wl1271_info("down");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002180
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03002181 /* enable dyn ps just in case (if left on due to fw crash etc) */
Eliad Peller536129c2011-10-05 11:55:45 +02002182 if (wlvif->bss_type == BSS_TYPE_STA_BSS)
Eliad Pellerbaf62772011-10-10 10:12:52 +02002183 ieee80211_enable_dyn_ps(vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03002184
Eliad Pellerbaf62772011-10-10 10:12:52 +02002185 if (wl->scan.state != WL1271_SCAN_STATE_IDLE &&
2186 wl->scan_vif == vif) {
Luciano Coelho08688d62010-07-08 17:50:07 +03002187 wl->scan.state = WL1271_SCAN_STATE_IDLE;
Luciano Coelho4a31c112011-03-21 23:16:14 +02002188 memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
Eliad Peller784f6942011-10-05 11:55:39 +02002189 wl->scan_vif = NULL;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02002190 wl->scan.req = NULL;
Juuso Oikarinen76a029f2010-07-29 04:54:45 +03002191 ieee80211_scan_completed(wl->hw, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002192 }
2193
Eliad Pellerb78b47e2011-08-14 13:17:08 +03002194 if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
2195 /* disable active roles */
2196 ret = wl1271_ps_elp_wakeup(wl);
2197 if (ret < 0)
2198 goto deinit;
2199
Eliad Peller536129c2011-10-05 11:55:45 +02002200 if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
Eliad Peller7edebf52011-10-05 11:55:52 +02002201 ret = wl12xx_cmd_role_disable(wl, &wlvif->dev_role_id);
Eliad Peller04e80792011-08-14 13:17:09 +03002202 if (ret < 0)
2203 goto deinit;
2204 }
2205
Eliad Peller0603d892011-10-05 11:55:51 +02002206 ret = wl12xx_cmd_role_disable(wl, &wlvif->role_id);
Eliad Pellerb78b47e2011-08-14 13:17:08 +03002207 if (ret < 0)
2208 goto deinit;
2209
2210 wl1271_ps_elp_sleep(wl);
2211 }
2212deinit:
Arik Nemtsove51ae9b2011-08-14 13:17:21 +03002213 /* clear all hlids (except system_hlid) */
Eliad Peller154da672011-10-05 11:55:53 +02002214 wlvif->sta.hlid = WL12XX_INVALID_LINK_ID;
Eliad Pellerafaf8bd2011-10-05 11:55:57 +02002215 wlvif->dev_hlid = WL12XX_INVALID_LINK_ID;
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002216 wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID;
2217 wlvif->ap.global_hlid = WL12XX_INVALID_LINK_ID;
Eliad Pellerb78b47e2011-08-14 13:17:08 +03002218
Eliad Pellerd6a3cc22011-10-10 10:12:51 +02002219 wl12xx_tx_reset_wlvif(wl, wlvif);
Eliad Peller170d0e62011-10-05 11:56:06 +02002220 wl1271_free_ap_keys(wl, wlvif);
Eliad Peller87627212011-10-10 10:12:54 +02002221 list_del(&wlvif->list);
Eliad Pellerc7ffb902011-10-05 11:56:05 +02002222 memset(wlvif->ap.sta_hlid_map, 0, sizeof(wlvif->ap.sta_hlid_map));
Eliad Peller0603d892011-10-05 11:55:51 +02002223 wlvif->role_id = WL12XX_INVALID_ROLE_ID;
Eliad Peller7edebf52011-10-05 11:55:52 +02002224 wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID;
Luciano Coelhod6e19d12009-10-12 15:08:43 +03002225
Eliad Pellera4e41302011-10-11 11:49:15 +02002226 if (wlvif->bss_type == BSS_TYPE_AP_BSS)
2227 wl->ap_count--;
2228 else
2229 wl->sta_count--;
2230
Eliad Pellerbaf62772011-10-10 10:12:52 +02002231 mutex_unlock(&wl->mutex);
Eliad Peller9eb599e2011-10-10 10:12:59 +02002232 del_timer_sync(&wlvif->rx_streaming_timer);
2233 cancel_work_sync(&wlvif->rx_streaming_enable_work);
2234 cancel_work_sync(&wlvif->rx_streaming_disable_work);
Eliad Pellerbaf62772011-10-10 10:12:52 +02002235 cancel_delayed_work_sync(&wlvif->pspoll_work);
Eliad Pellerf4df1bd2011-08-14 13:17:15 +03002236
Eliad Pellerbaf62772011-10-10 10:12:52 +02002237 mutex_lock(&wl->mutex);
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02002238}
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03002239
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02002240static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
2241 struct ieee80211_vif *vif)
2242{
2243 struct wl1271 *wl = hw->priv;
Eliad Peller10c8cd02011-10-10 10:13:06 +02002244 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02002245
2246 mutex_lock(&wl->mutex);
Eliad Peller10c8cd02011-10-10 10:13:06 +02002247
2248 if (wl->state == WL1271_STATE_OFF ||
2249 !test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
2250 goto out;
2251
Juuso Oikarinen67353292010-11-18 15:19:02 +02002252 /*
2253 * wl->vif can be null here if someone shuts down the interface
2254 * just when hardware recovery has been started.
2255 */
2256 if (wl->vif) {
2257 WARN_ON(wl->vif != vif);
Eliad Peller536129c2011-10-05 11:55:45 +02002258 __wl1271_op_remove_interface(wl, vif, true);
Juuso Oikarinen67353292010-11-18 15:19:02 +02002259 }
Eliad Peller10c8cd02011-10-10 10:13:06 +02002260out:
Juuso Oikarinen67353292010-11-18 15:19:02 +02002261 mutex_unlock(&wl->mutex);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02002262 cancel_work_sync(&wl->recovery_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002263}
2264
Eliad Peller87fbcb02011-10-05 11:55:41 +02002265static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
2266 bool set_assoc)
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002267{
2268 int ret;
Eliad Peller536129c2011-10-05 11:55:45 +02002269 bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002270
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002271 /*
2272 * One of the side effects of the JOIN command is that is clears
2273 * WPA/WPA2 keys from the chipset. Performing a JOIN while associated
2274 * to a WPA/WPA2 access point will therefore kill the data-path.
Ohad Ben-Cohen8bf69aa2011-03-30 19:18:31 +02002275 * Currently the only valid scenario for JOIN during association
2276 * is on roaming, in which case we will also be given new keys.
2277 * Keep the below message for now, unless it starts bothering
2278 * users who really like to roam a lot :)
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002279 */
Eliad Pellerba8447f2011-10-10 10:13:00 +02002280 if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002281 wl1271_info("JOIN while associated.");
2282
2283 if (set_assoc)
Eliad Pellerba8447f2011-10-10 10:13:00 +02002284 set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002285
Eliad Peller227e81e2011-08-14 13:17:26 +03002286 if (is_ibss)
Eliad Peller87fbcb02011-10-05 11:55:41 +02002287 ret = wl12xx_cmd_role_start_ibss(wl, wlvif);
Eliad Peller227e81e2011-08-14 13:17:26 +03002288 else
Eliad Peller87fbcb02011-10-05 11:55:41 +02002289 ret = wl12xx_cmd_role_start_sta(wl, wlvif);
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002290 if (ret < 0)
2291 goto out;
2292
Eliad Pellerba8447f2011-10-10 10:13:00 +02002293 if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002294 goto out;
2295
2296 /*
2297 * The join command disable the keep-alive mode, shut down its process,
2298 * and also clear the template config, so we need to reset it all after
2299 * the join. The acx_aid starts the keep-alive process, and the order
2300 * of the commands below is relevant.
2301 */
Eliad Peller0603d892011-10-05 11:55:51 +02002302 ret = wl1271_acx_keep_alive_mode(wl, wlvif, true);
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002303 if (ret < 0)
2304 goto out;
2305
Eliad Peller0603d892011-10-05 11:55:51 +02002306 ret = wl1271_acx_aid(wl, wlvif, wlvif->aid);
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002307 if (ret < 0)
2308 goto out;
2309
Eliad Pellerd2d66c52011-10-05 11:55:43 +02002310 ret = wl12xx_cmd_build_klv_null_data(wl, wlvif);
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002311 if (ret < 0)
2312 goto out;
2313
Eliad Peller0603d892011-10-05 11:55:51 +02002314 ret = wl1271_acx_keep_alive_config(wl, wlvif,
2315 CMD_TEMPL_KLV_IDX_NULL_DATA,
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002316 ACX_KEEP_ALIVE_TPL_VALID);
2317 if (ret < 0)
2318 goto out;
2319
2320out:
2321 return ret;
2322}
2323
Eliad Peller0603d892011-10-05 11:55:51 +02002324static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02002325{
2326 int ret;
2327
Eliad Peller52630c52011-10-10 10:13:08 +02002328 if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
Shahar Levi6d158ff2011-09-08 13:01:33 +03002329 wl12xx_cmd_stop_channel_switch(wl);
2330 ieee80211_chswitch_done(wl->vif, false);
2331 }
2332
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02002333 /* to stop listening to a channel, we disconnect */
Eliad Peller0603d892011-10-05 11:55:51 +02002334 ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02002335 if (ret < 0)
2336 goto out;
2337
Oz Krakowskib992c682011-06-26 10:36:02 +03002338 /* reset TX security counters on a clean disconnect */
Eliad Peller48e93e42011-10-10 10:12:58 +02002339 wlvif->tx_security_last_seq_lsb = 0;
2340 wlvif->tx_security_seq = 0;
Oz Krakowskib992c682011-06-26 10:36:02 +03002341
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02002342out:
2343 return ret;
2344}
2345
Eliad Peller87fbcb02011-10-05 11:55:41 +02002346static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002347{
Eliad Peller1b92f152011-10-10 10:13:09 +02002348 wlvif->basic_rate_set = wlvif->bitrate_masks[wlvif->band];
Eliad Peller30d0c8f2011-10-05 11:55:42 +02002349 wlvif->rate_set = wlvif->basic_rate_set;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002350}
2351
Eliad Peller251c1772011-08-14 13:17:17 +03002352static bool wl12xx_is_roc(struct wl1271 *wl)
2353{
2354 u8 role_id;
2355
2356 role_id = find_first_bit(wl->roc_map, WL12XX_MAX_ROLES);
2357 if (role_id >= WL12XX_MAX_ROLES)
2358 return false;
2359
2360 return true;
2361}
2362
Eliad Peller87fbcb02011-10-05 11:55:41 +02002363static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
2364 bool idle)
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002365{
2366 int ret;
2367
2368 if (idle) {
Eliad Peller251c1772011-08-14 13:17:17 +03002369 /* no need to croc if we weren't busy (e.g. during boot) */
2370 if (wl12xx_is_roc(wl)) {
Eliad Peller7edebf52011-10-05 11:55:52 +02002371 ret = wl12xx_croc(wl, wlvif->dev_role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03002372 if (ret < 0)
2373 goto out;
2374
Eliad Peller7edebf52011-10-05 11:55:52 +02002375 ret = wl12xx_cmd_role_stop_dev(wl, wlvif);
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002376 if (ret < 0)
2377 goto out;
2378 }
Eliad Peller30d0c8f2011-10-05 11:55:42 +02002379 wlvif->rate_set =
2380 wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
2381 ret = wl1271_acx_sta_rate_policies(wl, wlvif);
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002382 if (ret < 0)
2383 goto out;
2384 ret = wl1271_acx_keep_alive_config(
Eliad Peller0603d892011-10-05 11:55:51 +02002385 wl, wlvif, CMD_TEMPL_KLV_IDX_NULL_DATA,
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002386 ACX_KEEP_ALIVE_TPL_INVALID);
2387 if (ret < 0)
2388 goto out;
2389 set_bit(WL1271_FLAG_IDLE, &wl->flags);
2390 } else {
Luciano Coelho33c2c062011-05-10 14:46:02 +03002391 /* The current firmware only supports sched_scan in idle */
2392 if (wl->sched_scanning) {
2393 wl1271_scan_sched_scan_stop(wl);
2394 ieee80211_sched_scan_stopped(wl->hw);
2395 }
2396
Eliad Peller7edebf52011-10-05 11:55:52 +02002397 ret = wl12xx_cmd_role_start_dev(wl, wlvif);
Eliad Peller251c1772011-08-14 13:17:17 +03002398 if (ret < 0)
2399 goto out;
2400
Eliad Peller1b92f152011-10-10 10:13:09 +02002401 ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id);
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002402 if (ret < 0)
2403 goto out;
2404 clear_bit(WL1271_FLAG_IDLE, &wl->flags);
2405 }
2406
2407out:
2408 return ret;
2409}
2410
Eliad Peller9f259c42011-10-10 10:13:12 +02002411static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
2412 struct ieee80211_conf *conf, u32 changed)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002413{
Eliad Peller9f259c42011-10-10 10:13:12 +02002414 bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
2415 int channel, ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002416
2417 channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
2418
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002419 /* if the channel changes while joined, join again */
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002420 if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
Eliad Peller1b92f152011-10-10 10:13:09 +02002421 ((wlvif->band != conf->channel->band) ||
Eliad Peller61f845f2011-10-10 10:13:10 +02002422 (wlvif->channel != channel))) {
Eliad Pellerc6930b02011-09-15 13:00:01 +03002423 /* send all pending packets */
Eliad Pellera32d0cd2011-10-10 10:12:55 +02002424 wl1271_tx_work_locked(wl);
Eliad Peller61f845f2011-10-10 10:13:10 +02002425 wlvif->band = conf->channel->band;
2426 wlvif->channel = channel;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002427
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002428 if (!is_ap) {
2429 /*
2430 * FIXME: the mac80211 should really provide a fixed
2431 * rate to use here. for now, just use the smallest
2432 * possible rate for the band as a fixed rate for
2433 * association frames and other control messages.
2434 */
Eliad Pellerba8447f2011-10-10 10:13:00 +02002435 if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
Eliad Peller87fbcb02011-10-05 11:55:41 +02002436 wl1271_set_band_rate(wl, wlvif);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002437
Eliad Pellerd2d66c52011-10-05 11:55:43 +02002438 wlvif->basic_rate =
Eliad Peller87fbcb02011-10-05 11:55:41 +02002439 wl1271_tx_min_rate_get(wl,
2440 wlvif->basic_rate_set);
Eliad Peller30d0c8f2011-10-05 11:55:42 +02002441 ret = wl1271_acx_sta_rate_policies(wl, wlvif);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002442 if (ret < 0)
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002443 wl1271_warning("rate policy for channel "
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002444 "failed %d", ret);
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002445
Eliad Pellerba8447f2011-10-10 10:13:00 +02002446 if (test_bit(WLVIF_FLAG_STA_ASSOCIATED,
2447 &wlvif->flags)) {
Eliad Peller251c1772011-08-14 13:17:17 +03002448 if (wl12xx_is_roc(wl)) {
2449 /* roaming */
Eliad Peller7edebf52011-10-05 11:55:52 +02002450 ret = wl12xx_croc(wl,
2451 wlvif->dev_role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03002452 if (ret < 0)
Eliad Peller9f259c42011-10-10 10:13:12 +02002453 return ret;
Eliad Peller251c1772011-08-14 13:17:17 +03002454 }
Eliad Peller87fbcb02011-10-05 11:55:41 +02002455 ret = wl1271_join(wl, wlvif, false);
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002456 if (ret < 0)
2457 wl1271_warning("cmd join on channel "
2458 "failed %d", ret);
Eliad Peller251c1772011-08-14 13:17:17 +03002459 } else {
2460 /*
2461 * change the ROC channel. do it only if we are
2462 * not idle. otherwise, CROC will be called
2463 * anyway.
2464 */
2465 if (wl12xx_is_roc(wl) &&
2466 !(conf->flags & IEEE80211_CONF_IDLE)) {
Eliad Peller7edebf52011-10-05 11:55:52 +02002467 ret = wl12xx_croc(wl,
2468 wlvif->dev_role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03002469 if (ret < 0)
Eliad Peller9f259c42011-10-10 10:13:12 +02002470 return ret;
Eliad Peller251c1772011-08-14 13:17:17 +03002471
Eliad Peller1b92f152011-10-10 10:13:09 +02002472 ret = wl12xx_roc(wl, wlvif,
Eliad Peller7edebf52011-10-05 11:55:52 +02002473 wlvif->dev_role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03002474 if (ret < 0)
2475 wl1271_warning("roc failed %d",
2476 ret);
2477 }
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002478 }
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002479 }
2480 }
2481
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002482 if (changed & IEEE80211_CONF_CHANGE_IDLE && !is_ap) {
Eliad Peller87fbcb02011-10-05 11:55:41 +02002483 ret = wl1271_sta_handle_idle(wl, wlvif,
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002484 conf->flags & IEEE80211_CONF_IDLE);
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002485 if (ret < 0)
2486 wl1271_warning("idle mode change failed %d", ret);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002487 }
2488
Juuso Oikarinen90494a92010-07-08 17:50:00 +03002489 /*
2490 * if mac80211 changes the PSM mode, make sure the mode is not
2491 * incorrectly changed after the pspoll failure active window.
2492 */
2493 if (changed & IEEE80211_CONF_CHANGE_PS)
Eliad Peller836d6602011-10-10 10:13:07 +02002494 clear_bit(WLVIF_FLAG_PSPOLL_FAILURE, &wlvif->flags);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03002495
Juuso Oikarinen71449f82009-12-11 15:41:07 +02002496 if (conf->flags & IEEE80211_CONF_PS &&
Eliad Pellerc29bb002011-10-10 10:13:03 +02002497 !test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags)) {
2498 set_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002499
2500 /*
2501 * We enter PSM only if we're already associated.
2502 * If we're not, we'll enter it when joining an SSID,
2503 * through the bss_info_changed() hook.
2504 */
Eliad Pellerba8447f2011-10-10 10:13:00 +02002505 if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02002506 wl1271_debug(DEBUG_PSM, "psm enabled");
Eliad Peller0603d892011-10-05 11:55:51 +02002507 ret = wl1271_ps_set_mode(wl, wlvif,
2508 STATION_POWER_SAVE_MODE,
Eliad Pellerd2d66c52011-10-05 11:55:43 +02002509 wlvif->basic_rate, true);
Juuso Oikarinenaf5e0842009-12-11 15:41:00 +02002510 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002511 } else if (!(conf->flags & IEEE80211_CONF_PS) &&
Eliad Pellerc29bb002011-10-10 10:13:03 +02002512 test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02002513 wl1271_debug(DEBUG_PSM, "psm disabled");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002514
Eliad Pellerc29bb002011-10-10 10:13:03 +02002515 clear_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002516
Eliad Pellerc29bb002011-10-10 10:13:03 +02002517 if (test_bit(WLVIF_FLAG_PSM, &wlvif->flags))
Eliad Peller0603d892011-10-05 11:55:51 +02002518 ret = wl1271_ps_set_mode(wl, wlvif,
2519 STATION_ACTIVE_MODE,
Eliad Pellerd2d66c52011-10-05 11:55:43 +02002520 wlvif->basic_rate, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002521 }
2522
Eliad Peller6bd65022011-10-10 10:13:11 +02002523 if (conf->power_level != wlvif->power_level) {
Eliad Peller0603d892011-10-05 11:55:51 +02002524 ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002525 if (ret < 0)
Eliad Peller9f259c42011-10-10 10:13:12 +02002526 return ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002527
Eliad Peller6bd65022011-10-10 10:13:11 +02002528 wlvif->power_level = conf->power_level;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002529 }
2530
Eliad Peller9f259c42011-10-10 10:13:12 +02002531 return 0;
2532}
2533
2534static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
2535{
2536 struct wl1271 *wl = hw->priv;
2537 struct wl12xx_vif *wlvif;
2538 struct ieee80211_conf *conf = &hw->conf;
2539 int channel, ret = 0;
2540
2541 channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
2542
2543 wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s"
2544 " changed 0x%x",
2545 channel,
2546 conf->flags & IEEE80211_CONF_PS ? "on" : "off",
2547 conf->power_level,
2548 conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
2549 changed);
2550
2551 /*
2552 * mac80211 will go to idle nearly immediately after transmitting some
2553 * frames, such as the deauth. To make sure those frames reach the air,
2554 * wait here until the TX queue is fully flushed.
2555 */
2556 if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
2557 (conf->flags & IEEE80211_CONF_IDLE))
2558 wl1271_tx_flush(wl);
2559
2560 mutex_lock(&wl->mutex);
2561
2562 /* we support configuring the channel and band even while off */
2563 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
2564 wl->band = conf->channel->band;
2565 wl->channel = channel;
2566 }
2567
2568 if (changed & IEEE80211_CONF_CHANGE_POWER)
2569 wl->power_level = conf->power_level;
2570
2571 if (unlikely(wl->state == WL1271_STATE_OFF))
2572 goto out;
2573
2574 ret = wl1271_ps_elp_wakeup(wl);
2575 if (ret < 0)
2576 goto out;
2577
2578 /* configure each interface */
2579 wl12xx_for_each_wlvif(wl, wlvif) {
2580 ret = wl12xx_config_vif(wl, wlvif, conf, changed);
2581 if (ret < 0)
2582 goto out_sleep;
2583 }
2584
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002585out_sleep:
2586 wl1271_ps_elp_sleep(wl);
2587
2588out:
2589 mutex_unlock(&wl->mutex);
2590
2591 return ret;
2592}
2593
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002594struct wl1271_filter_params {
2595 bool enabled;
2596 int mc_list_length;
2597 u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
2598};
2599
Jiri Pirko22bedad2010-04-01 21:22:57 +00002600static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw,
2601 struct netdev_hw_addr_list *mc_list)
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002602{
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002603 struct wl1271_filter_params *fp;
Jiri Pirko22bedad2010-04-01 21:22:57 +00002604 struct netdev_hw_addr *ha;
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03002605 struct wl1271 *wl = hw->priv;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002606
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03002607 if (unlikely(wl->state == WL1271_STATE_OFF))
2608 return 0;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002609
Juuso Oikarinen74441132009-10-13 12:47:53 +03002610 fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002611 if (!fp) {
2612 wl1271_error("Out of memory setting filters.");
2613 return 0;
2614 }
2615
2616 /* update multicast filtering parameters */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002617 fp->mc_list_length = 0;
Jiri Pirko22bedad2010-04-01 21:22:57 +00002618 if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) {
2619 fp->enabled = false;
2620 } else {
2621 fp->enabled = true;
2622 netdev_hw_addr_list_for_each(ha, mc_list) {
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002623 memcpy(fp->mc_list[fp->mc_list_length],
Jiri Pirko22bedad2010-04-01 21:22:57 +00002624 ha->addr, ETH_ALEN);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002625 fp->mc_list_length++;
Jiri Pirko22bedad2010-04-01 21:22:57 +00002626 }
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002627 }
2628
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002629 return (u64)(unsigned long)fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002630}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002631
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002632#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
2633 FIF_ALLMULTI | \
2634 FIF_FCSFAIL | \
2635 FIF_BCN_PRBRESP_PROMISC | \
2636 FIF_CONTROL | \
2637 FIF_OTHER_BSS)
2638
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002639static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
2640 unsigned int changed,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002641 unsigned int *total, u64 multicast)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002642{
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002643 struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002644 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02002645 struct ieee80211_vif *vif = wl->vif; /* TODO: get as param */
2646 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
2647
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002648 int ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002649
Arik Nemtsov7d057862010-10-16 19:25:35 +02002650 wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter changed %x"
2651 " total %x", changed, *total);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002652
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002653 mutex_lock(&wl->mutex);
2654
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03002655 *total &= WL1271_SUPPORTED_FILTERS;
2656 changed &= WL1271_SUPPORTED_FILTERS;
2657
2658 if (unlikely(wl->state == WL1271_STATE_OFF))
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002659 goto out;
2660
Ido Yariva6208652011-03-01 15:14:41 +02002661 ret = wl1271_ps_elp_wakeup(wl);
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002662 if (ret < 0)
2663 goto out;
2664
Eliad Peller536129c2011-10-05 11:55:45 +02002665 if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
Arik Nemtsov7d057862010-10-16 19:25:35 +02002666 if (*total & FIF_ALLMULTI)
Eliad Peller0603d892011-10-05 11:55:51 +02002667 ret = wl1271_acx_group_address_tbl(wl, wlvif, false,
2668 NULL, 0);
Arik Nemtsov7d057862010-10-16 19:25:35 +02002669 else if (fp)
Eliad Peller0603d892011-10-05 11:55:51 +02002670 ret = wl1271_acx_group_address_tbl(wl, wlvif,
2671 fp->enabled,
Arik Nemtsov7d057862010-10-16 19:25:35 +02002672 fp->mc_list,
2673 fp->mc_list_length);
2674 if (ret < 0)
2675 goto out_sleep;
2676 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002677
Eliad Peller08c1d1c2011-08-14 13:17:04 +03002678 /*
2679 * the fw doesn't provide an api to configure the filters. instead,
2680 * the filters configuration is based on the active roles / ROC
2681 * state.
2682 */
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002683
2684out_sleep:
2685 wl1271_ps_elp_sleep(wl);
2686
2687out:
2688 mutex_unlock(&wl->mutex);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02002689 kfree(fp);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002690}
2691
Eliad Peller170d0e62011-10-05 11:56:06 +02002692static int wl1271_record_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
2693 u8 id, u8 key_type, u8 key_size,
2694 const u8 *key, u8 hlid, u32 tx_seq_32,
2695 u16 tx_seq_16)
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002696{
2697 struct wl1271_ap_key *ap_key;
2698 int i;
2699
2700 wl1271_debug(DEBUG_CRYPT, "record ap key id %d", (int)id);
2701
2702 if (key_size > MAX_KEY_SIZE)
2703 return -EINVAL;
2704
2705 /*
2706 * Find next free entry in ap_keys. Also check we are not replacing
2707 * an existing key.
2708 */
2709 for (i = 0; i < MAX_NUM_KEYS; i++) {
Eliad Peller170d0e62011-10-05 11:56:06 +02002710 if (wlvif->ap.recorded_keys[i] == NULL)
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002711 break;
2712
Eliad Peller170d0e62011-10-05 11:56:06 +02002713 if (wlvif->ap.recorded_keys[i]->id == id) {
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002714 wl1271_warning("trying to record key replacement");
2715 return -EINVAL;
2716 }
2717 }
2718
2719 if (i == MAX_NUM_KEYS)
2720 return -EBUSY;
2721
2722 ap_key = kzalloc(sizeof(*ap_key), GFP_KERNEL);
2723 if (!ap_key)
2724 return -ENOMEM;
2725
2726 ap_key->id = id;
2727 ap_key->key_type = key_type;
2728 ap_key->key_size = key_size;
2729 memcpy(ap_key->key, key, key_size);
2730 ap_key->hlid = hlid;
2731 ap_key->tx_seq_32 = tx_seq_32;
2732 ap_key->tx_seq_16 = tx_seq_16;
2733
Eliad Peller170d0e62011-10-05 11:56:06 +02002734 wlvif->ap.recorded_keys[i] = ap_key;
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002735 return 0;
2736}
2737
Eliad Peller170d0e62011-10-05 11:56:06 +02002738static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002739{
2740 int i;
2741
2742 for (i = 0; i < MAX_NUM_KEYS; i++) {
Eliad Peller170d0e62011-10-05 11:56:06 +02002743 kfree(wlvif->ap.recorded_keys[i]);
2744 wlvif->ap.recorded_keys[i] = NULL;
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002745 }
2746}
2747
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002748static int wl1271_ap_init_hwenc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002749{
2750 int i, ret = 0;
2751 struct wl1271_ap_key *key;
2752 bool wep_key_added = false;
2753
2754 for (i = 0; i < MAX_NUM_KEYS; i++) {
Eliad Peller7f97b482011-08-14 13:17:30 +03002755 u8 hlid;
Eliad Peller170d0e62011-10-05 11:56:06 +02002756 if (wlvif->ap.recorded_keys[i] == NULL)
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002757 break;
2758
Eliad Peller170d0e62011-10-05 11:56:06 +02002759 key = wlvif->ap.recorded_keys[i];
Eliad Peller7f97b482011-08-14 13:17:30 +03002760 hlid = key->hlid;
2761 if (hlid == WL12XX_INVALID_LINK_ID)
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002762 hlid = wlvif->ap.bcast_hlid;
Eliad Peller7f97b482011-08-14 13:17:30 +03002763
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002764 ret = wl1271_cmd_set_ap_key(wl, wlvif, KEY_ADD_OR_REPLACE,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002765 key->id, key->key_type,
2766 key->key_size, key->key,
Eliad Peller7f97b482011-08-14 13:17:30 +03002767 hlid, key->tx_seq_32,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002768 key->tx_seq_16);
2769 if (ret < 0)
2770 goto out;
2771
2772 if (key->key_type == KEY_WEP)
2773 wep_key_added = true;
2774 }
2775
2776 if (wep_key_added) {
Eliad Pellerf75c753f2011-10-05 11:55:59 +02002777 ret = wl12xx_cmd_set_default_wep_key(wl, wlvif->default_key,
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002778 wlvif->ap.bcast_hlid);
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002779 if (ret < 0)
2780 goto out;
2781 }
2782
2783out:
Eliad Peller170d0e62011-10-05 11:56:06 +02002784 wl1271_free_ap_keys(wl, wlvif);
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002785 return ret;
2786}
2787
Eliad Peller536129c2011-10-05 11:55:45 +02002788static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
2789 u16 action, u8 id, u8 key_type,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002790 u8 key_size, const u8 *key, u32 tx_seq_32,
2791 u16 tx_seq_16, struct ieee80211_sta *sta)
2792{
2793 int ret;
Eliad Peller536129c2011-10-05 11:55:45 +02002794 bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002795
2796 if (is_ap) {
2797 struct wl1271_station *wl_sta;
2798 u8 hlid;
2799
2800 if (sta) {
2801 wl_sta = (struct wl1271_station *)sta->drv_priv;
2802 hlid = wl_sta->hlid;
2803 } else {
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002804 hlid = wlvif->ap.bcast_hlid;
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002805 }
2806
Eliad Peller53d40d02011-10-10 10:13:02 +02002807 if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002808 /*
2809 * We do not support removing keys after AP shutdown.
2810 * Pretend we do to make mac80211 happy.
2811 */
2812 if (action != KEY_ADD_OR_REPLACE)
2813 return 0;
2814
Eliad Peller170d0e62011-10-05 11:56:06 +02002815 ret = wl1271_record_ap_key(wl, wlvif, id,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002816 key_type, key_size,
2817 key, hlid, tx_seq_32,
2818 tx_seq_16);
2819 } else {
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002820 ret = wl1271_cmd_set_ap_key(wl, wlvif, action,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002821 id, key_type, key_size,
2822 key, hlid, tx_seq_32,
2823 tx_seq_16);
2824 }
2825
2826 if (ret < 0)
2827 return ret;
2828 } else {
2829 const u8 *addr;
2830 static const u8 bcast_addr[ETH_ALEN] = {
2831 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
2832 };
2833
Guy Eilame9eb8cb2011-08-16 19:49:12 +03002834 /*
2835 * A STA set to GEM cipher requires 2 tx spare blocks.
2836 * Return to default value when GEM cipher key is removed
2837 */
2838 if (key_type == KEY_GEM) {
2839 if (action == KEY_ADD_OR_REPLACE)
2840 wl->tx_spare_blocks = 2;
2841 else if (action == KEY_REMOVE)
2842 wl->tx_spare_blocks = TX_HW_BLOCK_SPARE_DEFAULT;
2843 }
2844
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002845 addr = sta ? sta->addr : bcast_addr;
2846
2847 if (is_zero_ether_addr(addr)) {
2848 /* We dont support TX only encryption */
2849 return -EOPNOTSUPP;
2850 }
2851
2852 /* The wl1271 does not allow to remove unicast keys - they
2853 will be cleared automatically on next CMD_JOIN. Ignore the
2854 request silently, as we dont want the mac80211 to emit
2855 an error message. */
2856 if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr))
2857 return 0;
2858
Eliad Peller010d3d32011-08-14 13:17:31 +03002859 /* don't remove key if hlid was already deleted */
2860 if (action == KEY_REMOVE &&
Eliad Peller154da672011-10-05 11:55:53 +02002861 wlvif->sta.hlid == WL12XX_INVALID_LINK_ID)
Eliad Peller010d3d32011-08-14 13:17:31 +03002862 return 0;
2863
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002864 ret = wl1271_cmd_set_sta_key(wl, wlvif, action,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002865 id, key_type, key_size,
2866 key, addr, tx_seq_32,
2867 tx_seq_16);
2868 if (ret < 0)
2869 return ret;
2870
2871 /* the default WEP key needs to be configured at least once */
2872 if (key_type == KEY_WEP) {
Eliad Pellerc690ec82011-08-14 13:17:07 +03002873 ret = wl12xx_cmd_set_default_wep_key(wl,
Eliad Pellerf75c753f2011-10-05 11:55:59 +02002874 wlvif->default_key,
2875 wlvif->sta.hlid);
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002876 if (ret < 0)
2877 return ret;
2878 }
2879 }
2880
2881 return 0;
2882}
2883
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002884static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
2885 struct ieee80211_vif *vif,
2886 struct ieee80211_sta *sta,
2887 struct ieee80211_key_conf *key_conf)
2888{
2889 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02002890 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002891 int ret;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03002892 u32 tx_seq_32 = 0;
2893 u16 tx_seq_16 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002894 u8 key_type;
2895
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002896 wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
2897
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002898 wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002899 wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
Johannes Berg97359d12010-08-10 09:46:38 +02002900 key_conf->cipher, key_conf->keyidx,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002901 key_conf->keylen, key_conf->flags);
2902 wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
2903
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002904 mutex_lock(&wl->mutex);
2905
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002906 if (unlikely(wl->state == WL1271_STATE_OFF)) {
2907 ret = -EAGAIN;
2908 goto out_unlock;
2909 }
2910
Ido Yariva6208652011-03-01 15:14:41 +02002911 ret = wl1271_ps_elp_wakeup(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002912 if (ret < 0)
2913 goto out_unlock;
2914
Johannes Berg97359d12010-08-10 09:46:38 +02002915 switch (key_conf->cipher) {
2916 case WLAN_CIPHER_SUITE_WEP40:
2917 case WLAN_CIPHER_SUITE_WEP104:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002918 key_type = KEY_WEP;
2919
2920 key_conf->hw_key_idx = key_conf->keyidx;
2921 break;
Johannes Berg97359d12010-08-10 09:46:38 +02002922 case WLAN_CIPHER_SUITE_TKIP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002923 key_type = KEY_TKIP;
2924
2925 key_conf->hw_key_idx = key_conf->keyidx;
Eliad Peller48e93e42011-10-10 10:12:58 +02002926 tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
2927 tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002928 break;
Johannes Berg97359d12010-08-10 09:46:38 +02002929 case WLAN_CIPHER_SUITE_CCMP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002930 key_type = KEY_AES;
2931
2932 key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Eliad Peller48e93e42011-10-10 10:12:58 +02002933 tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
2934 tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002935 break;
Juuso Oikarinen7a557242010-09-27 12:42:07 +02002936 case WL1271_CIPHER_SUITE_GEM:
2937 key_type = KEY_GEM;
Eliad Peller48e93e42011-10-10 10:12:58 +02002938 tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
2939 tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
Juuso Oikarinen7a557242010-09-27 12:42:07 +02002940 break;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002941 default:
Johannes Berg97359d12010-08-10 09:46:38 +02002942 wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002943
2944 ret = -EOPNOTSUPP;
2945 goto out_sleep;
2946 }
2947
2948 switch (cmd) {
2949 case SET_KEY:
Eliad Peller536129c2011-10-05 11:55:45 +02002950 ret = wl1271_set_key(wl, wlvif, KEY_ADD_OR_REPLACE,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002951 key_conf->keyidx, key_type,
2952 key_conf->keylen, key_conf->key,
2953 tx_seq_32, tx_seq_16, sta);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002954 if (ret < 0) {
2955 wl1271_error("Could not add or replace key");
2956 goto out_sleep;
2957 }
2958 break;
2959
2960 case DISABLE_KEY:
Eliad Peller536129c2011-10-05 11:55:45 +02002961 ret = wl1271_set_key(wl, wlvif, KEY_REMOVE,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002962 key_conf->keyidx, key_type,
2963 key_conf->keylen, key_conf->key,
2964 0, 0, sta);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002965 if (ret < 0) {
2966 wl1271_error("Could not remove key");
2967 goto out_sleep;
2968 }
2969 break;
2970
2971 default:
2972 wl1271_error("Unsupported key cmd 0x%x", cmd);
2973 ret = -EOPNOTSUPP;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002974 break;
2975 }
2976
2977out_sleep:
2978 wl1271_ps_elp_sleep(wl);
2979
2980out_unlock:
2981 mutex_unlock(&wl->mutex);
2982
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002983 return ret;
2984}
2985
2986static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
Johannes Berga060bbf2010-04-27 11:59:34 +02002987 struct ieee80211_vif *vif,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002988 struct cfg80211_scan_request *req)
2989{
2990 struct wl1271 *wl = hw->priv;
Eliad Peller7edebf52011-10-05 11:55:52 +02002991 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
2992
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002993 int ret;
2994 u8 *ssid = NULL;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03002995 size_t len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002996
2997 wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
2998
2999 if (req->n_ssids) {
3000 ssid = req->ssids[0].ssid;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03003001 len = req->ssids[0].ssid_len;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003002 }
3003
3004 mutex_lock(&wl->mutex);
3005
Juuso Oikarinenb739a422010-10-26 13:24:38 +02003006 if (wl->state == WL1271_STATE_OFF) {
3007 /*
3008 * We cannot return -EBUSY here because cfg80211 will expect
3009 * a call to ieee80211_scan_completed if we do - in this case
3010 * there won't be any call.
3011 */
3012 ret = -EAGAIN;
3013 goto out;
3014 }
3015
Ido Yariva6208652011-03-01 15:14:41 +02003016 ret = wl1271_ps_elp_wakeup(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003017 if (ret < 0)
3018 goto out;
3019
Eliad Peller251c1772011-08-14 13:17:17 +03003020 /* cancel ROC before scanning */
3021 if (wl12xx_is_roc(wl)) {
Eliad Pellerba8447f2011-10-10 10:13:00 +02003022 if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
Eliad Peller251c1772011-08-14 13:17:17 +03003023 /* don't allow scanning right now */
3024 ret = -EBUSY;
3025 goto out_sleep;
3026 }
Eliad Peller7edebf52011-10-05 11:55:52 +02003027 wl12xx_croc(wl, wlvif->dev_role_id);
3028 wl12xx_cmd_role_stop_dev(wl, wlvif);
Eliad Peller251c1772011-08-14 13:17:17 +03003029 }
3030
Eliad Peller784f6942011-10-05 11:55:39 +02003031 ret = wl1271_scan(hw->priv, vif, ssid, len, req);
Eliad Peller251c1772011-08-14 13:17:17 +03003032out_sleep:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003033 wl1271_ps_elp_sleep(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003034out:
3035 mutex_unlock(&wl->mutex);
3036
3037 return ret;
3038}
3039
Eliad Peller73ecce32011-06-27 13:06:45 +03003040static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
3041 struct ieee80211_vif *vif)
3042{
3043 struct wl1271 *wl = hw->priv;
3044 int ret;
3045
3046 wl1271_debug(DEBUG_MAC80211, "mac80211 cancel hw scan");
3047
3048 mutex_lock(&wl->mutex);
3049
3050 if (wl->state == WL1271_STATE_OFF)
3051 goto out;
3052
3053 if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
3054 goto out;
3055
3056 ret = wl1271_ps_elp_wakeup(wl);
3057 if (ret < 0)
3058 goto out;
3059
3060 if (wl->scan.state != WL1271_SCAN_STATE_DONE) {
3061 ret = wl1271_scan_stop(wl);
3062 if (ret < 0)
3063 goto out_sleep;
3064 }
3065 wl->scan.state = WL1271_SCAN_STATE_IDLE;
3066 memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
Eliad Peller784f6942011-10-05 11:55:39 +02003067 wl->scan_vif = NULL;
Eliad Peller73ecce32011-06-27 13:06:45 +03003068 wl->scan.req = NULL;
3069 ieee80211_scan_completed(wl->hw, true);
3070
3071out_sleep:
3072 wl1271_ps_elp_sleep(wl);
3073out:
3074 mutex_unlock(&wl->mutex);
3075
3076 cancel_delayed_work_sync(&wl->scan_complete_work);
3077}
3078
Luciano Coelho33c2c062011-05-10 14:46:02 +03003079static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
3080 struct ieee80211_vif *vif,
3081 struct cfg80211_sched_scan_request *req,
3082 struct ieee80211_sched_scan_ies *ies)
3083{
3084 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02003085 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Luciano Coelho33c2c062011-05-10 14:46:02 +03003086 int ret;
3087
3088 wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_start");
3089
3090 mutex_lock(&wl->mutex);
3091
3092 ret = wl1271_ps_elp_wakeup(wl);
3093 if (ret < 0)
3094 goto out;
3095
Eliad Peller536129c2011-10-05 11:55:45 +02003096 ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies);
Luciano Coelho33c2c062011-05-10 14:46:02 +03003097 if (ret < 0)
3098 goto out_sleep;
3099
Eliad Peller536129c2011-10-05 11:55:45 +02003100 ret = wl1271_scan_sched_scan_start(wl, wlvif);
Luciano Coelho33c2c062011-05-10 14:46:02 +03003101 if (ret < 0)
3102 goto out_sleep;
3103
3104 wl->sched_scanning = true;
3105
3106out_sleep:
3107 wl1271_ps_elp_sleep(wl);
3108out:
3109 mutex_unlock(&wl->mutex);
3110 return ret;
3111}
3112
3113static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
3114 struct ieee80211_vif *vif)
3115{
3116 struct wl1271 *wl = hw->priv;
3117 int ret;
3118
3119 wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_stop");
3120
3121 mutex_lock(&wl->mutex);
3122
3123 ret = wl1271_ps_elp_wakeup(wl);
3124 if (ret < 0)
3125 goto out;
3126
3127 wl1271_scan_sched_scan_stop(wl);
3128
3129 wl1271_ps_elp_sleep(wl);
3130out:
3131 mutex_unlock(&wl->mutex);
3132}
3133
Arik Nemtsov68d069c2010-11-08 10:51:07 +01003134static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
3135{
3136 struct wl1271 *wl = hw->priv;
3137 int ret = 0;
3138
3139 mutex_lock(&wl->mutex);
3140
3141 if (unlikely(wl->state == WL1271_STATE_OFF)) {
3142 ret = -EAGAIN;
3143 goto out;
3144 }
3145
Ido Yariva6208652011-03-01 15:14:41 +02003146 ret = wl1271_ps_elp_wakeup(wl);
Arik Nemtsov68d069c2010-11-08 10:51:07 +01003147 if (ret < 0)
3148 goto out;
3149
Arik Nemtsov5f704d12011-04-18 14:15:21 +03003150 ret = wl1271_acx_frag_threshold(wl, value);
Arik Nemtsov68d069c2010-11-08 10:51:07 +01003151 if (ret < 0)
3152 wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret);
3153
3154 wl1271_ps_elp_sleep(wl);
3155
3156out:
3157 mutex_unlock(&wl->mutex);
3158
3159 return ret;
3160}
3161
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003162static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
3163{
3164 struct wl1271 *wl = hw->priv;
Eliad Peller0603d892011-10-05 11:55:51 +02003165 struct ieee80211_vif *vif = wl->vif;
3166 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03003167 int ret = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003168
3169 mutex_lock(&wl->mutex);
3170
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02003171 if (unlikely(wl->state == WL1271_STATE_OFF)) {
3172 ret = -EAGAIN;
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03003173 goto out;
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02003174 }
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03003175
Ido Yariva6208652011-03-01 15:14:41 +02003176 ret = wl1271_ps_elp_wakeup(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003177 if (ret < 0)
3178 goto out;
3179
Eliad Peller0603d892011-10-05 11:55:51 +02003180 ret = wl1271_acx_rts_threshold(wl, wlvif, value);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003181 if (ret < 0)
3182 wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret);
3183
3184 wl1271_ps_elp_sleep(wl);
3185
3186out:
3187 mutex_unlock(&wl->mutex);
3188
3189 return ret;
3190}
3191
Eliad Peller1fe9f162011-10-05 11:55:48 +02003192static int wl1271_ssid_set(struct ieee80211_vif *vif, struct sk_buff *skb,
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003193 int offset)
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02003194{
Eliad Peller1fe9f162011-10-05 11:55:48 +02003195 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Eliad Peller889cb362011-05-01 09:56:45 +03003196 u8 ssid_len;
3197 const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
3198 skb->len - offset);
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02003199
Eliad Peller889cb362011-05-01 09:56:45 +03003200 if (!ptr) {
3201 wl1271_error("No SSID in IEs!");
3202 return -ENOENT;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02003203 }
Arik Nemtsove78a2872010-10-16 19:07:21 +02003204
Eliad Peller889cb362011-05-01 09:56:45 +03003205 ssid_len = ptr[1];
3206 if (ssid_len > IEEE80211_MAX_SSID_LEN) {
3207 wl1271_error("SSID is too long!");
3208 return -EINVAL;
3209 }
3210
Eliad Peller1fe9f162011-10-05 11:55:48 +02003211 wlvif->ssid_len = ssid_len;
3212 memcpy(wlvif->ssid, ptr+2, ssid_len);
Eliad Peller889cb362011-05-01 09:56:45 +03003213 return 0;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02003214}
3215
Eliad Pellerd48055d2011-09-15 12:07:04 +03003216static void wl12xx_remove_ie(struct sk_buff *skb, u8 eid, int ieoffset)
3217{
3218 int len;
3219 const u8 *next, *end = skb->data + skb->len;
3220 u8 *ie = (u8 *)cfg80211_find_ie(eid, skb->data + ieoffset,
3221 skb->len - ieoffset);
3222 if (!ie)
3223 return;
3224 len = ie[1] + 2;
3225 next = ie + len;
3226 memmove(ie, next, end - next);
3227 skb_trim(skb, skb->len - len);
3228}
3229
Eliad Peller26b4bf22011-09-15 12:07:05 +03003230static void wl12xx_remove_vendor_ie(struct sk_buff *skb,
3231 unsigned int oui, u8 oui_type,
3232 int ieoffset)
3233{
3234 int len;
3235 const u8 *next, *end = skb->data + skb->len;
3236 u8 *ie = (u8 *)cfg80211_find_vendor_ie(oui, oui_type,
3237 skb->data + ieoffset,
3238 skb->len - ieoffset);
3239 if (!ie)
3240 return;
3241 len = ie[1] + 2;
3242 next = ie + len;
3243 memmove(ie, next, end - next);
3244 skb_trim(skb, skb->len - len);
3245}
3246
Arik Nemtsov68eaaf62011-09-03 20:22:03 +03003247static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl,
Eliad Peller1fe9f162011-10-05 11:55:48 +02003248 struct ieee80211_vif *vif,
Arik Nemtsov68eaaf62011-09-03 20:22:03 +03003249 u8 *probe_rsp_data,
3250 size_t probe_rsp_len,
3251 u32 rates)
3252{
Eliad Peller1fe9f162011-10-05 11:55:48 +02003253 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
3254 struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
Arik Nemtsov68eaaf62011-09-03 20:22:03 +03003255 u8 probe_rsp_templ[WL1271_CMD_TEMPL_MAX_SIZE];
3256 int ssid_ie_offset, ie_offset, templ_len;
3257 const u8 *ptr;
3258
3259 /* no need to change probe response if the SSID is set correctly */
Eliad Peller1fe9f162011-10-05 11:55:48 +02003260 if (wlvif->ssid_len > 0)
Arik Nemtsov68eaaf62011-09-03 20:22:03 +03003261 return wl1271_cmd_template_set(wl,
3262 CMD_TEMPL_AP_PROBE_RESPONSE,
3263 probe_rsp_data,
3264 probe_rsp_len, 0,
3265 rates);
3266
3267 if (probe_rsp_len + bss_conf->ssid_len > WL1271_CMD_TEMPL_MAX_SIZE) {
3268 wl1271_error("probe_rsp template too big");
3269 return -EINVAL;
3270 }
3271
3272 /* start searching from IE offset */
3273 ie_offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
3274
3275 ptr = cfg80211_find_ie(WLAN_EID_SSID, probe_rsp_data + ie_offset,
3276 probe_rsp_len - ie_offset);
3277 if (!ptr) {
3278 wl1271_error("No SSID in beacon!");
3279 return -EINVAL;
3280 }
3281
3282 ssid_ie_offset = ptr - probe_rsp_data;
3283 ptr += (ptr[1] + 2);
3284
3285 memcpy(probe_rsp_templ, probe_rsp_data, ssid_ie_offset);
3286
3287 /* insert SSID from bss_conf */
3288 probe_rsp_templ[ssid_ie_offset] = WLAN_EID_SSID;
3289 probe_rsp_templ[ssid_ie_offset + 1] = bss_conf->ssid_len;
3290 memcpy(probe_rsp_templ + ssid_ie_offset + 2,
3291 bss_conf->ssid, bss_conf->ssid_len);
3292 templ_len = ssid_ie_offset + 2 + bss_conf->ssid_len;
3293
3294 memcpy(probe_rsp_templ + ssid_ie_offset + 2 + bss_conf->ssid_len,
3295 ptr, probe_rsp_len - (ptr - probe_rsp_data));
3296 templ_len += probe_rsp_len - (ptr - probe_rsp_data);
3297
3298 return wl1271_cmd_template_set(wl,
3299 CMD_TEMPL_AP_PROBE_RESPONSE,
3300 probe_rsp_templ,
3301 templ_len, 0,
3302 rates);
3303}
3304
Arik Nemtsove78a2872010-10-16 19:07:21 +02003305static int wl1271_bss_erp_info_changed(struct wl1271 *wl,
Eliad Peller0603d892011-10-05 11:55:51 +02003306 struct ieee80211_vif *vif,
Arik Nemtsove78a2872010-10-16 19:07:21 +02003307 struct ieee80211_bss_conf *bss_conf,
3308 u32 changed)
3309{
Eliad Peller0603d892011-10-05 11:55:51 +02003310 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003311 int ret = 0;
3312
3313 if (changed & BSS_CHANGED_ERP_SLOT) {
3314 if (bss_conf->use_short_slot)
Eliad Peller0603d892011-10-05 11:55:51 +02003315 ret = wl1271_acx_slot(wl, wlvif, SLOT_TIME_SHORT);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003316 else
Eliad Peller0603d892011-10-05 11:55:51 +02003317 ret = wl1271_acx_slot(wl, wlvif, SLOT_TIME_LONG);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003318 if (ret < 0) {
3319 wl1271_warning("Set slot time failed %d", ret);
3320 goto out;
3321 }
3322 }
3323
3324 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
3325 if (bss_conf->use_short_preamble)
Eliad Peller0603d892011-10-05 11:55:51 +02003326 wl1271_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_SHORT);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003327 else
Eliad Peller0603d892011-10-05 11:55:51 +02003328 wl1271_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_LONG);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003329 }
3330
3331 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
3332 if (bss_conf->use_cts_prot)
Eliad Peller0603d892011-10-05 11:55:51 +02003333 ret = wl1271_acx_cts_protect(wl, wlvif,
3334 CTSPROTECT_ENABLE);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003335 else
Eliad Peller0603d892011-10-05 11:55:51 +02003336 ret = wl1271_acx_cts_protect(wl, wlvif,
3337 CTSPROTECT_DISABLE);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003338 if (ret < 0) {
3339 wl1271_warning("Set ctsprotect failed %d", ret);
3340 goto out;
3341 }
3342 }
3343
3344out:
3345 return ret;
3346}
3347
3348static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
3349 struct ieee80211_vif *vif,
3350 struct ieee80211_bss_conf *bss_conf,
3351 u32 changed)
3352{
Eliad Peller87fbcb02011-10-05 11:55:41 +02003353 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Eliad Peller536129c2011-10-05 11:55:45 +02003354 bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003355 int ret = 0;
3356
3357 if ((changed & BSS_CHANGED_BEACON_INT)) {
3358 wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d",
3359 bss_conf->beacon_int);
3360
Eliad Peller6a899792011-10-05 11:55:58 +02003361 wlvif->beacon_int = bss_conf->beacon_int;
Arik Nemtsove78a2872010-10-16 19:07:21 +02003362 }
3363
3364 if ((changed & BSS_CHANGED_BEACON)) {
3365 struct ieee80211_hdr *hdr;
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03003366 u32 min_rate;
Arik Nemtsove78a2872010-10-16 19:07:21 +02003367 int ieoffset = offsetof(struct ieee80211_mgmt,
3368 u.beacon.variable);
3369 struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
3370 u16 tmpl_id;
3371
3372 if (!beacon)
3373 goto out;
3374
3375 wl1271_debug(DEBUG_MASTER, "beacon updated");
3376
Eliad Peller1fe9f162011-10-05 11:55:48 +02003377 ret = wl1271_ssid_set(vif, beacon, ieoffset);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003378 if (ret < 0) {
3379 dev_kfree_skb(beacon);
3380 goto out;
3381 }
Eliad Peller87fbcb02011-10-05 11:55:41 +02003382 min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003383 tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
3384 CMD_TEMPL_BEACON;
3385 ret = wl1271_cmd_template_set(wl, tmpl_id,
3386 beacon->data,
3387 beacon->len, 0,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03003388 min_rate);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003389 if (ret < 0) {
3390 dev_kfree_skb(beacon);
3391 goto out;
3392 }
3393
Eliad Pellerd48055d2011-09-15 12:07:04 +03003394 /* remove TIM ie from probe response */
3395 wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset);
3396
Eliad Peller26b4bf22011-09-15 12:07:05 +03003397 /*
3398 * remove p2p ie from probe response.
3399 * the fw reponds to probe requests that don't include
3400 * the p2p ie. probe requests with p2p ie will be passed,
3401 * and will be responded by the supplicant (the spec
3402 * forbids including the p2p ie when responding to probe
3403 * requests that didn't include it).
3404 */
3405 wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA,
3406 WLAN_OUI_TYPE_WFA_P2P, ieoffset);
3407
Arik Nemtsove78a2872010-10-16 19:07:21 +02003408 hdr = (struct ieee80211_hdr *) beacon->data;
3409 hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
3410 IEEE80211_STYPE_PROBE_RESP);
Arik Nemtsov68eaaf62011-09-03 20:22:03 +03003411 if (is_ap)
Eliad Peller1fe9f162011-10-05 11:55:48 +02003412 ret = wl1271_ap_set_probe_resp_tmpl(wl, vif,
Arik Nemtsov68eaaf62011-09-03 20:22:03 +03003413 beacon->data,
3414 beacon->len,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03003415 min_rate);
Arik Nemtsov68eaaf62011-09-03 20:22:03 +03003416 else
3417 ret = wl1271_cmd_template_set(wl,
3418 CMD_TEMPL_PROBE_RESPONSE,
3419 beacon->data,
3420 beacon->len, 0,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03003421 min_rate);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003422 dev_kfree_skb(beacon);
3423 if (ret < 0)
3424 goto out;
3425 }
3426
3427out:
3428 return ret;
3429}
3430
3431/* AP mode changes */
3432static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003433 struct ieee80211_vif *vif,
3434 struct ieee80211_bss_conf *bss_conf,
3435 u32 changed)
3436{
Eliad Peller87fbcb02011-10-05 11:55:41 +02003437 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003438 int ret = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003439
Arik Nemtsove78a2872010-10-16 19:07:21 +02003440 if ((changed & BSS_CHANGED_BASIC_RATES)) {
3441 u32 rates = bss_conf->basic_rates;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003442
Eliad Peller87fbcb02011-10-05 11:55:41 +02003443 wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates,
Eliad Peller1b92f152011-10-10 10:13:09 +02003444 wlvif->band);
Eliad Pellerd2d66c52011-10-05 11:55:43 +02003445 wlvif->basic_rate = wl1271_tx_min_rate_get(wl,
Eliad Peller87fbcb02011-10-05 11:55:41 +02003446 wlvif->basic_rate_set);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003447
Eliad Peller87fbcb02011-10-05 11:55:41 +02003448 ret = wl1271_init_ap_rates(wl, wlvif);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003449 if (ret < 0) {
Arik Nemtsov70f47422011-04-18 14:15:25 +03003450 wl1271_error("AP rate policy change failed %d", ret);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003451 goto out;
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02003452 }
Arik Nemtsovc45a85b2011-04-18 14:15:26 +03003453
Eliad Peller784f6942011-10-05 11:55:39 +02003454 ret = wl1271_ap_init_templates(wl, vif);
Arik Nemtsovc45a85b2011-04-18 14:15:26 +03003455 if (ret < 0)
3456 goto out;
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02003457 }
3458
Arik Nemtsove78a2872010-10-16 19:07:21 +02003459 ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed);
3460 if (ret < 0)
3461 goto out;
3462
3463 if ((changed & BSS_CHANGED_BEACON_ENABLED)) {
3464 if (bss_conf->enable_beacon) {
Eliad Peller53d40d02011-10-10 10:13:02 +02003465 if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
Eliad Peller87fbcb02011-10-05 11:55:41 +02003466 ret = wl12xx_cmd_role_start_ap(wl, wlvif);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003467 if (ret < 0)
3468 goto out;
3469
Eliad Pellera8ab39a2011-10-05 11:55:54 +02003470 ret = wl1271_ap_init_hwenc(wl, wlvif);
Arik Nemtsov7f179b42010-10-16 21:39:06 +02003471 if (ret < 0)
3472 goto out;
Arik Nemtsovcf420392011-08-14 13:17:37 +03003473
Eliad Peller53d40d02011-10-10 10:13:02 +02003474 set_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags);
Arik Nemtsovcf420392011-08-14 13:17:37 +03003475 wl1271_debug(DEBUG_AP, "started AP");
Arik Nemtsove78a2872010-10-16 19:07:21 +02003476 }
3477 } else {
Eliad Peller53d40d02011-10-10 10:13:02 +02003478 if (test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
Eliad Peller0603d892011-10-05 11:55:51 +02003479 ret = wl12xx_cmd_role_stop_ap(wl, wlvif);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003480 if (ret < 0)
3481 goto out;
3482
Eliad Peller53d40d02011-10-10 10:13:02 +02003483 clear_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003484 wl1271_debug(DEBUG_AP, "stopped AP");
3485 }
3486 }
3487 }
3488
Eliad Peller0603d892011-10-05 11:55:51 +02003489 ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003490 if (ret < 0)
3491 goto out;
Arik Nemtsov0b932ab2011-08-14 13:17:27 +03003492
3493 /* Handle HT information change */
3494 if ((changed & BSS_CHANGED_HT) &&
3495 (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
Eliad Peller0603d892011-10-05 11:55:51 +02003496 ret = wl1271_acx_set_ht_information(wl, wlvif,
Arik Nemtsov0b932ab2011-08-14 13:17:27 +03003497 bss_conf->ht_operation_mode);
3498 if (ret < 0) {
3499 wl1271_warning("Set ht information failed %d", ret);
3500 goto out;
3501 }
3502 }
3503
Arik Nemtsove78a2872010-10-16 19:07:21 +02003504out:
3505 return;
3506}
3507
3508/* STA/IBSS mode changes */
3509static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
3510 struct ieee80211_vif *vif,
3511 struct ieee80211_bss_conf *bss_conf,
3512 u32 changed)
3513{
Eliad Peller87fbcb02011-10-05 11:55:41 +02003514 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003515 bool do_join = false, set_assoc = false;
Eliad Peller536129c2011-10-05 11:55:45 +02003516 bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
Eliad Peller227e81e2011-08-14 13:17:26 +03003517 bool ibss_joined = false;
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003518 u32 sta_rate_set = 0;
Arik Nemtsove78a2872010-10-16 19:07:21 +02003519 int ret;
Luciano Coelho2d6e4e762011-01-11 19:07:21 +01003520 struct ieee80211_sta *sta;
Arik Nemtsova1008852011-02-12 23:24:20 +02003521 bool sta_exists = false;
3522 struct ieee80211_sta_ht_cap sta_ht_cap;
Arik Nemtsove78a2872010-10-16 19:07:21 +02003523
3524 if (is_ibss) {
3525 ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf,
3526 changed);
3527 if (ret < 0)
3528 goto out;
3529 }
3530
Eliad Peller227e81e2011-08-14 13:17:26 +03003531 if (changed & BSS_CHANGED_IBSS) {
3532 if (bss_conf->ibss_joined) {
Eliad Pellereee514e2011-10-10 10:13:01 +02003533 set_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags);
Eliad Peller227e81e2011-08-14 13:17:26 +03003534 ibss_joined = true;
3535 } else {
Eliad Pellereee514e2011-10-10 10:13:01 +02003536 if (test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED,
3537 &wlvif->flags)) {
Eliad Peller0603d892011-10-05 11:55:51 +02003538 wl1271_unjoin(wl, wlvif);
Eliad Peller7edebf52011-10-05 11:55:52 +02003539 wl12xx_cmd_role_start_dev(wl, wlvif);
Eliad Peller1b92f152011-10-10 10:13:09 +02003540 wl12xx_roc(wl, wlvif, wlvif->dev_role_id);
Eliad Peller227e81e2011-08-14 13:17:26 +03003541 }
3542 }
3543 }
3544
3545 if ((changed & BSS_CHANGED_BEACON_INT) && ibss_joined)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003546 do_join = true;
3547
3548 /* Need to update the SSID (for filtering etc) */
Eliad Peller227e81e2011-08-14 13:17:26 +03003549 if ((changed & BSS_CHANGED_BEACON) && ibss_joined)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003550 do_join = true;
3551
Eliad Peller227e81e2011-08-14 13:17:26 +03003552 if ((changed & BSS_CHANGED_BEACON_ENABLED) && ibss_joined) {
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02003553 wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
3554 bss_conf->enable_beacon ? "enabled" : "disabled");
3555
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02003556 do_join = true;
3557 }
3558
Arik Nemtsove78a2872010-10-16 19:07:21 +02003559 if ((changed & BSS_CHANGED_CQM)) {
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03003560 bool enable = false;
3561 if (bss_conf->cqm_rssi_thold)
3562 enable = true;
Eliad Peller0603d892011-10-05 11:55:51 +02003563 ret = wl1271_acx_rssi_snr_trigger(wl, wlvif, enable,
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03003564 bss_conf->cqm_rssi_thold,
3565 bss_conf->cqm_rssi_hyst);
3566 if (ret < 0)
3567 goto out;
Eliad Peller04324d92011-10-05 11:56:03 +02003568 wlvif->rssi_thold = bss_conf->cqm_rssi_thold;
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03003569 }
3570
Eliad Pellercdf09492011-10-05 11:55:44 +02003571 if (changed & BSS_CHANGED_BSSID)
3572 if (!is_zero_ether_addr(bss_conf->bssid)) {
Eliad Pellerd2d66c52011-10-05 11:55:43 +02003573 ret = wl12xx_cmd_build_null_data(wl, wlvif);
Eliad Pellerfa287b82010-12-26 09:27:50 +01003574 if (ret < 0)
3575 goto out;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02003576
Eliad Peller784f6942011-10-05 11:55:39 +02003577 ret = wl1271_build_qos_null_data(wl, vif);
Eliad Pellerfa287b82010-12-26 09:27:50 +01003578 if (ret < 0)
3579 goto out;
Saravanan Dhanabal141418c2010-04-28 09:50:00 +03003580
Eliad Pellerfa287b82010-12-26 09:27:50 +01003581 /* Need to update the BSSID (for filtering etc) */
3582 do_join = true;
3583 }
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02003584
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03003585 if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) {
3586 rcu_read_lock();
3587 sta = ieee80211_find_sta(vif, bss_conf->bssid);
3588 if (!sta)
3589 goto sta_not_found;
3590
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003591 /* save the supp_rates of the ap */
3592 sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band];
3593 if (sta->ht_cap.ht_supported)
3594 sta_rate_set |=
3595 (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET);
Arik Nemtsova1008852011-02-12 23:24:20 +02003596 sta_ht_cap = sta->ht_cap;
3597 sta_exists = true;
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003598
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03003599sta_not_found:
3600 rcu_read_unlock();
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003601 }
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003602
Arik Nemtsove78a2872010-10-16 19:07:21 +02003603 if ((changed & BSS_CHANGED_ASSOC)) {
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003604 if (bss_conf->assoc) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003605 u32 rates;
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003606 int ieoffset;
Eliad Peller6840e372011-10-05 11:55:50 +02003607 wlvif->aid = bss_conf->aid;
Juuso Oikarinen69e54342010-05-07 11:39:00 +03003608 set_assoc = true;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003609
Eliad Peller74ec8392011-10-05 11:56:02 +02003610 wlvif->ps_poll_failures = 0;
Juuso Oikarinen90494a92010-07-08 17:50:00 +03003611
Luciano Coelhoae751ba2009-10-12 15:08:57 +03003612 /*
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003613 * use basic rates from AP, and determine lowest rate
3614 * to use with control frames.
3615 */
3616 rates = bss_conf->basic_rates;
Eliad Peller87fbcb02011-10-05 11:55:41 +02003617 wlvif->basic_rate_set =
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03003618 wl1271_tx_enabled_rates_get(wl, rates,
Eliad Peller1b92f152011-10-10 10:13:09 +02003619 wlvif->band);
Eliad Pellerd2d66c52011-10-05 11:55:43 +02003620 wlvif->basic_rate =
Eliad Peller87fbcb02011-10-05 11:55:41 +02003621 wl1271_tx_min_rate_get(wl,
3622 wlvif->basic_rate_set);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003623 if (sta_rate_set)
Eliad Peller30d0c8f2011-10-05 11:55:42 +02003624 wlvif->rate_set =
3625 wl1271_tx_enabled_rates_get(wl,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03003626 sta_rate_set,
Eliad Peller1b92f152011-10-10 10:13:09 +02003627 wlvif->band);
Eliad Peller30d0c8f2011-10-05 11:55:42 +02003628 ret = wl1271_acx_sta_rate_policies(wl, wlvif);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003629 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003630 goto out;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003631
3632 /*
Luciano Coelhoae751ba2009-10-12 15:08:57 +03003633 * with wl1271, we don't need to update the
3634 * beacon_int and dtim_period, because the firmware
3635 * updates it by itself when the first beacon is
3636 * received after a join.
3637 */
Eliad Peller6840e372011-10-05 11:55:50 +02003638 ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003639 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003640 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003641
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02003642 /*
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003643 * Get a template for hardware connection maintenance
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02003644 */
Eliad Pellerbddb29b2011-10-05 11:55:49 +02003645 dev_kfree_skb(wlvif->probereq);
3646 wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
Eliad Peller83587502011-10-10 10:12:53 +02003647 wlvif,
Eliad Pellerbddb29b2011-10-05 11:55:49 +02003648 NULL);
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003649 ieoffset = offsetof(struct ieee80211_mgmt,
3650 u.probe_req.variable);
Eliad Pellerbddb29b2011-10-05 11:55:49 +02003651 wl1271_ssid_set(vif, wlvif->probereq, ieoffset);
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02003652
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02003653 /* enable the connection monitoring feature */
Eliad Peller0603d892011-10-05 11:55:51 +02003654 ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003655 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003656 goto out;
Juuso Oikarinend94cd292009-10-08 21:56:25 +03003657 } else {
3658 /* use defaults when not associated */
Eliad Peller30df14d2011-04-05 19:13:28 +03003659 bool was_assoc =
Eliad Pellerba8447f2011-10-10 10:13:00 +02003660 !!test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED,
3661 &wlvif->flags);
Eliad Peller251c1772011-08-14 13:17:17 +03003662 bool was_ifup =
Eliad Peller8181aec2011-10-10 10:13:04 +02003663 !!test_and_clear_bit(WLVIF_FLAG_STA_STATE_SENT,
3664 &wlvif->flags);
Eliad Peller6840e372011-10-05 11:55:50 +02003665 wlvif->aid = 0;
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02003666
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003667 /* free probe-request template */
Eliad Pellerbddb29b2011-10-05 11:55:49 +02003668 dev_kfree_skb(wlvif->probereq);
3669 wlvif->probereq = NULL;
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003670
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03003671 /* re-enable dynamic ps - just in case */
Juuso Oikarinenf532be62010-07-08 17:50:05 +03003672 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03003673
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003674 /* revert back to minimum rates for the current band */
Eliad Peller87fbcb02011-10-05 11:55:41 +02003675 wl1271_set_band_rate(wl, wlvif);
Eliad Pellerd2d66c52011-10-05 11:55:43 +02003676 wlvif->basic_rate =
Eliad Peller87fbcb02011-10-05 11:55:41 +02003677 wl1271_tx_min_rate_get(wl,
3678 wlvif->basic_rate_set);
Eliad Peller30d0c8f2011-10-05 11:55:42 +02003679 ret = wl1271_acx_sta_rate_policies(wl, wlvif);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003680 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003681 goto out;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003682
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02003683 /* disable connection monitor features */
Eliad Peller0603d892011-10-05 11:55:51 +02003684 ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
Juuso Oikarinenc1899552010-03-26 12:53:32 +02003685
3686 /* Disable the keep-alive feature */
Eliad Peller0603d892011-10-05 11:55:51 +02003687 ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02003688 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003689 goto out;
Juuso Oikarinenb84a7d32010-11-22 12:59:08 +02003690
3691 /* restore the bssid filter and go to dummy bssid */
Eliad Peller30df14d2011-04-05 19:13:28 +03003692 if (was_assoc) {
Eliad Peller251c1772011-08-14 13:17:17 +03003693 u32 conf_flags = wl->hw->conf.flags;
3694 /*
3695 * we might have to disable roc, if there was
3696 * no IF_OPER_UP notification.
3697 */
3698 if (!was_ifup) {
Eliad Peller0603d892011-10-05 11:55:51 +02003699 ret = wl12xx_croc(wl, wlvif->role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03003700 if (ret < 0)
3701 goto out;
3702 }
3703 /*
3704 * (we also need to disable roc in case of
3705 * roaming on the same channel. until we will
3706 * have a better flow...)
3707 */
Eliad Peller7edebf52011-10-05 11:55:52 +02003708 if (test_bit(wlvif->dev_role_id, wl->roc_map)) {
3709 ret = wl12xx_croc(wl,
3710 wlvif->dev_role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03003711 if (ret < 0)
3712 goto out;
3713 }
3714
Eliad Peller0603d892011-10-05 11:55:51 +02003715 wl1271_unjoin(wl, wlvif);
Eliad Peller251c1772011-08-14 13:17:17 +03003716 if (!(conf_flags & IEEE80211_CONF_IDLE)) {
Eliad Peller7edebf52011-10-05 11:55:52 +02003717 wl12xx_cmd_role_start_dev(wl, wlvif);
Eliad Peller1b92f152011-10-10 10:13:09 +02003718 wl12xx_roc(wl, wlvif,
3719 wlvif->dev_role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03003720 }
Eliad Peller30df14d2011-04-05 19:13:28 +03003721 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003722 }
3723 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03003724
Eliad Pellerd192d262011-05-24 14:33:08 +03003725 if (changed & BSS_CHANGED_IBSS) {
3726 wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d",
3727 bss_conf->ibss_joined);
3728
3729 if (bss_conf->ibss_joined) {
3730 u32 rates = bss_conf->basic_rates;
Eliad Peller87fbcb02011-10-05 11:55:41 +02003731 wlvif->basic_rate_set =
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03003732 wl1271_tx_enabled_rates_get(wl, rates,
Eliad Peller1b92f152011-10-10 10:13:09 +02003733 wlvif->band);
Eliad Pellerd2d66c52011-10-05 11:55:43 +02003734 wlvif->basic_rate =
Eliad Peller87fbcb02011-10-05 11:55:41 +02003735 wl1271_tx_min_rate_get(wl,
3736 wlvif->basic_rate_set);
Eliad Pellerd192d262011-05-24 14:33:08 +03003737
Shahar Levi06b660e2011-09-05 13:54:36 +03003738 /* by default, use 11b + OFDM rates */
Eliad Peller30d0c8f2011-10-05 11:55:42 +02003739 wlvif->rate_set = CONF_TX_IBSS_DEFAULT_RATES;
3740 ret = wl1271_acx_sta_rate_policies(wl, wlvif);
Eliad Pellerd192d262011-05-24 14:33:08 +03003741 if (ret < 0)
3742 goto out;
3743 }
3744 }
3745
Eliad Peller0603d892011-10-05 11:55:51 +02003746 ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003747 if (ret < 0)
3748 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003749
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03003750 if (changed & BSS_CHANGED_ARP_FILTER) {
3751 __be32 addr = bss_conf->arp_addr_list[0];
Eliad Peller536129c2011-10-05 11:55:45 +02003752 WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS);
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03003753
Eliad Pellerc5312772010-12-09 11:31:27 +02003754 if (bss_conf->arp_addr_cnt == 1 &&
3755 bss_conf->arp_filter_enabled) {
3756 /*
3757 * The template should have been configured only upon
3758 * association. however, it seems that the correct ip
3759 * isn't being set (when sending), so we have to
3760 * reconfigure the template upon every ip change.
3761 */
Eliad Pellerd2d66c52011-10-05 11:55:43 +02003762 ret = wl1271_cmd_build_arp_rsp(wl, wlvif, addr);
Eliad Pellerc5312772010-12-09 11:31:27 +02003763 if (ret < 0) {
3764 wl1271_warning("build arp rsp failed: %d", ret);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003765 goto out;
Eliad Pellerc5312772010-12-09 11:31:27 +02003766 }
3767
Eliad Peller0603d892011-10-05 11:55:51 +02003768 ret = wl1271_acx_arp_ip_filter(wl, wlvif,
Eliad Pellere5e2f242011-01-24 19:19:03 +01003769 ACX_ARP_FILTER_ARP_FILTERING,
Eliad Pellerc5312772010-12-09 11:31:27 +02003770 addr);
3771 } else
Eliad Peller0603d892011-10-05 11:55:51 +02003772 ret = wl1271_acx_arp_ip_filter(wl, wlvif, 0, addr);
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03003773
3774 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003775 goto out;
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03003776 }
3777
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02003778 if (do_join) {
Eliad Peller87fbcb02011-10-05 11:55:41 +02003779 ret = wl1271_join(wl, wlvif, set_assoc);
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02003780 if (ret < 0) {
3781 wl1271_warning("cmd join failed %d", ret);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003782 goto out;
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02003783 }
Eliad Peller251c1772011-08-14 13:17:17 +03003784
3785 /* ROC until connected (after EAPOL exchange) */
3786 if (!is_ibss) {
Eliad Peller1b92f152011-10-10 10:13:09 +02003787 ret = wl12xx_roc(wl, wlvif, wlvif->role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03003788 if (ret < 0)
3789 goto out;
3790
Eliad Pellerba8447f2011-10-10 10:13:00 +02003791 wl1271_check_operstate(wl, wlvif,
Eliad Peller251c1772011-08-14 13:17:17 +03003792 ieee80211_get_operstate(vif));
3793 }
3794 /*
3795 * stop device role if started (we might already be in
3796 * STA role). TODO: make it better.
3797 */
Eliad Peller7edebf52011-10-05 11:55:52 +02003798 if (wlvif->dev_role_id != WL12XX_INVALID_ROLE_ID) {
3799 ret = wl12xx_croc(wl, wlvif->dev_role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03003800 if (ret < 0)
3801 goto out;
3802
Eliad Peller7edebf52011-10-05 11:55:52 +02003803 ret = wl12xx_cmd_role_stop_dev(wl, wlvif);
Eliad Peller251c1772011-08-14 13:17:17 +03003804 if (ret < 0)
3805 goto out;
3806 }
Eliad Peller05dba352011-08-23 16:37:01 +03003807
3808 /* If we want to go in PSM but we're not there yet */
Eliad Pellerc29bb002011-10-10 10:13:03 +02003809 if (test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags) &&
3810 !test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) {
Eliad Peller05dba352011-08-23 16:37:01 +03003811 enum wl1271_cmd_ps_mode mode;
3812
3813 mode = STATION_POWER_SAVE_MODE;
Eliad Peller0603d892011-10-05 11:55:51 +02003814 ret = wl1271_ps_set_mode(wl, wlvif, mode,
Eliad Pellerd2d66c52011-10-05 11:55:43 +02003815 wlvif->basic_rate,
Eliad Peller05dba352011-08-23 16:37:01 +03003816 true);
3817 if (ret < 0)
3818 goto out;
3819 }
Juuso Oikarinenc1899552010-03-26 12:53:32 +02003820 }
3821
Arik Nemtsov0b932ab2011-08-14 13:17:27 +03003822 /* Handle new association with HT. Do this after join. */
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03003823 if (sta_exists) {
3824 if ((changed & BSS_CHANGED_HT) &&
3825 (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
Arik Nemtsov0b932ab2011-08-14 13:17:27 +03003826 ret = wl1271_acx_set_ht_capabilities(wl,
3827 &sta_ht_cap,
3828 true,
Eliad Peller154da672011-10-05 11:55:53 +02003829 wlvif->sta.hlid);
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03003830 if (ret < 0) {
3831 wl1271_warning("Set ht cap true failed %d",
3832 ret);
3833 goto out;
3834 }
3835 }
3836 /* handle new association without HT and disassociation */
3837 else if (changed & BSS_CHANGED_ASSOC) {
Arik Nemtsov0b932ab2011-08-14 13:17:27 +03003838 ret = wl1271_acx_set_ht_capabilities(wl,
3839 &sta_ht_cap,
3840 false,
Eliad Peller154da672011-10-05 11:55:53 +02003841 wlvif->sta.hlid);
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03003842 if (ret < 0) {
3843 wl1271_warning("Set ht cap false failed %d",
3844 ret);
3845 goto out;
3846 }
3847 }
3848 }
3849
Arik Nemtsov0b932ab2011-08-14 13:17:27 +03003850 /* Handle HT information change. Done after join. */
3851 if ((changed & BSS_CHANGED_HT) &&
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03003852 (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
Eliad Peller0603d892011-10-05 11:55:51 +02003853 ret = wl1271_acx_set_ht_information(wl, wlvif,
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03003854 bss_conf->ht_operation_mode);
3855 if (ret < 0) {
3856 wl1271_warning("Set ht information failed %d", ret);
3857 goto out;
3858 }
3859 }
3860
Arik Nemtsove78a2872010-10-16 19:07:21 +02003861out:
3862 return;
3863}
3864
3865static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
3866 struct ieee80211_vif *vif,
3867 struct ieee80211_bss_conf *bss_conf,
3868 u32 changed)
3869{
3870 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02003871 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
3872 bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003873 int ret;
3874
3875 wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
3876 (int)changed);
3877
3878 mutex_lock(&wl->mutex);
3879
3880 if (unlikely(wl->state == WL1271_STATE_OFF))
3881 goto out;
3882
Eliad Peller10c8cd02011-10-10 10:13:06 +02003883 if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
3884 goto out;
3885
Ido Yariva6208652011-03-01 15:14:41 +02003886 ret = wl1271_ps_elp_wakeup(wl);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003887 if (ret < 0)
3888 goto out;
3889
3890 if (is_ap)
3891 wl1271_bss_info_changed_ap(wl, vif, bss_conf, changed);
3892 else
3893 wl1271_bss_info_changed_sta(wl, vif, bss_conf, changed);
3894
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003895 wl1271_ps_elp_sleep(wl);
3896
3897out:
3898 mutex_unlock(&wl->mutex);
3899}
3900
Eliad Peller8a3a3c82011-10-02 10:15:52 +02003901static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
3902 struct ieee80211_vif *vif, u16 queue,
Kalle Valoc6999d82010-02-18 13:25:41 +02003903 const struct ieee80211_tx_queue_params *params)
3904{
3905 struct wl1271 *wl = hw->priv;
Eliad Peller0603d892011-10-05 11:55:51 +02003906 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Kalle Valo4695dc92010-03-18 12:26:38 +02003907 u8 ps_scheme;
Arik Nemtsov488fc542010-10-16 20:33:45 +02003908 int ret = 0;
Kalle Valoc6999d82010-02-18 13:25:41 +02003909
3910 mutex_lock(&wl->mutex);
3911
3912 wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
3913
Kalle Valo4695dc92010-03-18 12:26:38 +02003914 if (params->uapsd)
3915 ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
3916 else
3917 ps_scheme = CONF_PS_SCHEME_LEGACY;
3918
Arik Nemtsov488fc542010-10-16 20:33:45 +02003919 if (wl->state == WL1271_STATE_OFF) {
3920 /*
3921 * If the state is off, the parameters will be recorded and
3922 * configured on init. This happens in AP-mode.
3923 */
3924 struct conf_tx_ac_category *conf_ac =
3925 &wl->conf.tx.ac_conf[wl1271_tx_get_queue(queue)];
3926 struct conf_tx_tid *conf_tid =
3927 &wl->conf.tx.tid_conf[wl1271_tx_get_queue(queue)];
3928
3929 conf_ac->ac = wl1271_tx_get_queue(queue);
3930 conf_ac->cw_min = (u8)params->cw_min;
3931 conf_ac->cw_max = params->cw_max;
3932 conf_ac->aifsn = params->aifs;
3933 conf_ac->tx_op_limit = params->txop << 5;
3934
3935 conf_tid->queue_id = wl1271_tx_get_queue(queue);
3936 conf_tid->channel_type = CONF_CHANNEL_TYPE_EDCF;
3937 conf_tid->tsid = wl1271_tx_get_queue(queue);
3938 conf_tid->ps_scheme = ps_scheme;
3939 conf_tid->ack_policy = CONF_ACK_POLICY_LEGACY;
3940 conf_tid->apsd_conf[0] = 0;
3941 conf_tid->apsd_conf[1] = 0;
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003942 goto out;
3943 }
Arik Nemtsov488fc542010-10-16 20:33:45 +02003944
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003945 ret = wl1271_ps_elp_wakeup(wl);
3946 if (ret < 0)
3947 goto out;
Arik Nemtsov488fc542010-10-16 20:33:45 +02003948
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003949 /*
3950 * the txop is confed in units of 32us by the mac80211,
3951 * we need us
3952 */
Eliad Peller0603d892011-10-05 11:55:51 +02003953 ret = wl1271_acx_ac_cfg(wl, wlvif, wl1271_tx_get_queue(queue),
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003954 params->cw_min, params->cw_max,
3955 params->aifs, params->txop << 5);
3956 if (ret < 0)
3957 goto out_sleep;
3958
Eliad Peller0603d892011-10-05 11:55:51 +02003959 ret = wl1271_acx_tid_cfg(wl, wlvif, wl1271_tx_get_queue(queue),
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003960 CONF_CHANNEL_TYPE_EDCF,
3961 wl1271_tx_get_queue(queue),
3962 ps_scheme, CONF_ACK_POLICY_LEGACY,
3963 0, 0);
Kalle Valoc82c1dd2010-02-18 13:25:47 +02003964
3965out_sleep:
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003966 wl1271_ps_elp_sleep(wl);
Kalle Valoc6999d82010-02-18 13:25:41 +02003967
3968out:
3969 mutex_unlock(&wl->mutex);
3970
3971 return ret;
3972}
3973
Eliad Peller37a41b42011-09-21 14:06:11 +03003974static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw,
3975 struct ieee80211_vif *vif)
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03003976{
3977
3978 struct wl1271 *wl = hw->priv;
3979 u64 mactime = ULLONG_MAX;
3980 int ret;
3981
3982 wl1271_debug(DEBUG_MAC80211, "mac80211 get tsf");
3983
3984 mutex_lock(&wl->mutex);
3985
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02003986 if (unlikely(wl->state == WL1271_STATE_OFF))
3987 goto out;
3988
Ido Yariva6208652011-03-01 15:14:41 +02003989 ret = wl1271_ps_elp_wakeup(wl);
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03003990 if (ret < 0)
3991 goto out;
3992
3993 ret = wl1271_acx_tsf_info(wl, &mactime);
3994 if (ret < 0)
3995 goto out_sleep;
3996
3997out_sleep:
3998 wl1271_ps_elp_sleep(wl);
3999
4000out:
4001 mutex_unlock(&wl->mutex);
4002 return mactime;
4003}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004004
John W. Linvilleece550d2010-07-28 16:41:06 -04004005static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
4006 struct survey_info *survey)
4007{
4008 struct wl1271 *wl = hw->priv;
4009 struct ieee80211_conf *conf = &hw->conf;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02004010
John W. Linvilleece550d2010-07-28 16:41:06 -04004011 if (idx != 0)
4012 return -ENOENT;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02004013
John W. Linvilleece550d2010-07-28 16:41:06 -04004014 survey->channel = conf->channel;
4015 survey->filled = SURVEY_INFO_NOISE_DBM;
4016 survey->noise = wl->noise;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02004017
John W. Linvilleece550d2010-07-28 16:41:06 -04004018 return 0;
4019}
4020
Arik Nemtsov409622e2011-02-23 00:22:29 +02004021static int wl1271_allocate_sta(struct wl1271 *wl,
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004022 struct wl12xx_vif *wlvif,
4023 struct ieee80211_sta *sta)
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004024{
4025 struct wl1271_station *wl_sta;
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004026 int ret;
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004027
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004028
4029 if (wl->active_sta_count >= AP_MAX_STATIONS) {
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004030 wl1271_warning("could not allocate HLID - too much stations");
4031 return -EBUSY;
4032 }
4033
4034 wl_sta = (struct wl1271_station *)sta->drv_priv;
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004035 ret = wl12xx_allocate_link(wl, wlvif, &wl_sta->hlid);
4036 if (ret < 0) {
4037 wl1271_warning("could not allocate HLID - too many links");
4038 return -EBUSY;
4039 }
4040
4041 set_bit(wl_sta->hlid, wlvif->ap.sta_hlid_map);
Arik Nemtsovb622d992011-02-23 00:22:31 +02004042 memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN);
Arik Nemtsovda032092011-08-25 12:43:15 +03004043 wl->active_sta_count++;
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004044 return 0;
4045}
4046
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004047void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004048{
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004049 if (!test_bit(hlid, wlvif->ap.sta_hlid_map))
Arik Nemtsovf1acea92011-08-25 12:43:17 +03004050 return;
4051
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004052 clear_bit(hlid, wlvif->ap.sta_hlid_map);
Arik Nemtsovb622d992011-02-23 00:22:31 +02004053 memset(wl->links[hlid].addr, 0, ETH_ALEN);
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03004054 wl->links[hlid].ba_bitmap = 0;
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02004055 wl1271_tx_reset_link_queues(wl, hlid);
Arik Nemtsovb622d992011-02-23 00:22:31 +02004056 __clear_bit(hlid, &wl->ap_ps_map);
4057 __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004058 wl12xx_free_link(wl, wlvif, &hlid);
Arik Nemtsovda032092011-08-25 12:43:15 +03004059 wl->active_sta_count--;
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004060}
4061
4062static int wl1271_op_sta_add(struct ieee80211_hw *hw,
4063 struct ieee80211_vif *vif,
4064 struct ieee80211_sta *sta)
4065{
4066 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02004067 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004068 struct wl1271_station *wl_sta;
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004069 int ret = 0;
4070 u8 hlid;
4071
4072 mutex_lock(&wl->mutex);
4073
4074 if (unlikely(wl->state == WL1271_STATE_OFF))
4075 goto out;
4076
Eliad Peller536129c2011-10-05 11:55:45 +02004077 if (wlvif->bss_type != BSS_TYPE_AP_BSS)
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004078 goto out;
4079
4080 wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);
4081
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004082 ret = wl1271_allocate_sta(wl, wlvif, sta);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004083 if (ret < 0)
4084 goto out;
4085
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004086 wl_sta = (struct wl1271_station *)sta->drv_priv;
4087 hlid = wl_sta->hlid;
4088
Ido Yariva6208652011-03-01 15:14:41 +02004089 ret = wl1271_ps_elp_wakeup(wl);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004090 if (ret < 0)
Arik Nemtsov409622e2011-02-23 00:22:29 +02004091 goto out_free_sta;
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004092
Eliad Peller1b92f152011-10-10 10:13:09 +02004093 ret = wl12xx_cmd_add_peer(wl, wlvif, sta, hlid);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004094 if (ret < 0)
4095 goto out_sleep;
4096
Eliad Pellerb67476e2011-08-14 13:17:23 +03004097 ret = wl12xx_cmd_set_peer_state(wl, hlid);
4098 if (ret < 0)
4099 goto out_sleep;
4100
Arik Nemtsov0b932ab2011-08-14 13:17:27 +03004101 ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true, hlid);
4102 if (ret < 0)
4103 goto out_sleep;
4104
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004105out_sleep:
4106 wl1271_ps_elp_sleep(wl);
4107
Arik Nemtsov409622e2011-02-23 00:22:29 +02004108out_free_sta:
4109 if (ret < 0)
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004110 wl1271_free_sta(wl, wlvif, hlid);
Arik Nemtsov409622e2011-02-23 00:22:29 +02004111
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004112out:
4113 mutex_unlock(&wl->mutex);
4114 return ret;
4115}
4116
4117static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
4118 struct ieee80211_vif *vif,
4119 struct ieee80211_sta *sta)
4120{
4121 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02004122 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004123 struct wl1271_station *wl_sta;
4124 int ret = 0, id;
4125
4126 mutex_lock(&wl->mutex);
4127
4128 if (unlikely(wl->state == WL1271_STATE_OFF))
4129 goto out;
4130
Eliad Peller536129c2011-10-05 11:55:45 +02004131 if (wlvif->bss_type != BSS_TYPE_AP_BSS)
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004132 goto out;
4133
4134 wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
4135
4136 wl_sta = (struct wl1271_station *)sta->drv_priv;
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004137 id = wl_sta->hlid;
4138 if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map)))
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004139 goto out;
4140
Ido Yariva6208652011-03-01 15:14:41 +02004141 ret = wl1271_ps_elp_wakeup(wl);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004142 if (ret < 0)
4143 goto out;
4144
Eliad Pellerc690ec82011-08-14 13:17:07 +03004145 ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004146 if (ret < 0)
4147 goto out_sleep;
4148
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004149 wl1271_free_sta(wl, wlvif, wl_sta->hlid);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004150
4151out_sleep:
4152 wl1271_ps_elp_sleep(wl);
4153
4154out:
4155 mutex_unlock(&wl->mutex);
4156 return ret;
4157}
4158
Luciano Coelho4623ec72011-03-21 19:26:41 +02004159static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
4160 struct ieee80211_vif *vif,
4161 enum ieee80211_ampdu_mlme_action action,
4162 struct ieee80211_sta *sta, u16 tid, u16 *ssn,
4163 u8 buf_size)
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004164{
4165 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02004166 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004167 int ret;
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03004168 u8 hlid, *ba_bitmap;
4169
4170 wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu action %d tid %d", action,
4171 tid);
4172
4173 /* sanity check - the fields in FW are only 8bits wide */
4174 if (WARN_ON(tid > 0xFF))
4175 return -ENOTSUPP;
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004176
4177 mutex_lock(&wl->mutex);
4178
4179 if (unlikely(wl->state == WL1271_STATE_OFF)) {
4180 ret = -EAGAIN;
4181 goto out;
4182 }
4183
Eliad Peller536129c2011-10-05 11:55:45 +02004184 if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
Eliad Peller154da672011-10-05 11:55:53 +02004185 hlid = wlvif->sta.hlid;
Eliad Pellerd0802ab2011-10-05 11:56:04 +02004186 ba_bitmap = &wlvif->sta.ba_rx_bitmap;
Eliad Peller536129c2011-10-05 11:55:45 +02004187 } else if (wlvif->bss_type == BSS_TYPE_AP_BSS) {
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03004188 struct wl1271_station *wl_sta;
4189
4190 wl_sta = (struct wl1271_station *)sta->drv_priv;
4191 hlid = wl_sta->hlid;
4192 ba_bitmap = &wl->links[hlid].ba_bitmap;
4193 } else {
4194 ret = -EINVAL;
4195 goto out;
4196 }
4197
Ido Yariva6208652011-03-01 15:14:41 +02004198 ret = wl1271_ps_elp_wakeup(wl);
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004199 if (ret < 0)
4200 goto out;
4201
Shahar Levi70559a02011-05-22 16:10:22 +03004202 wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu: Rx tid %d action %d",
4203 tid, action);
4204
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004205 switch (action) {
4206 case IEEE80211_AMPDU_RX_START:
Eliad Pellerd0802ab2011-10-05 11:56:04 +02004207 if (!wlvif->ba_support || !wlvif->ba_allowed) {
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004208 ret = -ENOTSUPP;
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03004209 break;
4210 }
4211
4212 if (wl->ba_rx_session_count >= RX_BA_MAX_SESSIONS) {
4213 ret = -EBUSY;
4214 wl1271_error("exceeded max RX BA sessions");
4215 break;
4216 }
4217
4218 if (*ba_bitmap & BIT(tid)) {
4219 ret = -EINVAL;
4220 wl1271_error("cannot enable RX BA session on active "
4221 "tid: %d", tid);
4222 break;
4223 }
4224
4225 ret = wl12xx_acx_set_ba_receiver_session(wl, tid, *ssn, true,
4226 hlid);
4227 if (!ret) {
4228 *ba_bitmap |= BIT(tid);
4229 wl->ba_rx_session_count++;
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004230 }
4231 break;
4232
4233 case IEEE80211_AMPDU_RX_STOP:
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03004234 if (!(*ba_bitmap & BIT(tid))) {
4235 ret = -EINVAL;
4236 wl1271_error("no active RX BA session on tid: %d",
4237 tid);
4238 break;
4239 }
4240
4241 ret = wl12xx_acx_set_ba_receiver_session(wl, tid, 0, false,
4242 hlid);
4243 if (!ret) {
4244 *ba_bitmap &= ~BIT(tid);
4245 wl->ba_rx_session_count--;
4246 }
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004247 break;
4248
4249 /*
4250 * The BA initiator session management in FW independently.
4251 * Falling break here on purpose for all TX APDU commands.
4252 */
4253 case IEEE80211_AMPDU_TX_START:
4254 case IEEE80211_AMPDU_TX_STOP:
4255 case IEEE80211_AMPDU_TX_OPERATIONAL:
4256 ret = -EINVAL;
4257 break;
4258
4259 default:
4260 wl1271_error("Incorrect ampdu action id=%x\n", action);
4261 ret = -EINVAL;
4262 }
4263
4264 wl1271_ps_elp_sleep(wl);
4265
4266out:
4267 mutex_unlock(&wl->mutex);
4268
4269 return ret;
4270}
4271
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03004272static int wl12xx_set_bitrate_mask(struct ieee80211_hw *hw,
4273 struct ieee80211_vif *vif,
4274 const struct cfg80211_bitrate_mask *mask)
4275{
Eliad Peller83587502011-10-10 10:12:53 +02004276 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03004277 struct wl1271 *wl = hw->priv;
4278 int i;
4279
4280 wl1271_debug(DEBUG_MAC80211, "mac80211 set_bitrate_mask 0x%x 0x%x",
4281 mask->control[NL80211_BAND_2GHZ].legacy,
4282 mask->control[NL80211_BAND_5GHZ].legacy);
4283
4284 mutex_lock(&wl->mutex);
4285
4286 for (i = 0; i < IEEE80211_NUM_BANDS; i++)
Eliad Peller83587502011-10-10 10:12:53 +02004287 wlvif->bitrate_masks[i] =
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03004288 wl1271_tx_enabled_rates_get(wl,
4289 mask->control[i].legacy,
4290 i);
4291 mutex_unlock(&wl->mutex);
4292
4293 return 0;
4294}
4295
Shahar Levi6d158ff2011-09-08 13:01:33 +03004296static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
4297 struct ieee80211_channel_switch *ch_switch)
4298{
4299 struct wl1271 *wl = hw->priv;
Eliad Peller52630c52011-10-10 10:13:08 +02004300 struct wl12xx_vif *wlvif;
Shahar Levi6d158ff2011-09-08 13:01:33 +03004301 int ret;
4302
4303 wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch");
4304
4305 mutex_lock(&wl->mutex);
4306
4307 if (unlikely(wl->state == WL1271_STATE_OFF)) {
4308 mutex_unlock(&wl->mutex);
4309 ieee80211_chswitch_done(wl->vif, false);
4310 return;
4311 }
4312
4313 ret = wl1271_ps_elp_wakeup(wl);
4314 if (ret < 0)
4315 goto out;
4316
Eliad Peller52630c52011-10-10 10:13:08 +02004317 /* TODO: change mac80211 to pass vif as param */
4318 wl12xx_for_each_wlvif_sta(wl, wlvif) {
4319 ret = wl12xx_cmd_channel_switch(wl, ch_switch);
Shahar Levi6d158ff2011-09-08 13:01:33 +03004320
Eliad Peller52630c52011-10-10 10:13:08 +02004321 if (!ret)
4322 set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
4323 }
Shahar Levi6d158ff2011-09-08 13:01:33 +03004324
4325 wl1271_ps_elp_sleep(wl);
4326
4327out:
4328 mutex_unlock(&wl->mutex);
4329}
4330
Arik Nemtsov33437892011-04-26 23:35:39 +03004331static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
4332{
4333 struct wl1271 *wl = hw->priv;
4334 bool ret = false;
4335
4336 mutex_lock(&wl->mutex);
4337
4338 if (unlikely(wl->state == WL1271_STATE_OFF))
4339 goto out;
4340
4341 /* packets are considered pending if in the TX queue or the FW */
Arik Nemtsovf1a46382011-07-07 14:25:23 +03004342 ret = (wl1271_tx_total_queue_count(wl) > 0) || (wl->tx_frames_cnt > 0);
Arik Nemtsov33437892011-04-26 23:35:39 +03004343out:
4344 mutex_unlock(&wl->mutex);
4345
4346 return ret;
4347}
4348
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004349/* can't be const, mac80211 writes to this */
4350static struct ieee80211_rate wl1271_rates[] = {
4351 { .bitrate = 10,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004352 .hw_value = CONF_HW_BIT_RATE_1MBPS,
4353 .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004354 { .bitrate = 20,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004355 .hw_value = CONF_HW_BIT_RATE_2MBPS,
4356 .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004357 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
4358 { .bitrate = 55,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004359 .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
4360 .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004361 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
4362 { .bitrate = 110,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004363 .hw_value = CONF_HW_BIT_RATE_11MBPS,
4364 .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004365 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
4366 { .bitrate = 60,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004367 .hw_value = CONF_HW_BIT_RATE_6MBPS,
4368 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004369 { .bitrate = 90,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004370 .hw_value = CONF_HW_BIT_RATE_9MBPS,
4371 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004372 { .bitrate = 120,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004373 .hw_value = CONF_HW_BIT_RATE_12MBPS,
4374 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004375 { .bitrate = 180,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004376 .hw_value = CONF_HW_BIT_RATE_18MBPS,
4377 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004378 { .bitrate = 240,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004379 .hw_value = CONF_HW_BIT_RATE_24MBPS,
4380 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004381 { .bitrate = 360,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004382 .hw_value = CONF_HW_BIT_RATE_36MBPS,
4383 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004384 { .bitrate = 480,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004385 .hw_value = CONF_HW_BIT_RATE_48MBPS,
4386 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004387 { .bitrate = 540,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004388 .hw_value = CONF_HW_BIT_RATE_54MBPS,
4389 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004390};
4391
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01004392/* can't be const, mac80211 writes to this */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004393static struct ieee80211_channel wl1271_channels[] = {
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02004394 { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02004395 { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01004396 { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
4397 { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
4398 { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02004399 { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01004400 { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
4401 { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
4402 { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02004403 { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01004404 { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
4405 { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
4406 { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
Arik Nemtsov6c89b7b2011-01-18 20:39:52 +01004407 { .hw_value = 14, .center_freq = 2484, .max_power = 25 },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004408};
4409
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004410/* mapping to indexes for wl1271_rates */
Tobias Klausera0ea9492010-05-20 10:38:11 +02004411static const u8 wl1271_rate_to_idx_2ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004412 /* MCS rates are used only with 11n */
Shahar Levi18357852010-10-13 16:09:41 +02004413 7, /* CONF_HW_RXTX_RATE_MCS7 */
4414 6, /* CONF_HW_RXTX_RATE_MCS6 */
4415 5, /* CONF_HW_RXTX_RATE_MCS5 */
4416 4, /* CONF_HW_RXTX_RATE_MCS4 */
4417 3, /* CONF_HW_RXTX_RATE_MCS3 */
4418 2, /* CONF_HW_RXTX_RATE_MCS2 */
4419 1, /* CONF_HW_RXTX_RATE_MCS1 */
4420 0, /* CONF_HW_RXTX_RATE_MCS0 */
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004421
4422 11, /* CONF_HW_RXTX_RATE_54 */
4423 10, /* CONF_HW_RXTX_RATE_48 */
4424 9, /* CONF_HW_RXTX_RATE_36 */
4425 8, /* CONF_HW_RXTX_RATE_24 */
4426
4427 /* TI-specific rate */
4428 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
4429
4430 7, /* CONF_HW_RXTX_RATE_18 */
4431 6, /* CONF_HW_RXTX_RATE_12 */
4432 3, /* CONF_HW_RXTX_RATE_11 */
4433 5, /* CONF_HW_RXTX_RATE_9 */
4434 4, /* CONF_HW_RXTX_RATE_6 */
4435 2, /* CONF_HW_RXTX_RATE_5_5 */
4436 1, /* CONF_HW_RXTX_RATE_2 */
4437 0 /* CONF_HW_RXTX_RATE_1 */
4438};
4439
Shahar Levie8b03a22010-10-13 16:09:39 +02004440/* 11n STA capabilities */
4441#define HW_RX_HIGHEST_RATE 72
4442
Shahar Levi00d20102010-11-08 11:20:10 +00004443#define WL12XX_HT_CAP { \
Shahar Levi871d0c32011-03-13 11:24:40 +02004444 .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | \
4445 (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), \
Shahar Levie8b03a22010-10-13 16:09:39 +02004446 .ht_supported = true, \
4447 .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, \
4448 .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \
4449 .mcs = { \
4450 .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \
4451 .rx_highest = cpu_to_le16(HW_RX_HIGHEST_RATE), \
4452 .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \
4453 }, \
4454}
4455
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004456/* can't be const, mac80211 writes to this */
4457static struct ieee80211_supported_band wl1271_band_2ghz = {
4458 .channels = wl1271_channels,
4459 .n_channels = ARRAY_SIZE(wl1271_channels),
4460 .bitrates = wl1271_rates,
4461 .n_bitrates = ARRAY_SIZE(wl1271_rates),
Shahar Levi00d20102010-11-08 11:20:10 +00004462 .ht_cap = WL12XX_HT_CAP,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004463};
4464
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03004465/* 5 GHz data rates for WL1273 */
4466static struct ieee80211_rate wl1271_rates_5ghz[] = {
4467 { .bitrate = 60,
4468 .hw_value = CONF_HW_BIT_RATE_6MBPS,
4469 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
4470 { .bitrate = 90,
4471 .hw_value = CONF_HW_BIT_RATE_9MBPS,
4472 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
4473 { .bitrate = 120,
4474 .hw_value = CONF_HW_BIT_RATE_12MBPS,
4475 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
4476 { .bitrate = 180,
4477 .hw_value = CONF_HW_BIT_RATE_18MBPS,
4478 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
4479 { .bitrate = 240,
4480 .hw_value = CONF_HW_BIT_RATE_24MBPS,
4481 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
4482 { .bitrate = 360,
4483 .hw_value = CONF_HW_BIT_RATE_36MBPS,
4484 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
4485 { .bitrate = 480,
4486 .hw_value = CONF_HW_BIT_RATE_48MBPS,
4487 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
4488 { .bitrate = 540,
4489 .hw_value = CONF_HW_BIT_RATE_54MBPS,
4490 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
4491};
4492
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01004493/* 5 GHz band channels for WL1273 */
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03004494static struct ieee80211_channel wl1271_channels_5ghz[] = {
Arik Nemtsov6cfa5cf2011-06-27 22:06:33 +03004495 { .hw_value = 7, .center_freq = 5035, .max_power = 25 },
4496 { .hw_value = 8, .center_freq = 5040, .max_power = 25 },
4497 { .hw_value = 9, .center_freq = 5045, .max_power = 25 },
4498 { .hw_value = 11, .center_freq = 5055, .max_power = 25 },
4499 { .hw_value = 12, .center_freq = 5060, .max_power = 25 },
4500 { .hw_value = 16, .center_freq = 5080, .max_power = 25 },
4501 { .hw_value = 34, .center_freq = 5170, .max_power = 25 },
4502 { .hw_value = 36, .center_freq = 5180, .max_power = 25 },
4503 { .hw_value = 38, .center_freq = 5190, .max_power = 25 },
4504 { .hw_value = 40, .center_freq = 5200, .max_power = 25 },
4505 { .hw_value = 42, .center_freq = 5210, .max_power = 25 },
4506 { .hw_value = 44, .center_freq = 5220, .max_power = 25 },
4507 { .hw_value = 46, .center_freq = 5230, .max_power = 25 },
4508 { .hw_value = 48, .center_freq = 5240, .max_power = 25 },
4509 { .hw_value = 52, .center_freq = 5260, .max_power = 25 },
4510 { .hw_value = 56, .center_freq = 5280, .max_power = 25 },
4511 { .hw_value = 60, .center_freq = 5300, .max_power = 25 },
4512 { .hw_value = 64, .center_freq = 5320, .max_power = 25 },
4513 { .hw_value = 100, .center_freq = 5500, .max_power = 25 },
4514 { .hw_value = 104, .center_freq = 5520, .max_power = 25 },
4515 { .hw_value = 108, .center_freq = 5540, .max_power = 25 },
4516 { .hw_value = 112, .center_freq = 5560, .max_power = 25 },
4517 { .hw_value = 116, .center_freq = 5580, .max_power = 25 },
4518 { .hw_value = 120, .center_freq = 5600, .max_power = 25 },
4519 { .hw_value = 124, .center_freq = 5620, .max_power = 25 },
4520 { .hw_value = 128, .center_freq = 5640, .max_power = 25 },
4521 { .hw_value = 132, .center_freq = 5660, .max_power = 25 },
4522 { .hw_value = 136, .center_freq = 5680, .max_power = 25 },
4523 { .hw_value = 140, .center_freq = 5700, .max_power = 25 },
4524 { .hw_value = 149, .center_freq = 5745, .max_power = 25 },
4525 { .hw_value = 153, .center_freq = 5765, .max_power = 25 },
4526 { .hw_value = 157, .center_freq = 5785, .max_power = 25 },
4527 { .hw_value = 161, .center_freq = 5805, .max_power = 25 },
4528 { .hw_value = 165, .center_freq = 5825, .max_power = 25 },
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03004529};
4530
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004531/* mapping to indexes for wl1271_rates_5ghz */
Tobias Klausera0ea9492010-05-20 10:38:11 +02004532static const u8 wl1271_rate_to_idx_5ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004533 /* MCS rates are used only with 11n */
Shahar Levi18357852010-10-13 16:09:41 +02004534 7, /* CONF_HW_RXTX_RATE_MCS7 */
4535 6, /* CONF_HW_RXTX_RATE_MCS6 */
4536 5, /* CONF_HW_RXTX_RATE_MCS5 */
4537 4, /* CONF_HW_RXTX_RATE_MCS4 */
4538 3, /* CONF_HW_RXTX_RATE_MCS3 */
4539 2, /* CONF_HW_RXTX_RATE_MCS2 */
4540 1, /* CONF_HW_RXTX_RATE_MCS1 */
4541 0, /* CONF_HW_RXTX_RATE_MCS0 */
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004542
4543 7, /* CONF_HW_RXTX_RATE_54 */
4544 6, /* CONF_HW_RXTX_RATE_48 */
4545 5, /* CONF_HW_RXTX_RATE_36 */
4546 4, /* CONF_HW_RXTX_RATE_24 */
4547
4548 /* TI-specific rate */
4549 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
4550
4551 3, /* CONF_HW_RXTX_RATE_18 */
4552 2, /* CONF_HW_RXTX_RATE_12 */
4553 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_11 */
4554 1, /* CONF_HW_RXTX_RATE_9 */
4555 0, /* CONF_HW_RXTX_RATE_6 */
4556 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_5_5 */
4557 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_2 */
4558 CONF_HW_RXTX_RATE_UNSUPPORTED /* CONF_HW_RXTX_RATE_1 */
4559};
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03004560
4561static struct ieee80211_supported_band wl1271_band_5ghz = {
4562 .channels = wl1271_channels_5ghz,
4563 .n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
4564 .bitrates = wl1271_rates_5ghz,
4565 .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
Shahar Levi00d20102010-11-08 11:20:10 +00004566 .ht_cap = WL12XX_HT_CAP,
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03004567};
4568
Tobias Klausera0ea9492010-05-20 10:38:11 +02004569static const u8 *wl1271_band_rate_to_idx[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004570 [IEEE80211_BAND_2GHZ] = wl1271_rate_to_idx_2ghz,
4571 [IEEE80211_BAND_5GHZ] = wl1271_rate_to_idx_5ghz
4572};
4573
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004574static const struct ieee80211_ops wl1271_ops = {
4575 .start = wl1271_op_start,
4576 .stop = wl1271_op_stop,
4577 .add_interface = wl1271_op_add_interface,
4578 .remove_interface = wl1271_op_remove_interface,
Luciano Coelhof634a4e2011-05-18 16:51:26 -04004579#ifdef CONFIG_PM
Eliad Peller402e48612011-05-13 11:57:09 +03004580 .suspend = wl1271_op_suspend,
4581 .resume = wl1271_op_resume,
Luciano Coelhof634a4e2011-05-18 16:51:26 -04004582#endif
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004583 .config = wl1271_op_config,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03004584 .prepare_multicast = wl1271_op_prepare_multicast,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004585 .configure_filter = wl1271_op_configure_filter,
4586 .tx = wl1271_op_tx,
4587 .set_key = wl1271_op_set_key,
4588 .hw_scan = wl1271_op_hw_scan,
Eliad Peller73ecce32011-06-27 13:06:45 +03004589 .cancel_hw_scan = wl1271_op_cancel_hw_scan,
Luciano Coelho33c2c062011-05-10 14:46:02 +03004590 .sched_scan_start = wl1271_op_sched_scan_start,
4591 .sched_scan_stop = wl1271_op_sched_scan_stop,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004592 .bss_info_changed = wl1271_op_bss_info_changed,
Arik Nemtsov68d069c2010-11-08 10:51:07 +01004593 .set_frag_threshold = wl1271_op_set_frag_threshold,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004594 .set_rts_threshold = wl1271_op_set_rts_threshold,
Kalle Valoc6999d82010-02-18 13:25:41 +02004595 .conf_tx = wl1271_op_conf_tx,
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03004596 .get_tsf = wl1271_op_get_tsf,
John W. Linvilleece550d2010-07-28 16:41:06 -04004597 .get_survey = wl1271_op_get_survey,
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004598 .sta_add = wl1271_op_sta_add,
4599 .sta_remove = wl1271_op_sta_remove,
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004600 .ampdu_action = wl1271_op_ampdu_action,
Arik Nemtsov33437892011-04-26 23:35:39 +03004601 .tx_frames_pending = wl1271_tx_frames_pending,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03004602 .set_bitrate_mask = wl12xx_set_bitrate_mask,
Shahar Levi6d158ff2011-09-08 13:01:33 +03004603 .channel_switch = wl12xx_op_channel_switch,
Kalle Valoc8c90872010-02-18 13:25:53 +02004604 CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004605};
4606
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004607
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02004608u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band)
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004609{
4610 u8 idx;
4611
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02004612 BUG_ON(band >= sizeof(wl1271_band_rate_to_idx)/sizeof(u8 *));
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004613
4614 if (unlikely(rate >= CONF_HW_RXTX_RATE_MAX)) {
4615 wl1271_error("Illegal RX rate from HW: %d", rate);
4616 return 0;
4617 }
4618
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02004619 idx = wl1271_band_rate_to_idx[band][rate];
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004620 if (unlikely(idx == CONF_HW_RXTX_RATE_UNSUPPORTED)) {
4621 wl1271_error("Unsupported RX rate from HW: %d", rate);
4622 return 0;
4623 }
4624
4625 return idx;
4626}
4627
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02004628static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
4629 struct device_attribute *attr,
4630 char *buf)
4631{
4632 struct wl1271 *wl = dev_get_drvdata(dev);
4633 ssize_t len;
4634
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02004635 len = PAGE_SIZE;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02004636
4637 mutex_lock(&wl->mutex);
4638 len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
4639 wl->sg_enabled);
4640 mutex_unlock(&wl->mutex);
4641
4642 return len;
4643
4644}
4645
4646static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
4647 struct device_attribute *attr,
4648 const char *buf, size_t count)
4649{
4650 struct wl1271 *wl = dev_get_drvdata(dev);
4651 unsigned long res;
4652 int ret;
4653
Luciano Coelho6277ed62011-04-01 17:49:54 +03004654 ret = kstrtoul(buf, 10, &res);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02004655 if (ret < 0) {
4656 wl1271_warning("incorrect value written to bt_coex_mode");
4657 return count;
4658 }
4659
4660 mutex_lock(&wl->mutex);
4661
4662 res = !!res;
4663
4664 if (res == wl->sg_enabled)
4665 goto out;
4666
4667 wl->sg_enabled = res;
4668
4669 if (wl->state == WL1271_STATE_OFF)
4670 goto out;
4671
Ido Yariva6208652011-03-01 15:14:41 +02004672 ret = wl1271_ps_elp_wakeup(wl);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02004673 if (ret < 0)
4674 goto out;
4675
4676 wl1271_acx_sg_enable(wl, wl->sg_enabled);
4677 wl1271_ps_elp_sleep(wl);
4678
4679 out:
4680 mutex_unlock(&wl->mutex);
4681 return count;
4682}
4683
4684static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
4685 wl1271_sysfs_show_bt_coex_state,
4686 wl1271_sysfs_store_bt_coex_state);
4687
Juuso Oikarinend717fd62010-05-07 11:38:58 +03004688static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
4689 struct device_attribute *attr,
4690 char *buf)
4691{
4692 struct wl1271 *wl = dev_get_drvdata(dev);
4693 ssize_t len;
4694
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02004695 len = PAGE_SIZE;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03004696
4697 mutex_lock(&wl->mutex);
4698 if (wl->hw_pg_ver >= 0)
4699 len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
4700 else
4701 len = snprintf(buf, len, "n/a\n");
4702 mutex_unlock(&wl->mutex);
4703
4704 return len;
4705}
4706
Gery Kahn6f07b722011-07-18 14:21:49 +03004707static DEVICE_ATTR(hw_pg_ver, S_IRUGO,
Juuso Oikarinend717fd62010-05-07 11:38:58 +03004708 wl1271_sysfs_show_hw_pg_ver, NULL);
4709
Ido Yariv95dac04f2011-06-06 14:57:06 +03004710static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
4711 struct bin_attribute *bin_attr,
4712 char *buffer, loff_t pos, size_t count)
4713{
4714 struct device *dev = container_of(kobj, struct device, kobj);
4715 struct wl1271 *wl = dev_get_drvdata(dev);
4716 ssize_t len;
4717 int ret;
4718
4719 ret = mutex_lock_interruptible(&wl->mutex);
4720 if (ret < 0)
4721 return -ERESTARTSYS;
4722
4723 /* Let only one thread read the log at a time, blocking others */
4724 while (wl->fwlog_size == 0) {
4725 DEFINE_WAIT(wait);
4726
4727 prepare_to_wait_exclusive(&wl->fwlog_waitq,
4728 &wait,
4729 TASK_INTERRUPTIBLE);
4730
4731 if (wl->fwlog_size != 0) {
4732 finish_wait(&wl->fwlog_waitq, &wait);
4733 break;
4734 }
4735
4736 mutex_unlock(&wl->mutex);
4737
4738 schedule();
4739 finish_wait(&wl->fwlog_waitq, &wait);
4740
4741 if (signal_pending(current))
4742 return -ERESTARTSYS;
4743
4744 ret = mutex_lock_interruptible(&wl->mutex);
4745 if (ret < 0)
4746 return -ERESTARTSYS;
4747 }
4748
4749 /* Check if the fwlog is still valid */
4750 if (wl->fwlog_size < 0) {
4751 mutex_unlock(&wl->mutex);
4752 return 0;
4753 }
4754
4755 /* Seeking is not supported - old logs are not kept. Disregard pos. */
4756 len = min(count, (size_t)wl->fwlog_size);
4757 wl->fwlog_size -= len;
4758 memcpy(buffer, wl->fwlog, len);
4759
4760 /* Make room for new messages */
4761 memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
4762
4763 mutex_unlock(&wl->mutex);
4764
4765 return len;
4766}
4767
4768static struct bin_attribute fwlog_attr = {
4769 .attr = {.name = "fwlog", .mode = S_IRUSR},
4770 .read = wl1271_sysfs_read_fwlog,
4771};
4772
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02004773int wl1271_register_hw(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004774{
4775 int ret;
4776
4777 if (wl->mac80211_registered)
4778 return 0;
4779
Arik Nemtsov31d26ec2010-10-16 21:49:52 +02004780 ret = wl1271_fetch_nvs(wl);
4781 if (ret == 0) {
Shahar Levibc765bf2011-03-06 16:32:10 +02004782 /* NOTE: The wl->nvs->nvs element must be first, in
4783 * order to simplify the casting, we assume it is at
4784 * the beginning of the wl->nvs structure.
4785 */
4786 u8 *nvs_ptr = (u8 *)wl->nvs;
Arik Nemtsov31d26ec2010-10-16 21:49:52 +02004787
4788 wl->mac_addr[0] = nvs_ptr[11];
4789 wl->mac_addr[1] = nvs_ptr[10];
4790 wl->mac_addr[2] = nvs_ptr[6];
4791 wl->mac_addr[3] = nvs_ptr[5];
4792 wl->mac_addr[4] = nvs_ptr[4];
4793 wl->mac_addr[5] = nvs_ptr[3];
4794 }
4795
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004796 SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
4797
4798 ret = ieee80211_register_hw(wl->hw);
4799 if (ret < 0) {
4800 wl1271_error("unable to register mac80211 hw: %d", ret);
4801 return ret;
4802 }
4803
4804 wl->mac80211_registered = true;
4805
Eliad Pellerd60080a2010-11-24 12:53:16 +02004806 wl1271_debugfs_init(wl);
4807
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03004808 register_netdevice_notifier(&wl1271_dev_notifier);
4809
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004810 wl1271_notice("loaded");
4811
4812 return 0;
4813}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02004814EXPORT_SYMBOL_GPL(wl1271_register_hw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004815
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004816void wl1271_unregister_hw(struct wl1271 *wl)
4817{
Juuso Oikarinen4ae3fa82011-01-14 12:48:46 +01004818 if (wl->state == WL1271_STATE_PLT)
4819 __wl1271_plt_stop(wl);
4820
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03004821 unregister_netdevice_notifier(&wl1271_dev_notifier);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004822 ieee80211_unregister_hw(wl->hw);
4823 wl->mac80211_registered = false;
4824
4825}
4826EXPORT_SYMBOL_GPL(wl1271_unregister_hw);
4827
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02004828int wl1271_init_ieee80211(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004829{
Juuso Oikarinen7a557242010-09-27 12:42:07 +02004830 static const u32 cipher_suites[] = {
4831 WLAN_CIPHER_SUITE_WEP40,
4832 WLAN_CIPHER_SUITE_WEP104,
4833 WLAN_CIPHER_SUITE_TKIP,
4834 WLAN_CIPHER_SUITE_CCMP,
4835 WL1271_CIPHER_SUITE_GEM,
4836 };
4837
Juuso Oikarinen1e2b7972009-10-08 21:56:20 +03004838 /* The tx descriptor buffer and the TKIP space. */
4839 wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
4840 sizeof(struct wl1271_tx_hw_descr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004841
4842 /* unit us */
4843 /* FIXME: find a proper value */
4844 wl->hw->channel_change_time = 10000;
Juuso Oikarinen50c500a2010-04-01 11:38:22 +03004845 wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004846
4847 wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
Juuso Oikarinen03442a32009-11-23 23:22:14 +02004848 IEEE80211_HW_BEACON_FILTER |
Juuso Oikarinen0a343322010-02-22 08:38:41 +02004849 IEEE80211_HW_SUPPORTS_PS |
Kalle Valo4695dc92010-03-18 12:26:38 +02004850 IEEE80211_HW_SUPPORTS_UAPSD |
Juuso Oikarinena9af0922010-03-26 12:53:30 +02004851 IEEE80211_HW_HAS_RATE_CONTROL |
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03004852 IEEE80211_HW_CONNECTION_MONITOR |
Eliad Peller62c07402011-02-02 11:20:05 +02004853 IEEE80211_HW_SUPPORTS_CQM_RSSI |
Luciano Coelho25eaea302011-05-02 12:37:33 +03004854 IEEE80211_HW_REPORTS_TX_ACK_STATUS |
Shahar Levifcd23b62011-05-11 12:12:56 +03004855 IEEE80211_HW_SPECTRUM_MGMT |
Arik Nemtsov93f8c8e2011-08-30 09:34:01 +03004856 IEEE80211_HW_AP_LINK_PS |
4857 IEEE80211_HW_AMPDU_AGGREGATION |
4858 IEEE80211_HW_TX_AMPDU_SETUP_IN_HW;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004859
Juuso Oikarinen7a557242010-09-27 12:42:07 +02004860 wl->hw->wiphy->cipher_suites = cipher_suites;
4861 wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
4862
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02004863 wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
Eliad Peller045c7452011-08-28 15:23:01 +03004864 BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) |
4865 BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004866 wl->hw->wiphy->max_scan_ssids = 1;
Luciano Coelho221737d2011-09-02 14:28:22 +03004867 wl->hw->wiphy->max_sched_scan_ssids = 16;
4868 wl->hw->wiphy->max_match_sets = 16;
Guy Eilamea559b42010-12-09 16:54:59 +02004869 /*
4870 * Maximum length of elements in scanning probe request templates
4871 * should be the maximum length possible for a template, without
4872 * the IEEE80211 header of the template
4873 */
Eliad Peller154037d2011-08-14 13:17:12 +03004874 wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_DFLT_SIZE -
Guy Eilamea559b42010-12-09 16:54:59 +02004875 sizeof(struct ieee80211_header);
Luciano Coelhoa8aaaf52011-01-11 18:25:18 +01004876
Luciano Coelhoc9e79a42011-09-27 16:22:35 +03004877 wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_DFLT_SIZE -
4878 sizeof(struct ieee80211_header);
4879
Eliad Peller1ec23f72011-08-25 14:26:54 +03004880 wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
4881
Luciano Coelho4a31c112011-03-21 23:16:14 +02004882 /* make sure all our channels fit in the scanned_ch bitmask */
4883 BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
4884 ARRAY_SIZE(wl1271_channels_5ghz) >
4885 WL1271_MAX_CHANNELS);
Luciano Coelhoa8aaaf52011-01-11 18:25:18 +01004886 /*
4887 * We keep local copies of the band structs because we need to
4888 * modify them on a per-device basis.
4889 */
4890 memcpy(&wl->bands[IEEE80211_BAND_2GHZ], &wl1271_band_2ghz,
4891 sizeof(wl1271_band_2ghz));
4892 memcpy(&wl->bands[IEEE80211_BAND_5GHZ], &wl1271_band_5ghz,
4893 sizeof(wl1271_band_5ghz));
4894
4895 wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
4896 &wl->bands[IEEE80211_BAND_2GHZ];
4897 wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
4898 &wl->bands[IEEE80211_BAND_5GHZ];
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03004899
Kalle Valo12bd8942010-03-18 12:26:33 +02004900 wl->hw->queues = 4;
Juuso Oikarinen31627dc2010-03-26 12:53:12 +02004901 wl->hw->max_rates = 1;
Kalle Valo12bd8942010-03-18 12:26:33 +02004902
Juuso Oikarinenb7417d92010-11-10 11:27:19 +01004903 wl->hw->wiphy->reg_notifier = wl1271_reg_notify;
4904
Teemu Paasikivi8197b712010-02-22 08:38:23 +02004905 SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004906
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004907 wl->hw->sta_data_size = sizeof(struct wl1271_station);
Eliad Peller87fbcb02011-10-05 11:55:41 +02004908 wl->hw->vif_data_size = sizeof(struct wl12xx_vif);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004909
Luciano Coelho4c9cfa72011-01-12 14:27:03 +01004910 wl->hw->max_rx_aggregation_subframes = 8;
4911
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004912 return 0;
4913}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02004914EXPORT_SYMBOL_GPL(wl1271_init_ieee80211);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004915
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004916#define WL1271_DEFAULT_CHANNEL 0
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02004917
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02004918struct ieee80211_hw *wl1271_alloc_hw(void)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004919{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004920 struct ieee80211_hw *hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004921 struct platform_device *plat_dev = NULL;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004922 struct wl1271 *wl;
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02004923 int i, j, ret;
Ido Yariv1f37cbc2010-09-30 13:28:27 +02004924 unsigned int order;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004925
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004926 BUILD_BUG_ON(AP_MAX_STATIONS > WL12XX_MAX_LINKS);
Arik Nemtsovf80c2d12011-09-22 09:52:05 +03004927
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004928 hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
4929 if (!hw) {
4930 wl1271_error("could not alloc ieee80211_hw");
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02004931 ret = -ENOMEM;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004932 goto err_hw_alloc;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004933 }
4934
Julia Lawall929ebd32010-05-15 23:16:39 +02004935 plat_dev = kmemdup(&wl1271_device, sizeof(wl1271_device), GFP_KERNEL);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004936 if (!plat_dev) {
4937 wl1271_error("could not allocate platform_device");
4938 ret = -ENOMEM;
4939 goto err_plat_alloc;
4940 }
4941
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004942 wl = hw->priv;
4943 memset(wl, 0, sizeof(*wl));
4944
Juuso Oikarinen01c09162009-10-13 12:47:55 +03004945 INIT_LIST_HEAD(&wl->list);
Eliad Peller87627212011-10-10 10:12:54 +02004946 INIT_LIST_HEAD(&wl->wlvif_list);
Juuso Oikarinen01c09162009-10-13 12:47:55 +03004947
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004948 wl->hw = hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004949 wl->plat_dev = plat_dev;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004950
Juuso Oikarinen6742f552010-12-13 09:52:37 +02004951 for (i = 0; i < NUM_TX_QUEUES; i++)
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004952 for (j = 0; j < WL12XX_MAX_LINKS; j++)
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02004953 skb_queue_head_init(&wl->links[j].tx_queue[i]);
4954
Ido Yariva6208652011-03-01 15:14:41 +02004955 skb_queue_head_init(&wl->deferred_rx_queue);
4956 skb_queue_head_init(&wl->deferred_tx_queue);
4957
Juuso Oikarinen37b70a82009-10-08 21:56:21 +03004958 INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
Ido Yariva6208652011-03-01 15:14:41 +02004959 INIT_WORK(&wl->netstack_work, wl1271_netstack_work);
Juuso Oikarinen117b38d2010-09-30 10:43:28 +02004960 INIT_WORK(&wl->tx_work, wl1271_tx_work);
4961 INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
4962 INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
Eliad Peller77ddaa12011-05-15 11:10:29 +03004963
Eliad Peller92ef8962011-06-07 12:50:46 +03004964 wl->freezable_wq = create_freezable_workqueue("wl12xx_wq");
4965 if (!wl->freezable_wq) {
4966 ret = -ENOMEM;
4967 goto err_hw;
4968 }
4969
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004970 wl->channel = WL1271_DEFAULT_CHANNEL;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004971 wl->rx_counter = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004972 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03004973 wl->band = IEEE80211_BAND_2GHZ;
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03004974 wl->vif = NULL;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02004975 wl->flags = 0;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02004976 wl->sg_enabled = true;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03004977 wl->hw_pg_ver = -1;
Arik Nemtsovb622d992011-02-23 00:22:31 +02004978 wl->ap_ps_map = 0;
4979 wl->ap_fw_ps_map = 0;
Ido Yariv606ea9f2011-03-01 15:14:39 +02004980 wl->quirks = 0;
Ido Yariv341b7cd2011-03-31 10:07:01 +02004981 wl->platform_quirks = 0;
Luciano Coelho33c2c062011-05-10 14:46:02 +03004982 wl->sched_scanning = false;
Guy Eilame9eb8cb2011-08-16 19:49:12 +03004983 wl->tx_spare_blocks = TX_HW_BLOCK_SPARE_DEFAULT;
Eliad Pellerf4df1bd2011-08-14 13:17:15 +03004984 wl->system_hlid = WL12XX_SYSTEM_HLID;
Arik Nemtsovda032092011-08-25 12:43:15 +03004985 wl->active_sta_count = 0;
Ido Yariv95dac04f2011-06-06 14:57:06 +03004986 wl->fwlog_size = 0;
4987 init_waitqueue_head(&wl->fwlog_waitq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004988
Eliad Pellerf4df1bd2011-08-14 13:17:15 +03004989 /* The system link is always allocated */
4990 __set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
4991
Ido Yariv25eeb9e2010-10-12 16:20:06 +02004992 memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
Juuso Oikarinenbe7078c2009-10-08 21:56:26 +03004993 for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004994 wl->tx_frames[i] = NULL;
4995
4996 spin_lock_init(&wl->wl_lock);
4997
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004998 wl->state = WL1271_STATE_OFF;
4999 mutex_init(&wl->mutex);
5000
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02005001 /* Apply default driver configuration. */
5002 wl1271_conf_init(wl);
5003
Ido Yariv1f37cbc2010-09-30 13:28:27 +02005004 order = get_order(WL1271_AGGR_BUFFER_SIZE);
5005 wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
5006 if (!wl->aggr_buf) {
5007 ret = -ENOMEM;
Eliad Peller92ef8962011-06-07 12:50:46 +03005008 goto err_wq;
Ido Yariv1f37cbc2010-09-30 13:28:27 +02005009 }
5010
Ido Yariv990f5de2011-03-31 10:06:59 +02005011 wl->dummy_packet = wl12xx_alloc_dummy_packet(wl);
5012 if (!wl->dummy_packet) {
5013 ret = -ENOMEM;
5014 goto err_aggr;
5015 }
5016
Ido Yariv95dac04f2011-06-06 14:57:06 +03005017 /* Allocate one page for the FW log */
5018 wl->fwlog = (u8 *)get_zeroed_page(GFP_KERNEL);
5019 if (!wl->fwlog) {
5020 ret = -ENOMEM;
5021 goto err_dummy_packet;
5022 }
5023
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005024 /* Register platform device */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005025 ret = platform_device_register(wl->plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005026 if (ret) {
5027 wl1271_error("couldn't register platform device");
Ido Yariv95dac04f2011-06-06 14:57:06 +03005028 goto err_fwlog;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005029 }
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005030 dev_set_drvdata(&wl->plat_dev->dev, wl);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005031
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02005032 /* Create sysfs file to control bt coex state */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005033 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02005034 if (ret < 0) {
5035 wl1271_error("failed to create sysfs file bt_coex_state");
5036 goto err_platform;
5037 }
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005038
Juuso Oikarinend717fd62010-05-07 11:38:58 +03005039 /* Create sysfs file to get HW PG version */
5040 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
5041 if (ret < 0) {
5042 wl1271_error("failed to create sysfs file hw_pg_ver");
5043 goto err_bt_coex_state;
5044 }
5045
Ido Yariv95dac04f2011-06-06 14:57:06 +03005046 /* Create sysfs file for the FW log */
5047 ret = device_create_bin_file(&wl->plat_dev->dev, &fwlog_attr);
5048 if (ret < 0) {
5049 wl1271_error("failed to create sysfs file fwlog");
5050 goto err_hw_pg_ver;
5051 }
5052
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02005053 return hw;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005054
Ido Yariv95dac04f2011-06-06 14:57:06 +03005055err_hw_pg_ver:
5056 device_remove_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
5057
Juuso Oikarinend717fd62010-05-07 11:38:58 +03005058err_bt_coex_state:
5059 device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
5060
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02005061err_platform:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005062 platform_device_unregister(wl->plat_dev);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02005063
Ido Yariv95dac04f2011-06-06 14:57:06 +03005064err_fwlog:
5065 free_page((unsigned long)wl->fwlog);
5066
Ido Yariv990f5de2011-03-31 10:06:59 +02005067err_dummy_packet:
5068 dev_kfree_skb(wl->dummy_packet);
5069
Ido Yariv1f37cbc2010-09-30 13:28:27 +02005070err_aggr:
5071 free_pages((unsigned long)wl->aggr_buf, order);
5072
Eliad Peller92ef8962011-06-07 12:50:46 +03005073err_wq:
5074 destroy_workqueue(wl->freezable_wq);
5075
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005076err_hw:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005077 wl1271_debugfs_exit(wl);
5078 kfree(plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005079
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005080err_plat_alloc:
5081 ieee80211_free_hw(hw);
5082
5083err_hw_alloc:
5084
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005085 return ERR_PTR(ret);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02005086}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02005087EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02005088
5089int wl1271_free_hw(struct wl1271 *wl)
5090{
Ido Yariv95dac04f2011-06-06 14:57:06 +03005091 /* Unblock any fwlog readers */
5092 mutex_lock(&wl->mutex);
5093 wl->fwlog_size = -1;
5094 wake_up_interruptible_all(&wl->fwlog_waitq);
5095 mutex_unlock(&wl->mutex);
5096
5097 device_remove_bin_file(&wl->plat_dev->dev, &fwlog_attr);
Gery Kahn6f07b722011-07-18 14:21:49 +03005098
5099 device_remove_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
5100
5101 device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005102 platform_device_unregister(wl->plat_dev);
Ido Yariv95dac04f2011-06-06 14:57:06 +03005103 free_page((unsigned long)wl->fwlog);
Ido Yariv990f5de2011-03-31 10:06:59 +02005104 dev_kfree_skb(wl->dummy_packet);
Ido Yariv1f37cbc2010-09-30 13:28:27 +02005105 free_pages((unsigned long)wl->aggr_buf,
5106 get_order(WL1271_AGGR_BUFFER_SIZE));
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005107 kfree(wl->plat_dev);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02005108
5109 wl1271_debugfs_exit(wl);
5110
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02005111 vfree(wl->fw);
5112 wl->fw = NULL;
5113 kfree(wl->nvs);
5114 wl->nvs = NULL;
5115
5116 kfree(wl->fw_status);
5117 kfree(wl->tx_res_if);
Eliad Peller92ef8962011-06-07 12:50:46 +03005118 destroy_workqueue(wl->freezable_wq);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02005119
5120 ieee80211_free_hw(wl->hw);
5121
5122 return 0;
5123}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02005124EXPORT_SYMBOL_GPL(wl1271_free_hw);
5125
Guy Eilam491bbd62011-01-12 10:33:29 +01005126u32 wl12xx_debug_level = DEBUG_NONE;
Eliad Peller17c17552010-12-12 12:15:35 +02005127EXPORT_SYMBOL_GPL(wl12xx_debug_level);
Guy Eilam491bbd62011-01-12 10:33:29 +01005128module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
Eliad Peller17c17552010-12-12 12:15:35 +02005129MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
5130
Ido Yariv95dac04f2011-06-06 14:57:06 +03005131module_param_named(fwlog, fwlog_param, charp, 0);
5132MODULE_PARM_DESC(keymap,
5133 "FW logger options: continuous, ondemand, dbgpins or disable");
5134
Eliad Peller2a5bff02011-08-25 18:10:59 +03005135module_param(bug_on_recovery, bool, S_IRUSR | S_IWUSR);
5136MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");
5137
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02005138MODULE_LICENSE("GPL");
Luciano Coelhob1a48ca2011-02-22 14:19:28 +02005139MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02005140MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");