blob: 8e395f1f4b1aa1dccf55ab2885e160bbf96a7bfc [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;
1976
Eliad Peller9eb599e2011-10-10 10:12:59 +02001977 INIT_WORK(&wlvif->rx_streaming_enable_work,
1978 wl1271_rx_streaming_enable_work);
1979 INIT_WORK(&wlvif->rx_streaming_disable_work,
1980 wl1271_rx_streaming_disable_work);
Eliad Peller252efa42011-10-05 11:56:00 +02001981 INIT_DELAYED_WORK(&wlvif->pspoll_work, wl1271_pspoll_work);
Eliad Peller87627212011-10-10 10:12:54 +02001982 INIT_LIST_HEAD(&wlvif->list);
Eliad Peller252efa42011-10-05 11:56:00 +02001983
Eliad Peller9eb599e2011-10-10 10:12:59 +02001984 setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
1985 (unsigned long) wlvif);
Eliad Pellere936bbe2011-10-05 11:55:56 +02001986 return 0;
Eliad Peller87fbcb02011-10-05 11:55:41 +02001987}
1988
Eliad Peller1d095472011-10-10 10:12:49 +02001989static bool wl12xx_init_fw(struct wl1271 *wl)
1990{
1991 int retries = WL1271_BOOT_RETRIES;
1992 bool booted = false;
1993 struct wiphy *wiphy = wl->hw->wiphy;
1994 int ret;
1995
1996 while (retries) {
1997 retries--;
1998 ret = wl1271_chip_wakeup(wl);
1999 if (ret < 0)
2000 goto power_off;
2001
2002 ret = wl1271_boot(wl);
2003 if (ret < 0)
2004 goto power_off;
2005
2006 ret = wl1271_hw_init(wl);
2007 if (ret < 0)
2008 goto irq_disable;
2009
2010 booted = true;
2011 break;
2012
2013irq_disable:
2014 mutex_unlock(&wl->mutex);
2015 /* Unlocking the mutex in the middle of handling is
2016 inherently unsafe. In this case we deem it safe to do,
2017 because we need to let any possibly pending IRQ out of
2018 the system (and while we are WL1271_STATE_OFF the IRQ
2019 work function will not do anything.) Also, any other
2020 possible concurrent operations will fail due to the
2021 current state, hence the wl1271 struct should be safe. */
2022 wl1271_disable_interrupts(wl);
2023 wl1271_flush_deferred_work(wl);
2024 cancel_work_sync(&wl->netstack_work);
2025 mutex_lock(&wl->mutex);
2026power_off:
2027 wl1271_power_off(wl);
2028 }
2029
2030 if (!booted) {
2031 wl1271_error("firmware boot failed despite %d retries",
2032 WL1271_BOOT_RETRIES);
2033 goto out;
2034 }
2035
2036 wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str);
2037
2038 /* update hw/fw version info in wiphy struct */
2039 wiphy->hw_version = wl->chip.id;
2040 strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
2041 sizeof(wiphy->fw_version));
2042
2043 /*
2044 * Now we know if 11a is supported (info from the NVS), so disable
2045 * 11a channels if not supported
2046 */
2047 if (!wl->enable_11a)
2048 wiphy->bands[IEEE80211_BAND_5GHZ]->n_channels = 0;
2049
2050 wl1271_debug(DEBUG_MAC80211, "11a is %ssupported",
2051 wl->enable_11a ? "" : "not ");
2052
2053 wl->state = WL1271_STATE_ON;
2054out:
2055 return booted;
2056}
2057
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002058static int wl1271_op_add_interface(struct ieee80211_hw *hw,
2059 struct ieee80211_vif *vif)
2060{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002061 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02002062 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002063 int ret = 0;
Eliad Pellerb78b47e2011-08-14 13:17:08 +03002064 u8 role_type;
Eliad Peller71125ab2010-10-28 21:46:43 +02002065 bool booted = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002066
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002067 wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
Eliad Peller045c7452011-08-28 15:23:01 +03002068 ieee80211_vif_type_p2p(vif), vif->addr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002069
2070 mutex_lock(&wl->mutex);
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002071 if (wl->vif) {
Eliad Peller71125ab2010-10-28 21:46:43 +02002072 wl1271_debug(DEBUG_MAC80211,
2073 "multiple vifs are not supported yet");
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002074 ret = -EBUSY;
2075 goto out;
2076 }
2077
Juuso Oikarinen13026de2011-03-29 16:43:50 +03002078 /*
2079 * in some very corner case HW recovery scenarios its possible to
2080 * get here before __wl1271_op_remove_interface is complete, so
2081 * opt out if that is the case.
2082 */
Eliad Peller10c8cd02011-10-10 10:13:06 +02002083 if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags) ||
2084 test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)) {
Juuso Oikarinen13026de2011-03-29 16:43:50 +03002085 ret = -EBUSY;
2086 goto out;
2087 }
2088
Eliad Peller83587502011-10-10 10:12:53 +02002089 ret = wl12xx_init_vif_data(wl, vif);
Eliad Pellere936bbe2011-10-05 11:55:56 +02002090 if (ret < 0)
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002091 goto out;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002092
Eliad Peller252efa42011-10-05 11:56:00 +02002093 wlvif->wl = wl;
Eliad Peller536129c2011-10-05 11:55:45 +02002094 role_type = wl12xx_get_role_type(wl, wlvif);
Eliad Pellerb78b47e2011-08-14 13:17:08 +03002095 if (role_type == WL12XX_INVALID_ROLE_TYPE) {
2096 ret = -EINVAL;
2097 goto out;
2098 }
Eliad Peller1d095472011-10-10 10:12:49 +02002099
Eliad Peller784f6942011-10-05 11:55:39 +02002100 /*
Eliad Peller1d095472011-10-10 10:12:49 +02002101 * TODO: after the nvs issue will be solved, move this block
2102 * to start(), and make sure here the driver is ON.
Eliad Peller784f6942011-10-05 11:55:39 +02002103 */
Eliad Peller1d095472011-10-10 10:12:49 +02002104 if (wl->state == WL1271_STATE_OFF) {
2105 /*
2106 * we still need this in order to configure the fw
2107 * while uploading the nvs
2108 */
2109 memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002110
Eliad Peller1d095472011-10-10 10:12:49 +02002111 booted = wl12xx_init_fw(wl);
2112 if (!booted) {
2113 ret = -EINVAL;
2114 goto out;
Eliad Peller04e80792011-08-14 13:17:09 +03002115 }
Eliad Peller1d095472011-10-10 10:12:49 +02002116 }
Eliad Peller04e80792011-08-14 13:17:09 +03002117
Eliad Peller1d095472011-10-10 10:12:49 +02002118 if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
2119 wlvif->bss_type == BSS_TYPE_IBSS) {
2120 /*
2121 * The device role is a special role used for
2122 * rx and tx frames prior to association (as
2123 * the STA role can get packets only from
2124 * its associated bssid)
2125 */
Eliad Peller784f6942011-10-05 11:55:39 +02002126 ret = wl12xx_cmd_role_enable(wl, vif->addr,
Eliad Peller1d095472011-10-10 10:12:49 +02002127 WL1271_ROLE_DEVICE,
2128 &wlvif->dev_role_id);
Eliad Pellerb78b47e2011-08-14 13:17:08 +03002129 if (ret < 0)
Eliad Peller1d095472011-10-10 10:12:49 +02002130 goto out;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02002131 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002132
Eliad Peller1d095472011-10-10 10:12:49 +02002133 ret = wl12xx_cmd_role_enable(wl, vif->addr,
2134 role_type, &wlvif->role_id);
2135 if (ret < 0)
Eliad Peller71125ab2010-10-28 21:46:43 +02002136 goto out;
Eliad Peller1d095472011-10-10 10:12:49 +02002137
2138 ret = wl1271_init_vif_specific(wl, vif);
2139 if (ret < 0)
2140 goto out;
Eliad Peller71125ab2010-10-28 21:46:43 +02002141
2142 wl->vif = vif;
Eliad Peller87627212011-10-10 10:12:54 +02002143 list_add(&wlvif->list, &wl->wlvif_list);
Eliad Peller10c8cd02011-10-10 10:13:06 +02002144 set_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags);
Eliad Pellera4e41302011-10-11 11:49:15 +02002145
2146 if (wlvif->bss_type == BSS_TYPE_AP_BSS)
2147 wl->ap_count++;
2148 else
2149 wl->sta_count++;
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +03002150out:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002151 mutex_unlock(&wl->mutex);
2152
Juuso Oikarinenf9f774c2011-03-21 10:43:36 +02002153 mutex_lock(&wl_list_mutex);
Juuso Oikarineneb887df2010-07-08 17:49:58 +03002154 if (!ret)
Juuso Oikarinen01c09162009-10-13 12:47:55 +03002155 list_add(&wl->list, &wl_list);
Juuso Oikarinenf9f774c2011-03-21 10:43:36 +02002156 mutex_unlock(&wl_list_mutex);
Juuso Oikarinen01c09162009-10-13 12:47:55 +03002157
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002158 return ret;
2159}
2160
Arik Nemtsov7dece1c2011-04-18 14:15:28 +03002161static void __wl1271_op_remove_interface(struct wl1271 *wl,
Eliad Peller536129c2011-10-05 11:55:45 +02002162 struct ieee80211_vif *vif,
Arik Nemtsov7dece1c2011-04-18 14:15:28 +03002163 bool reset_tx_queues)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002164{
Eliad Peller536129c2011-10-05 11:55:45 +02002165 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Eliad Pellerbaf62772011-10-10 10:12:52 +02002166 int ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002167
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002168 wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002169
Eliad Peller10c8cd02011-10-10 10:13:06 +02002170 if (!test_and_clear_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
2171 return;
2172
Juuso Oikarinen13026de2011-03-29 16:43:50 +03002173 /* because of hardware recovery, we may get here twice */
2174 if (wl->state != WL1271_STATE_ON)
2175 return;
2176
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02002177 wl1271_info("down");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002178
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03002179 /* enable dyn ps just in case (if left on due to fw crash etc) */
Eliad Peller536129c2011-10-05 11:55:45 +02002180 if (wlvif->bss_type == BSS_TYPE_STA_BSS)
Eliad Pellerbaf62772011-10-10 10:12:52 +02002181 ieee80211_enable_dyn_ps(vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03002182
Eliad Pellerbaf62772011-10-10 10:12:52 +02002183 if (wl->scan.state != WL1271_SCAN_STATE_IDLE &&
2184 wl->scan_vif == vif) {
Luciano Coelho08688d62010-07-08 17:50:07 +03002185 wl->scan.state = WL1271_SCAN_STATE_IDLE;
Luciano Coelho4a31c112011-03-21 23:16:14 +02002186 memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
Eliad Peller784f6942011-10-05 11:55:39 +02002187 wl->scan_vif = NULL;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02002188 wl->scan.req = NULL;
Juuso Oikarinen76a029f2010-07-29 04:54:45 +03002189 ieee80211_scan_completed(wl->hw, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002190 }
2191
Eliad Pellerb78b47e2011-08-14 13:17:08 +03002192 if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
2193 /* disable active roles */
2194 ret = wl1271_ps_elp_wakeup(wl);
2195 if (ret < 0)
2196 goto deinit;
2197
Eliad Peller536129c2011-10-05 11:55:45 +02002198 if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
Eliad Peller7edebf52011-10-05 11:55:52 +02002199 ret = wl12xx_cmd_role_disable(wl, &wlvif->dev_role_id);
Eliad Peller04e80792011-08-14 13:17:09 +03002200 if (ret < 0)
2201 goto deinit;
2202 }
2203
Eliad Peller0603d892011-10-05 11:55:51 +02002204 ret = wl12xx_cmd_role_disable(wl, &wlvif->role_id);
Eliad Pellerb78b47e2011-08-14 13:17:08 +03002205 if (ret < 0)
2206 goto deinit;
2207
2208 wl1271_ps_elp_sleep(wl);
2209 }
2210deinit:
Arik Nemtsove51ae9b2011-08-14 13:17:21 +03002211 /* clear all hlids (except system_hlid) */
Eliad Peller154da672011-10-05 11:55:53 +02002212 wlvif->sta.hlid = WL12XX_INVALID_LINK_ID;
Eliad Pellerafaf8bd2011-10-05 11:55:57 +02002213 wlvif->dev_hlid = WL12XX_INVALID_LINK_ID;
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002214 wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID;
2215 wlvif->ap.global_hlid = WL12XX_INVALID_LINK_ID;
Eliad Pellerb78b47e2011-08-14 13:17:08 +03002216
Eliad Pellerd6a3cc22011-10-10 10:12:51 +02002217 wl12xx_tx_reset_wlvif(wl, wlvif);
Eliad Peller170d0e62011-10-05 11:56:06 +02002218 wl1271_free_ap_keys(wl, wlvif);
Eliad Peller87627212011-10-10 10:12:54 +02002219 list_del(&wlvif->list);
Eliad Pellerc7ffb902011-10-05 11:56:05 +02002220 memset(wlvif->ap.sta_hlid_map, 0, sizeof(wlvif->ap.sta_hlid_map));
Eliad Peller0603d892011-10-05 11:55:51 +02002221 wlvif->role_id = WL12XX_INVALID_ROLE_ID;
Eliad Peller7edebf52011-10-05 11:55:52 +02002222 wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID;
Luciano Coelhod6e19d12009-10-12 15:08:43 +03002223
Eliad Pellera4e41302011-10-11 11:49:15 +02002224 if (wlvif->bss_type == BSS_TYPE_AP_BSS)
2225 wl->ap_count--;
2226 else
2227 wl->sta_count--;
2228
Eliad Pellerbaf62772011-10-10 10:12:52 +02002229 mutex_unlock(&wl->mutex);
Eliad Peller9eb599e2011-10-10 10:12:59 +02002230 del_timer_sync(&wlvif->rx_streaming_timer);
2231 cancel_work_sync(&wlvif->rx_streaming_enable_work);
2232 cancel_work_sync(&wlvif->rx_streaming_disable_work);
Eliad Pellerbaf62772011-10-10 10:12:52 +02002233 cancel_delayed_work_sync(&wlvif->pspoll_work);
Eliad Pellerf4df1bd2011-08-14 13:17:15 +03002234
Eliad Pellerbaf62772011-10-10 10:12:52 +02002235 mutex_lock(&wl->mutex);
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02002236}
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03002237
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02002238static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
2239 struct ieee80211_vif *vif)
2240{
2241 struct wl1271 *wl = hw->priv;
Eliad Peller10c8cd02011-10-10 10:13:06 +02002242 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02002243
2244 mutex_lock(&wl->mutex);
Eliad Peller10c8cd02011-10-10 10:13:06 +02002245
2246 if (wl->state == WL1271_STATE_OFF ||
2247 !test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
2248 goto out;
2249
Juuso Oikarinen67353292010-11-18 15:19:02 +02002250 /*
2251 * wl->vif can be null here if someone shuts down the interface
2252 * just when hardware recovery has been started.
2253 */
2254 if (wl->vif) {
2255 WARN_ON(wl->vif != vif);
Eliad Peller536129c2011-10-05 11:55:45 +02002256 __wl1271_op_remove_interface(wl, vif, true);
Juuso Oikarinen67353292010-11-18 15:19:02 +02002257 }
Eliad Peller10c8cd02011-10-10 10:13:06 +02002258out:
Juuso Oikarinen67353292010-11-18 15:19:02 +02002259 mutex_unlock(&wl->mutex);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02002260 cancel_work_sync(&wl->recovery_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002261}
2262
Eliad Peller87fbcb02011-10-05 11:55:41 +02002263static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
2264 bool set_assoc)
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002265{
2266 int ret;
Eliad Peller536129c2011-10-05 11:55:45 +02002267 bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002268
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002269 /*
2270 * One of the side effects of the JOIN command is that is clears
2271 * WPA/WPA2 keys from the chipset. Performing a JOIN while associated
2272 * to a WPA/WPA2 access point will therefore kill the data-path.
Ohad Ben-Cohen8bf69aa2011-03-30 19:18:31 +02002273 * Currently the only valid scenario for JOIN during association
2274 * is on roaming, in which case we will also be given new keys.
2275 * Keep the below message for now, unless it starts bothering
2276 * users who really like to roam a lot :)
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002277 */
Eliad Pellerba8447f2011-10-10 10:13:00 +02002278 if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002279 wl1271_info("JOIN while associated.");
2280
2281 if (set_assoc)
Eliad Pellerba8447f2011-10-10 10:13:00 +02002282 set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002283
Eliad Peller227e81e2011-08-14 13:17:26 +03002284 if (is_ibss)
Eliad Peller87fbcb02011-10-05 11:55:41 +02002285 ret = wl12xx_cmd_role_start_ibss(wl, wlvif);
Eliad Peller227e81e2011-08-14 13:17:26 +03002286 else
Eliad Peller87fbcb02011-10-05 11:55:41 +02002287 ret = wl12xx_cmd_role_start_sta(wl, wlvif);
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002288 if (ret < 0)
2289 goto out;
2290
Eliad Pellerba8447f2011-10-10 10:13:00 +02002291 if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002292 goto out;
2293
2294 /*
2295 * The join command disable the keep-alive mode, shut down its process,
2296 * and also clear the template config, so we need to reset it all after
2297 * the join. The acx_aid starts the keep-alive process, and the order
2298 * of the commands below is relevant.
2299 */
Eliad Peller0603d892011-10-05 11:55:51 +02002300 ret = wl1271_acx_keep_alive_mode(wl, wlvif, true);
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002301 if (ret < 0)
2302 goto out;
2303
Eliad Peller0603d892011-10-05 11:55:51 +02002304 ret = wl1271_acx_aid(wl, wlvif, wlvif->aid);
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002305 if (ret < 0)
2306 goto out;
2307
Eliad Pellerd2d66c52011-10-05 11:55:43 +02002308 ret = wl12xx_cmd_build_klv_null_data(wl, wlvif);
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002309 if (ret < 0)
2310 goto out;
2311
Eliad Peller0603d892011-10-05 11:55:51 +02002312 ret = wl1271_acx_keep_alive_config(wl, wlvif,
2313 CMD_TEMPL_KLV_IDX_NULL_DATA,
Juuso Oikarinen82429d32010-04-28 09:50:01 +03002314 ACX_KEEP_ALIVE_TPL_VALID);
2315 if (ret < 0)
2316 goto out;
2317
2318out:
2319 return ret;
2320}
2321
Eliad Peller0603d892011-10-05 11:55:51 +02002322static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02002323{
2324 int ret;
2325
Eliad Peller52630c52011-10-10 10:13:08 +02002326 if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
Shahar Levi6d158ff2011-09-08 13:01:33 +03002327 wl12xx_cmd_stop_channel_switch(wl);
2328 ieee80211_chswitch_done(wl->vif, false);
2329 }
2330
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02002331 /* to stop listening to a channel, we disconnect */
Eliad Peller0603d892011-10-05 11:55:51 +02002332 ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02002333 if (ret < 0)
2334 goto out;
2335
Oz Krakowskib992c682011-06-26 10:36:02 +03002336 /* reset TX security counters on a clean disconnect */
Eliad Peller48e93e42011-10-10 10:12:58 +02002337 wlvif->tx_security_last_seq_lsb = 0;
2338 wlvif->tx_security_seq = 0;
Oz Krakowskib992c682011-06-26 10:36:02 +03002339
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02002340out:
2341 return ret;
2342}
2343
Eliad Peller87fbcb02011-10-05 11:55:41 +02002344static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002345{
Eliad Peller1b92f152011-10-10 10:13:09 +02002346 wlvif->basic_rate_set = wlvif->bitrate_masks[wlvif->band];
Eliad Peller30d0c8f2011-10-05 11:55:42 +02002347 wlvif->rate_set = wlvif->basic_rate_set;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002348}
2349
Eliad Peller251c1772011-08-14 13:17:17 +03002350static bool wl12xx_is_roc(struct wl1271 *wl)
2351{
2352 u8 role_id;
2353
2354 role_id = find_first_bit(wl->roc_map, WL12XX_MAX_ROLES);
2355 if (role_id >= WL12XX_MAX_ROLES)
2356 return false;
2357
2358 return true;
2359}
2360
Eliad Peller87fbcb02011-10-05 11:55:41 +02002361static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
2362 bool idle)
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002363{
2364 int ret;
2365
2366 if (idle) {
Eliad Peller251c1772011-08-14 13:17:17 +03002367 /* no need to croc if we weren't busy (e.g. during boot) */
2368 if (wl12xx_is_roc(wl)) {
Eliad Peller7edebf52011-10-05 11:55:52 +02002369 ret = wl12xx_croc(wl, wlvif->dev_role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03002370 if (ret < 0)
2371 goto out;
2372
Eliad Peller7edebf52011-10-05 11:55:52 +02002373 ret = wl12xx_cmd_role_stop_dev(wl, wlvif);
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002374 if (ret < 0)
2375 goto out;
2376 }
Eliad Peller30d0c8f2011-10-05 11:55:42 +02002377 wlvif->rate_set =
2378 wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
2379 ret = wl1271_acx_sta_rate_policies(wl, wlvif);
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002380 if (ret < 0)
2381 goto out;
2382 ret = wl1271_acx_keep_alive_config(
Eliad Peller0603d892011-10-05 11:55:51 +02002383 wl, wlvif, CMD_TEMPL_KLV_IDX_NULL_DATA,
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002384 ACX_KEEP_ALIVE_TPL_INVALID);
2385 if (ret < 0)
2386 goto out;
2387 set_bit(WL1271_FLAG_IDLE, &wl->flags);
2388 } else {
Luciano Coelho33c2c062011-05-10 14:46:02 +03002389 /* The current firmware only supports sched_scan in idle */
2390 if (wl->sched_scanning) {
2391 wl1271_scan_sched_scan_stop(wl);
2392 ieee80211_sched_scan_stopped(wl->hw);
2393 }
2394
Eliad Peller7edebf52011-10-05 11:55:52 +02002395 ret = wl12xx_cmd_role_start_dev(wl, wlvif);
Eliad Peller251c1772011-08-14 13:17:17 +03002396 if (ret < 0)
2397 goto out;
2398
Eliad Peller1b92f152011-10-10 10:13:09 +02002399 ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id);
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002400 if (ret < 0)
2401 goto out;
2402 clear_bit(WL1271_FLAG_IDLE, &wl->flags);
2403 }
2404
2405out:
2406 return ret;
2407}
2408
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002409static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
2410{
2411 struct wl1271 *wl = hw->priv;
Eliad Peller87fbcb02011-10-05 11:55:41 +02002412 struct ieee80211_vif *vif = wl->vif; /* TODO: reconfig all vifs */
2413 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002414 struct ieee80211_conf *conf = &hw->conf;
2415 int channel, ret = 0;
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002416 bool is_ap;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002417
2418 channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
2419
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002420 wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s"
2421 " changed 0x%x",
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002422 channel,
2423 conf->flags & IEEE80211_CONF_PS ? "on" : "off",
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02002424 conf->power_level,
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002425 conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
2426 changed);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002427
Juuso Oikarinen781608c2010-05-24 11:18:17 +03002428 /*
2429 * mac80211 will go to idle nearly immediately after transmitting some
2430 * frames, such as the deauth. To make sure those frames reach the air,
2431 * wait here until the TX queue is fully flushed.
2432 */
2433 if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
2434 (conf->flags & IEEE80211_CONF_IDLE))
2435 wl1271_tx_flush(wl);
2436
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002437 mutex_lock(&wl->mutex);
2438
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002439 if (unlikely(wl->state == WL1271_STATE_OFF)) {
Arik Nemtsov17e672d2011-03-22 10:07:47 +02002440 /* we support configuring the channel and band while off */
2441 if ((changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
2442 wl->band = conf->channel->band;
2443 wl->channel = channel;
2444 }
2445
Arik Nemtsov097f8822011-06-27 22:06:34 +03002446 if ((changed & IEEE80211_CONF_CHANGE_POWER))
2447 wl->power_level = conf->power_level;
2448
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03002449 goto out;
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002450 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03002451
Eliad Peller536129c2011-10-05 11:55:45 +02002452 is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002453
Ido Yariva6208652011-03-01 15:14:41 +02002454 ret = wl1271_ps_elp_wakeup(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002455 if (ret < 0)
2456 goto out;
2457
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002458 /* if the channel changes while joined, join again */
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002459 if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
Eliad Peller1b92f152011-10-10 10:13:09 +02002460 ((wlvif->band != conf->channel->band) ||
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002461 (wl->channel != channel))) {
Eliad Pellerc6930b02011-09-15 13:00:01 +03002462 /* send all pending packets */
Eliad Pellera32d0cd2011-10-10 10:12:55 +02002463 wl1271_tx_work_locked(wl);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002464 wl->band = conf->channel->band;
Eliad Peller1b92f152011-10-10 10:13:09 +02002465 wlvif->band = conf->channel->band;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002466 wl->channel = channel;
2467
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002468 if (!is_ap) {
2469 /*
2470 * FIXME: the mac80211 should really provide a fixed
2471 * rate to use here. for now, just use the smallest
2472 * possible rate for the band as a fixed rate for
2473 * association frames and other control messages.
2474 */
Eliad Pellerba8447f2011-10-10 10:13:00 +02002475 if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
Eliad Peller87fbcb02011-10-05 11:55:41 +02002476 wl1271_set_band_rate(wl, wlvif);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002477
Eliad Pellerd2d66c52011-10-05 11:55:43 +02002478 wlvif->basic_rate =
Eliad Peller87fbcb02011-10-05 11:55:41 +02002479 wl1271_tx_min_rate_get(wl,
2480 wlvif->basic_rate_set);
Eliad Peller30d0c8f2011-10-05 11:55:42 +02002481 ret = wl1271_acx_sta_rate_policies(wl, wlvif);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002482 if (ret < 0)
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002483 wl1271_warning("rate policy for channel "
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002484 "failed %d", ret);
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002485
Eliad Pellerba8447f2011-10-10 10:13:00 +02002486 if (test_bit(WLVIF_FLAG_STA_ASSOCIATED,
2487 &wlvif->flags)) {
Eliad Peller251c1772011-08-14 13:17:17 +03002488 if (wl12xx_is_roc(wl)) {
2489 /* roaming */
Eliad Peller7edebf52011-10-05 11:55:52 +02002490 ret = wl12xx_croc(wl,
2491 wlvif->dev_role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03002492 if (ret < 0)
2493 goto out_sleep;
2494 }
Eliad Peller87fbcb02011-10-05 11:55:41 +02002495 ret = wl1271_join(wl, wlvif, false);
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002496 if (ret < 0)
2497 wl1271_warning("cmd join on channel "
2498 "failed %d", ret);
Eliad Peller251c1772011-08-14 13:17:17 +03002499 } else {
2500 /*
2501 * change the ROC channel. do it only if we are
2502 * not idle. otherwise, CROC will be called
2503 * anyway.
2504 */
2505 if (wl12xx_is_roc(wl) &&
2506 !(conf->flags & IEEE80211_CONF_IDLE)) {
Eliad Peller7edebf52011-10-05 11:55:52 +02002507 ret = wl12xx_croc(wl,
2508 wlvif->dev_role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03002509 if (ret < 0)
2510 goto out_sleep;
2511
Eliad Peller1b92f152011-10-10 10:13:09 +02002512 ret = wl12xx_roc(wl, wlvif,
Eliad Peller7edebf52011-10-05 11:55:52 +02002513 wlvif->dev_role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03002514 if (ret < 0)
2515 wl1271_warning("roc failed %d",
2516 ret);
2517 }
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002518 }
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002519 }
2520 }
2521
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002522 if (changed & IEEE80211_CONF_CHANGE_IDLE && !is_ap) {
Eliad Peller87fbcb02011-10-05 11:55:41 +02002523 ret = wl1271_sta_handle_idle(wl, wlvif,
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002524 conf->flags & IEEE80211_CONF_IDLE);
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002525 if (ret < 0)
2526 wl1271_warning("idle mode change failed %d", ret);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002527 }
2528
Juuso Oikarinen90494a92010-07-08 17:50:00 +03002529 /*
2530 * if mac80211 changes the PSM mode, make sure the mode is not
2531 * incorrectly changed after the pspoll failure active window.
2532 */
2533 if (changed & IEEE80211_CONF_CHANGE_PS)
Eliad Peller836d6602011-10-10 10:13:07 +02002534 clear_bit(WLVIF_FLAG_PSPOLL_FAILURE, &wlvif->flags);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03002535
Juuso Oikarinen71449f82009-12-11 15:41:07 +02002536 if (conf->flags & IEEE80211_CONF_PS &&
Eliad Pellerc29bb002011-10-10 10:13:03 +02002537 !test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags)) {
2538 set_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002539
2540 /*
2541 * We enter PSM only if we're already associated.
2542 * If we're not, we'll enter it when joining an SSID,
2543 * through the bss_info_changed() hook.
2544 */
Eliad Pellerba8447f2011-10-10 10:13:00 +02002545 if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02002546 wl1271_debug(DEBUG_PSM, "psm enabled");
Eliad Peller0603d892011-10-05 11:55:51 +02002547 ret = wl1271_ps_set_mode(wl, wlvif,
2548 STATION_POWER_SAVE_MODE,
Eliad Pellerd2d66c52011-10-05 11:55:43 +02002549 wlvif->basic_rate, true);
Juuso Oikarinenaf5e0842009-12-11 15:41:00 +02002550 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002551 } else if (!(conf->flags & IEEE80211_CONF_PS) &&
Eliad Pellerc29bb002011-10-10 10:13:03 +02002552 test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02002553 wl1271_debug(DEBUG_PSM, "psm disabled");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002554
Eliad Pellerc29bb002011-10-10 10:13:03 +02002555 clear_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002556
Eliad Pellerc29bb002011-10-10 10:13:03 +02002557 if (test_bit(WLVIF_FLAG_PSM, &wlvif->flags))
Eliad Peller0603d892011-10-05 11:55:51 +02002558 ret = wl1271_ps_set_mode(wl, wlvif,
2559 STATION_ACTIVE_MODE,
Eliad Pellerd2d66c52011-10-05 11:55:43 +02002560 wlvif->basic_rate, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002561 }
2562
2563 if (conf->power_level != wl->power_level) {
Eliad Peller0603d892011-10-05 11:55:51 +02002564 ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002565 if (ret < 0)
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02002566 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002567
2568 wl->power_level = conf->power_level;
2569 }
2570
2571out_sleep:
2572 wl1271_ps_elp_sleep(wl);
2573
2574out:
2575 mutex_unlock(&wl->mutex);
2576
2577 return ret;
2578}
2579
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002580struct wl1271_filter_params {
2581 bool enabled;
2582 int mc_list_length;
2583 u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
2584};
2585
Jiri Pirko22bedad2010-04-01 21:22:57 +00002586static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw,
2587 struct netdev_hw_addr_list *mc_list)
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002588{
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002589 struct wl1271_filter_params *fp;
Jiri Pirko22bedad2010-04-01 21:22:57 +00002590 struct netdev_hw_addr *ha;
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03002591 struct wl1271 *wl = hw->priv;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002592
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03002593 if (unlikely(wl->state == WL1271_STATE_OFF))
2594 return 0;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002595
Juuso Oikarinen74441132009-10-13 12:47:53 +03002596 fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002597 if (!fp) {
2598 wl1271_error("Out of memory setting filters.");
2599 return 0;
2600 }
2601
2602 /* update multicast filtering parameters */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002603 fp->mc_list_length = 0;
Jiri Pirko22bedad2010-04-01 21:22:57 +00002604 if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) {
2605 fp->enabled = false;
2606 } else {
2607 fp->enabled = true;
2608 netdev_hw_addr_list_for_each(ha, mc_list) {
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002609 memcpy(fp->mc_list[fp->mc_list_length],
Jiri Pirko22bedad2010-04-01 21:22:57 +00002610 ha->addr, ETH_ALEN);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002611 fp->mc_list_length++;
Jiri Pirko22bedad2010-04-01 21:22:57 +00002612 }
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002613 }
2614
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002615 return (u64)(unsigned long)fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002616}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002617
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002618#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
2619 FIF_ALLMULTI | \
2620 FIF_FCSFAIL | \
2621 FIF_BCN_PRBRESP_PROMISC | \
2622 FIF_CONTROL | \
2623 FIF_OTHER_BSS)
2624
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002625static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
2626 unsigned int changed,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002627 unsigned int *total, u64 multicast)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002628{
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002629 struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002630 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02002631 struct ieee80211_vif *vif = wl->vif; /* TODO: get as param */
2632 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
2633
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002634 int ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002635
Arik Nemtsov7d057862010-10-16 19:25:35 +02002636 wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter changed %x"
2637 " total %x", changed, *total);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002638
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002639 mutex_lock(&wl->mutex);
2640
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03002641 *total &= WL1271_SUPPORTED_FILTERS;
2642 changed &= WL1271_SUPPORTED_FILTERS;
2643
2644 if (unlikely(wl->state == WL1271_STATE_OFF))
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002645 goto out;
2646
Ido Yariva6208652011-03-01 15:14:41 +02002647 ret = wl1271_ps_elp_wakeup(wl);
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002648 if (ret < 0)
2649 goto out;
2650
Eliad Peller536129c2011-10-05 11:55:45 +02002651 if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
Arik Nemtsov7d057862010-10-16 19:25:35 +02002652 if (*total & FIF_ALLMULTI)
Eliad Peller0603d892011-10-05 11:55:51 +02002653 ret = wl1271_acx_group_address_tbl(wl, wlvif, false,
2654 NULL, 0);
Arik Nemtsov7d057862010-10-16 19:25:35 +02002655 else if (fp)
Eliad Peller0603d892011-10-05 11:55:51 +02002656 ret = wl1271_acx_group_address_tbl(wl, wlvif,
2657 fp->enabled,
Arik Nemtsov7d057862010-10-16 19:25:35 +02002658 fp->mc_list,
2659 fp->mc_list_length);
2660 if (ret < 0)
2661 goto out_sleep;
2662 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002663
Eliad Peller08c1d1c2011-08-14 13:17:04 +03002664 /*
2665 * the fw doesn't provide an api to configure the filters. instead,
2666 * the filters configuration is based on the active roles / ROC
2667 * state.
2668 */
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002669
2670out_sleep:
2671 wl1271_ps_elp_sleep(wl);
2672
2673out:
2674 mutex_unlock(&wl->mutex);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02002675 kfree(fp);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002676}
2677
Eliad Peller170d0e62011-10-05 11:56:06 +02002678static int wl1271_record_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
2679 u8 id, u8 key_type, u8 key_size,
2680 const u8 *key, u8 hlid, u32 tx_seq_32,
2681 u16 tx_seq_16)
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002682{
2683 struct wl1271_ap_key *ap_key;
2684 int i;
2685
2686 wl1271_debug(DEBUG_CRYPT, "record ap key id %d", (int)id);
2687
2688 if (key_size > MAX_KEY_SIZE)
2689 return -EINVAL;
2690
2691 /*
2692 * Find next free entry in ap_keys. Also check we are not replacing
2693 * an existing key.
2694 */
2695 for (i = 0; i < MAX_NUM_KEYS; i++) {
Eliad Peller170d0e62011-10-05 11:56:06 +02002696 if (wlvif->ap.recorded_keys[i] == NULL)
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002697 break;
2698
Eliad Peller170d0e62011-10-05 11:56:06 +02002699 if (wlvif->ap.recorded_keys[i]->id == id) {
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002700 wl1271_warning("trying to record key replacement");
2701 return -EINVAL;
2702 }
2703 }
2704
2705 if (i == MAX_NUM_KEYS)
2706 return -EBUSY;
2707
2708 ap_key = kzalloc(sizeof(*ap_key), GFP_KERNEL);
2709 if (!ap_key)
2710 return -ENOMEM;
2711
2712 ap_key->id = id;
2713 ap_key->key_type = key_type;
2714 ap_key->key_size = key_size;
2715 memcpy(ap_key->key, key, key_size);
2716 ap_key->hlid = hlid;
2717 ap_key->tx_seq_32 = tx_seq_32;
2718 ap_key->tx_seq_16 = tx_seq_16;
2719
Eliad Peller170d0e62011-10-05 11:56:06 +02002720 wlvif->ap.recorded_keys[i] = ap_key;
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002721 return 0;
2722}
2723
Eliad Peller170d0e62011-10-05 11:56:06 +02002724static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002725{
2726 int i;
2727
2728 for (i = 0; i < MAX_NUM_KEYS; i++) {
Eliad Peller170d0e62011-10-05 11:56:06 +02002729 kfree(wlvif->ap.recorded_keys[i]);
2730 wlvif->ap.recorded_keys[i] = NULL;
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002731 }
2732}
2733
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002734static int wl1271_ap_init_hwenc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002735{
2736 int i, ret = 0;
2737 struct wl1271_ap_key *key;
2738 bool wep_key_added = false;
2739
2740 for (i = 0; i < MAX_NUM_KEYS; i++) {
Eliad Peller7f97b482011-08-14 13:17:30 +03002741 u8 hlid;
Eliad Peller170d0e62011-10-05 11:56:06 +02002742 if (wlvif->ap.recorded_keys[i] == NULL)
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002743 break;
2744
Eliad Peller170d0e62011-10-05 11:56:06 +02002745 key = wlvif->ap.recorded_keys[i];
Eliad Peller7f97b482011-08-14 13:17:30 +03002746 hlid = key->hlid;
2747 if (hlid == WL12XX_INVALID_LINK_ID)
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002748 hlid = wlvif->ap.bcast_hlid;
Eliad Peller7f97b482011-08-14 13:17:30 +03002749
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002750 ret = wl1271_cmd_set_ap_key(wl, wlvif, KEY_ADD_OR_REPLACE,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002751 key->id, key->key_type,
2752 key->key_size, key->key,
Eliad Peller7f97b482011-08-14 13:17:30 +03002753 hlid, key->tx_seq_32,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002754 key->tx_seq_16);
2755 if (ret < 0)
2756 goto out;
2757
2758 if (key->key_type == KEY_WEP)
2759 wep_key_added = true;
2760 }
2761
2762 if (wep_key_added) {
Eliad Pellerf75c753f2011-10-05 11:55:59 +02002763 ret = wl12xx_cmd_set_default_wep_key(wl, wlvif->default_key,
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002764 wlvif->ap.bcast_hlid);
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002765 if (ret < 0)
2766 goto out;
2767 }
2768
2769out:
Eliad Peller170d0e62011-10-05 11:56:06 +02002770 wl1271_free_ap_keys(wl, wlvif);
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002771 return ret;
2772}
2773
Eliad Peller536129c2011-10-05 11:55:45 +02002774static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
2775 u16 action, u8 id, u8 key_type,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002776 u8 key_size, const u8 *key, u32 tx_seq_32,
2777 u16 tx_seq_16, struct ieee80211_sta *sta)
2778{
2779 int ret;
Eliad Peller536129c2011-10-05 11:55:45 +02002780 bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002781
2782 if (is_ap) {
2783 struct wl1271_station *wl_sta;
2784 u8 hlid;
2785
2786 if (sta) {
2787 wl_sta = (struct wl1271_station *)sta->drv_priv;
2788 hlid = wl_sta->hlid;
2789 } else {
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002790 hlid = wlvif->ap.bcast_hlid;
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002791 }
2792
Eliad Peller53d40d02011-10-10 10:13:02 +02002793 if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002794 /*
2795 * We do not support removing keys after AP shutdown.
2796 * Pretend we do to make mac80211 happy.
2797 */
2798 if (action != KEY_ADD_OR_REPLACE)
2799 return 0;
2800
Eliad Peller170d0e62011-10-05 11:56:06 +02002801 ret = wl1271_record_ap_key(wl, wlvif, id,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002802 key_type, key_size,
2803 key, hlid, tx_seq_32,
2804 tx_seq_16);
2805 } else {
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002806 ret = wl1271_cmd_set_ap_key(wl, wlvif, action,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002807 id, key_type, key_size,
2808 key, hlid, tx_seq_32,
2809 tx_seq_16);
2810 }
2811
2812 if (ret < 0)
2813 return ret;
2814 } else {
2815 const u8 *addr;
2816 static const u8 bcast_addr[ETH_ALEN] = {
2817 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
2818 };
2819
Guy Eilame9eb8cb2011-08-16 19:49:12 +03002820 /*
2821 * A STA set to GEM cipher requires 2 tx spare blocks.
2822 * Return to default value when GEM cipher key is removed
2823 */
2824 if (key_type == KEY_GEM) {
2825 if (action == KEY_ADD_OR_REPLACE)
2826 wl->tx_spare_blocks = 2;
2827 else if (action == KEY_REMOVE)
2828 wl->tx_spare_blocks = TX_HW_BLOCK_SPARE_DEFAULT;
2829 }
2830
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002831 addr = sta ? sta->addr : bcast_addr;
2832
2833 if (is_zero_ether_addr(addr)) {
2834 /* We dont support TX only encryption */
2835 return -EOPNOTSUPP;
2836 }
2837
2838 /* The wl1271 does not allow to remove unicast keys - they
2839 will be cleared automatically on next CMD_JOIN. Ignore the
2840 request silently, as we dont want the mac80211 to emit
2841 an error message. */
2842 if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr))
2843 return 0;
2844
Eliad Peller010d3d32011-08-14 13:17:31 +03002845 /* don't remove key if hlid was already deleted */
2846 if (action == KEY_REMOVE &&
Eliad Peller154da672011-10-05 11:55:53 +02002847 wlvif->sta.hlid == WL12XX_INVALID_LINK_ID)
Eliad Peller010d3d32011-08-14 13:17:31 +03002848 return 0;
2849
Eliad Pellera8ab39a2011-10-05 11:55:54 +02002850 ret = wl1271_cmd_set_sta_key(wl, wlvif, action,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002851 id, key_type, key_size,
2852 key, addr, tx_seq_32,
2853 tx_seq_16);
2854 if (ret < 0)
2855 return ret;
2856
2857 /* the default WEP key needs to be configured at least once */
2858 if (key_type == KEY_WEP) {
Eliad Pellerc690ec82011-08-14 13:17:07 +03002859 ret = wl12xx_cmd_set_default_wep_key(wl,
Eliad Pellerf75c753f2011-10-05 11:55:59 +02002860 wlvif->default_key,
2861 wlvif->sta.hlid);
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002862 if (ret < 0)
2863 return ret;
2864 }
2865 }
2866
2867 return 0;
2868}
2869
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002870static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
2871 struct ieee80211_vif *vif,
2872 struct ieee80211_sta *sta,
2873 struct ieee80211_key_conf *key_conf)
2874{
2875 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02002876 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002877 int ret;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03002878 u32 tx_seq_32 = 0;
2879 u16 tx_seq_16 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002880 u8 key_type;
2881
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002882 wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
2883
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002884 wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002885 wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
Johannes Berg97359d12010-08-10 09:46:38 +02002886 key_conf->cipher, key_conf->keyidx,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002887 key_conf->keylen, key_conf->flags);
2888 wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
2889
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002890 mutex_lock(&wl->mutex);
2891
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002892 if (unlikely(wl->state == WL1271_STATE_OFF)) {
2893 ret = -EAGAIN;
2894 goto out_unlock;
2895 }
2896
Ido Yariva6208652011-03-01 15:14:41 +02002897 ret = wl1271_ps_elp_wakeup(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002898 if (ret < 0)
2899 goto out_unlock;
2900
Johannes Berg97359d12010-08-10 09:46:38 +02002901 switch (key_conf->cipher) {
2902 case WLAN_CIPHER_SUITE_WEP40:
2903 case WLAN_CIPHER_SUITE_WEP104:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002904 key_type = KEY_WEP;
2905
2906 key_conf->hw_key_idx = key_conf->keyidx;
2907 break;
Johannes Berg97359d12010-08-10 09:46:38 +02002908 case WLAN_CIPHER_SUITE_TKIP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002909 key_type = KEY_TKIP;
2910
2911 key_conf->hw_key_idx = key_conf->keyidx;
Eliad Peller48e93e42011-10-10 10:12:58 +02002912 tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
2913 tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002914 break;
Johannes Berg97359d12010-08-10 09:46:38 +02002915 case WLAN_CIPHER_SUITE_CCMP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002916 key_type = KEY_AES;
2917
2918 key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Eliad Peller48e93e42011-10-10 10:12:58 +02002919 tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
2920 tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002921 break;
Juuso Oikarinen7a557242010-09-27 12:42:07 +02002922 case WL1271_CIPHER_SUITE_GEM:
2923 key_type = KEY_GEM;
Eliad Peller48e93e42011-10-10 10:12:58 +02002924 tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
2925 tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
Juuso Oikarinen7a557242010-09-27 12:42:07 +02002926 break;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002927 default:
Johannes Berg97359d12010-08-10 09:46:38 +02002928 wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002929
2930 ret = -EOPNOTSUPP;
2931 goto out_sleep;
2932 }
2933
2934 switch (cmd) {
2935 case SET_KEY:
Eliad Peller536129c2011-10-05 11:55:45 +02002936 ret = wl1271_set_key(wl, wlvif, KEY_ADD_OR_REPLACE,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002937 key_conf->keyidx, key_type,
2938 key_conf->keylen, key_conf->key,
2939 tx_seq_32, tx_seq_16, sta);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002940 if (ret < 0) {
2941 wl1271_error("Could not add or replace key");
2942 goto out_sleep;
2943 }
2944 break;
2945
2946 case DISABLE_KEY:
Eliad Peller536129c2011-10-05 11:55:45 +02002947 ret = wl1271_set_key(wl, wlvif, KEY_REMOVE,
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002948 key_conf->keyidx, key_type,
2949 key_conf->keylen, key_conf->key,
2950 0, 0, sta);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002951 if (ret < 0) {
2952 wl1271_error("Could not remove key");
2953 goto out_sleep;
2954 }
2955 break;
2956
2957 default:
2958 wl1271_error("Unsupported key cmd 0x%x", cmd);
2959 ret = -EOPNOTSUPP;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002960 break;
2961 }
2962
2963out_sleep:
2964 wl1271_ps_elp_sleep(wl);
2965
2966out_unlock:
2967 mutex_unlock(&wl->mutex);
2968
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002969 return ret;
2970}
2971
2972static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
Johannes Berga060bbf2010-04-27 11:59:34 +02002973 struct ieee80211_vif *vif,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002974 struct cfg80211_scan_request *req)
2975{
2976 struct wl1271 *wl = hw->priv;
Eliad Peller7edebf52011-10-05 11:55:52 +02002977 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
2978
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002979 int ret;
2980 u8 *ssid = NULL;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03002981 size_t len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002982
2983 wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
2984
2985 if (req->n_ssids) {
2986 ssid = req->ssids[0].ssid;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03002987 len = req->ssids[0].ssid_len;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002988 }
2989
2990 mutex_lock(&wl->mutex);
2991
Juuso Oikarinenb739a422010-10-26 13:24:38 +02002992 if (wl->state == WL1271_STATE_OFF) {
2993 /*
2994 * We cannot return -EBUSY here because cfg80211 will expect
2995 * a call to ieee80211_scan_completed if we do - in this case
2996 * there won't be any call.
2997 */
2998 ret = -EAGAIN;
2999 goto out;
3000 }
3001
Ido Yariva6208652011-03-01 15:14:41 +02003002 ret = wl1271_ps_elp_wakeup(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003003 if (ret < 0)
3004 goto out;
3005
Eliad Peller251c1772011-08-14 13:17:17 +03003006 /* cancel ROC before scanning */
3007 if (wl12xx_is_roc(wl)) {
Eliad Pellerba8447f2011-10-10 10:13:00 +02003008 if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
Eliad Peller251c1772011-08-14 13:17:17 +03003009 /* don't allow scanning right now */
3010 ret = -EBUSY;
3011 goto out_sleep;
3012 }
Eliad Peller7edebf52011-10-05 11:55:52 +02003013 wl12xx_croc(wl, wlvif->dev_role_id);
3014 wl12xx_cmd_role_stop_dev(wl, wlvif);
Eliad Peller251c1772011-08-14 13:17:17 +03003015 }
3016
Eliad Peller784f6942011-10-05 11:55:39 +02003017 ret = wl1271_scan(hw->priv, vif, ssid, len, req);
Eliad Peller251c1772011-08-14 13:17:17 +03003018out_sleep:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003019 wl1271_ps_elp_sleep(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003020out:
3021 mutex_unlock(&wl->mutex);
3022
3023 return ret;
3024}
3025
Eliad Peller73ecce32011-06-27 13:06:45 +03003026static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
3027 struct ieee80211_vif *vif)
3028{
3029 struct wl1271 *wl = hw->priv;
3030 int ret;
3031
3032 wl1271_debug(DEBUG_MAC80211, "mac80211 cancel hw scan");
3033
3034 mutex_lock(&wl->mutex);
3035
3036 if (wl->state == WL1271_STATE_OFF)
3037 goto out;
3038
3039 if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
3040 goto out;
3041
3042 ret = wl1271_ps_elp_wakeup(wl);
3043 if (ret < 0)
3044 goto out;
3045
3046 if (wl->scan.state != WL1271_SCAN_STATE_DONE) {
3047 ret = wl1271_scan_stop(wl);
3048 if (ret < 0)
3049 goto out_sleep;
3050 }
3051 wl->scan.state = WL1271_SCAN_STATE_IDLE;
3052 memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
Eliad Peller784f6942011-10-05 11:55:39 +02003053 wl->scan_vif = NULL;
Eliad Peller73ecce32011-06-27 13:06:45 +03003054 wl->scan.req = NULL;
3055 ieee80211_scan_completed(wl->hw, true);
3056
3057out_sleep:
3058 wl1271_ps_elp_sleep(wl);
3059out:
3060 mutex_unlock(&wl->mutex);
3061
3062 cancel_delayed_work_sync(&wl->scan_complete_work);
3063}
3064
Luciano Coelho33c2c062011-05-10 14:46:02 +03003065static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
3066 struct ieee80211_vif *vif,
3067 struct cfg80211_sched_scan_request *req,
3068 struct ieee80211_sched_scan_ies *ies)
3069{
3070 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02003071 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Luciano Coelho33c2c062011-05-10 14:46:02 +03003072 int ret;
3073
3074 wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_start");
3075
3076 mutex_lock(&wl->mutex);
3077
3078 ret = wl1271_ps_elp_wakeup(wl);
3079 if (ret < 0)
3080 goto out;
3081
Eliad Peller536129c2011-10-05 11:55:45 +02003082 ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies);
Luciano Coelho33c2c062011-05-10 14:46:02 +03003083 if (ret < 0)
3084 goto out_sleep;
3085
Eliad Peller536129c2011-10-05 11:55:45 +02003086 ret = wl1271_scan_sched_scan_start(wl, wlvif);
Luciano Coelho33c2c062011-05-10 14:46:02 +03003087 if (ret < 0)
3088 goto out_sleep;
3089
3090 wl->sched_scanning = true;
3091
3092out_sleep:
3093 wl1271_ps_elp_sleep(wl);
3094out:
3095 mutex_unlock(&wl->mutex);
3096 return ret;
3097}
3098
3099static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
3100 struct ieee80211_vif *vif)
3101{
3102 struct wl1271 *wl = hw->priv;
3103 int ret;
3104
3105 wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_stop");
3106
3107 mutex_lock(&wl->mutex);
3108
3109 ret = wl1271_ps_elp_wakeup(wl);
3110 if (ret < 0)
3111 goto out;
3112
3113 wl1271_scan_sched_scan_stop(wl);
3114
3115 wl1271_ps_elp_sleep(wl);
3116out:
3117 mutex_unlock(&wl->mutex);
3118}
3119
Arik Nemtsov68d069c2010-11-08 10:51:07 +01003120static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
3121{
3122 struct wl1271 *wl = hw->priv;
3123 int ret = 0;
3124
3125 mutex_lock(&wl->mutex);
3126
3127 if (unlikely(wl->state == WL1271_STATE_OFF)) {
3128 ret = -EAGAIN;
3129 goto out;
3130 }
3131
Ido Yariva6208652011-03-01 15:14:41 +02003132 ret = wl1271_ps_elp_wakeup(wl);
Arik Nemtsov68d069c2010-11-08 10:51:07 +01003133 if (ret < 0)
3134 goto out;
3135
Arik Nemtsov5f704d12011-04-18 14:15:21 +03003136 ret = wl1271_acx_frag_threshold(wl, value);
Arik Nemtsov68d069c2010-11-08 10:51:07 +01003137 if (ret < 0)
3138 wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret);
3139
3140 wl1271_ps_elp_sleep(wl);
3141
3142out:
3143 mutex_unlock(&wl->mutex);
3144
3145 return ret;
3146}
3147
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003148static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
3149{
3150 struct wl1271 *wl = hw->priv;
Eliad Peller0603d892011-10-05 11:55:51 +02003151 struct ieee80211_vif *vif = wl->vif;
3152 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03003153 int ret = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003154
3155 mutex_lock(&wl->mutex);
3156
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02003157 if (unlikely(wl->state == WL1271_STATE_OFF)) {
3158 ret = -EAGAIN;
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03003159 goto out;
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02003160 }
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03003161
Ido Yariva6208652011-03-01 15:14:41 +02003162 ret = wl1271_ps_elp_wakeup(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003163 if (ret < 0)
3164 goto out;
3165
Eliad Peller0603d892011-10-05 11:55:51 +02003166 ret = wl1271_acx_rts_threshold(wl, wlvif, value);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003167 if (ret < 0)
3168 wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret);
3169
3170 wl1271_ps_elp_sleep(wl);
3171
3172out:
3173 mutex_unlock(&wl->mutex);
3174
3175 return ret;
3176}
3177
Eliad Peller1fe9f162011-10-05 11:55:48 +02003178static int wl1271_ssid_set(struct ieee80211_vif *vif, struct sk_buff *skb,
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003179 int offset)
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02003180{
Eliad Peller1fe9f162011-10-05 11:55:48 +02003181 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Eliad Peller889cb362011-05-01 09:56:45 +03003182 u8 ssid_len;
3183 const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
3184 skb->len - offset);
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02003185
Eliad Peller889cb362011-05-01 09:56:45 +03003186 if (!ptr) {
3187 wl1271_error("No SSID in IEs!");
3188 return -ENOENT;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02003189 }
Arik Nemtsove78a2872010-10-16 19:07:21 +02003190
Eliad Peller889cb362011-05-01 09:56:45 +03003191 ssid_len = ptr[1];
3192 if (ssid_len > IEEE80211_MAX_SSID_LEN) {
3193 wl1271_error("SSID is too long!");
3194 return -EINVAL;
3195 }
3196
Eliad Peller1fe9f162011-10-05 11:55:48 +02003197 wlvif->ssid_len = ssid_len;
3198 memcpy(wlvif->ssid, ptr+2, ssid_len);
Eliad Peller889cb362011-05-01 09:56:45 +03003199 return 0;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02003200}
3201
Eliad Pellerd48055d2011-09-15 12:07:04 +03003202static void wl12xx_remove_ie(struct sk_buff *skb, u8 eid, int ieoffset)
3203{
3204 int len;
3205 const u8 *next, *end = skb->data + skb->len;
3206 u8 *ie = (u8 *)cfg80211_find_ie(eid, skb->data + ieoffset,
3207 skb->len - ieoffset);
3208 if (!ie)
3209 return;
3210 len = ie[1] + 2;
3211 next = ie + len;
3212 memmove(ie, next, end - next);
3213 skb_trim(skb, skb->len - len);
3214}
3215
Eliad Peller26b4bf22011-09-15 12:07:05 +03003216static void wl12xx_remove_vendor_ie(struct sk_buff *skb,
3217 unsigned int oui, u8 oui_type,
3218 int ieoffset)
3219{
3220 int len;
3221 const u8 *next, *end = skb->data + skb->len;
3222 u8 *ie = (u8 *)cfg80211_find_vendor_ie(oui, oui_type,
3223 skb->data + ieoffset,
3224 skb->len - ieoffset);
3225 if (!ie)
3226 return;
3227 len = ie[1] + 2;
3228 next = ie + len;
3229 memmove(ie, next, end - next);
3230 skb_trim(skb, skb->len - len);
3231}
3232
Arik Nemtsov68eaaf62011-09-03 20:22:03 +03003233static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl,
Eliad Peller1fe9f162011-10-05 11:55:48 +02003234 struct ieee80211_vif *vif,
Arik Nemtsov68eaaf62011-09-03 20:22:03 +03003235 u8 *probe_rsp_data,
3236 size_t probe_rsp_len,
3237 u32 rates)
3238{
Eliad Peller1fe9f162011-10-05 11:55:48 +02003239 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
3240 struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
Arik Nemtsov68eaaf62011-09-03 20:22:03 +03003241 u8 probe_rsp_templ[WL1271_CMD_TEMPL_MAX_SIZE];
3242 int ssid_ie_offset, ie_offset, templ_len;
3243 const u8 *ptr;
3244
3245 /* no need to change probe response if the SSID is set correctly */
Eliad Peller1fe9f162011-10-05 11:55:48 +02003246 if (wlvif->ssid_len > 0)
Arik Nemtsov68eaaf62011-09-03 20:22:03 +03003247 return wl1271_cmd_template_set(wl,
3248 CMD_TEMPL_AP_PROBE_RESPONSE,
3249 probe_rsp_data,
3250 probe_rsp_len, 0,
3251 rates);
3252
3253 if (probe_rsp_len + bss_conf->ssid_len > WL1271_CMD_TEMPL_MAX_SIZE) {
3254 wl1271_error("probe_rsp template too big");
3255 return -EINVAL;
3256 }
3257
3258 /* start searching from IE offset */
3259 ie_offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
3260
3261 ptr = cfg80211_find_ie(WLAN_EID_SSID, probe_rsp_data + ie_offset,
3262 probe_rsp_len - ie_offset);
3263 if (!ptr) {
3264 wl1271_error("No SSID in beacon!");
3265 return -EINVAL;
3266 }
3267
3268 ssid_ie_offset = ptr - probe_rsp_data;
3269 ptr += (ptr[1] + 2);
3270
3271 memcpy(probe_rsp_templ, probe_rsp_data, ssid_ie_offset);
3272
3273 /* insert SSID from bss_conf */
3274 probe_rsp_templ[ssid_ie_offset] = WLAN_EID_SSID;
3275 probe_rsp_templ[ssid_ie_offset + 1] = bss_conf->ssid_len;
3276 memcpy(probe_rsp_templ + ssid_ie_offset + 2,
3277 bss_conf->ssid, bss_conf->ssid_len);
3278 templ_len = ssid_ie_offset + 2 + bss_conf->ssid_len;
3279
3280 memcpy(probe_rsp_templ + ssid_ie_offset + 2 + bss_conf->ssid_len,
3281 ptr, probe_rsp_len - (ptr - probe_rsp_data));
3282 templ_len += probe_rsp_len - (ptr - probe_rsp_data);
3283
3284 return wl1271_cmd_template_set(wl,
3285 CMD_TEMPL_AP_PROBE_RESPONSE,
3286 probe_rsp_templ,
3287 templ_len, 0,
3288 rates);
3289}
3290
Arik Nemtsove78a2872010-10-16 19:07:21 +02003291static int wl1271_bss_erp_info_changed(struct wl1271 *wl,
Eliad Peller0603d892011-10-05 11:55:51 +02003292 struct ieee80211_vif *vif,
Arik Nemtsove78a2872010-10-16 19:07:21 +02003293 struct ieee80211_bss_conf *bss_conf,
3294 u32 changed)
3295{
Eliad Peller0603d892011-10-05 11:55:51 +02003296 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003297 int ret = 0;
3298
3299 if (changed & BSS_CHANGED_ERP_SLOT) {
3300 if (bss_conf->use_short_slot)
Eliad Peller0603d892011-10-05 11:55:51 +02003301 ret = wl1271_acx_slot(wl, wlvif, SLOT_TIME_SHORT);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003302 else
Eliad Peller0603d892011-10-05 11:55:51 +02003303 ret = wl1271_acx_slot(wl, wlvif, SLOT_TIME_LONG);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003304 if (ret < 0) {
3305 wl1271_warning("Set slot time failed %d", ret);
3306 goto out;
3307 }
3308 }
3309
3310 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
3311 if (bss_conf->use_short_preamble)
Eliad Peller0603d892011-10-05 11:55:51 +02003312 wl1271_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_SHORT);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003313 else
Eliad Peller0603d892011-10-05 11:55:51 +02003314 wl1271_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_LONG);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003315 }
3316
3317 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
3318 if (bss_conf->use_cts_prot)
Eliad Peller0603d892011-10-05 11:55:51 +02003319 ret = wl1271_acx_cts_protect(wl, wlvif,
3320 CTSPROTECT_ENABLE);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003321 else
Eliad Peller0603d892011-10-05 11:55:51 +02003322 ret = wl1271_acx_cts_protect(wl, wlvif,
3323 CTSPROTECT_DISABLE);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003324 if (ret < 0) {
3325 wl1271_warning("Set ctsprotect failed %d", ret);
3326 goto out;
3327 }
3328 }
3329
3330out:
3331 return ret;
3332}
3333
3334static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
3335 struct ieee80211_vif *vif,
3336 struct ieee80211_bss_conf *bss_conf,
3337 u32 changed)
3338{
Eliad Peller87fbcb02011-10-05 11:55:41 +02003339 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Eliad Peller536129c2011-10-05 11:55:45 +02003340 bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003341 int ret = 0;
3342
3343 if ((changed & BSS_CHANGED_BEACON_INT)) {
3344 wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d",
3345 bss_conf->beacon_int);
3346
Eliad Peller6a899792011-10-05 11:55:58 +02003347 wlvif->beacon_int = bss_conf->beacon_int;
Arik Nemtsove78a2872010-10-16 19:07:21 +02003348 }
3349
3350 if ((changed & BSS_CHANGED_BEACON)) {
3351 struct ieee80211_hdr *hdr;
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03003352 u32 min_rate;
Arik Nemtsove78a2872010-10-16 19:07:21 +02003353 int ieoffset = offsetof(struct ieee80211_mgmt,
3354 u.beacon.variable);
3355 struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
3356 u16 tmpl_id;
3357
3358 if (!beacon)
3359 goto out;
3360
3361 wl1271_debug(DEBUG_MASTER, "beacon updated");
3362
Eliad Peller1fe9f162011-10-05 11:55:48 +02003363 ret = wl1271_ssid_set(vif, beacon, ieoffset);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003364 if (ret < 0) {
3365 dev_kfree_skb(beacon);
3366 goto out;
3367 }
Eliad Peller87fbcb02011-10-05 11:55:41 +02003368 min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003369 tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
3370 CMD_TEMPL_BEACON;
3371 ret = wl1271_cmd_template_set(wl, tmpl_id,
3372 beacon->data,
3373 beacon->len, 0,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03003374 min_rate);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003375 if (ret < 0) {
3376 dev_kfree_skb(beacon);
3377 goto out;
3378 }
3379
Eliad Pellerd48055d2011-09-15 12:07:04 +03003380 /* remove TIM ie from probe response */
3381 wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset);
3382
Eliad Peller26b4bf22011-09-15 12:07:05 +03003383 /*
3384 * remove p2p ie from probe response.
3385 * the fw reponds to probe requests that don't include
3386 * the p2p ie. probe requests with p2p ie will be passed,
3387 * and will be responded by the supplicant (the spec
3388 * forbids including the p2p ie when responding to probe
3389 * requests that didn't include it).
3390 */
3391 wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA,
3392 WLAN_OUI_TYPE_WFA_P2P, ieoffset);
3393
Arik Nemtsove78a2872010-10-16 19:07:21 +02003394 hdr = (struct ieee80211_hdr *) beacon->data;
3395 hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
3396 IEEE80211_STYPE_PROBE_RESP);
Arik Nemtsov68eaaf62011-09-03 20:22:03 +03003397 if (is_ap)
Eliad Peller1fe9f162011-10-05 11:55:48 +02003398 ret = wl1271_ap_set_probe_resp_tmpl(wl, vif,
Arik Nemtsov68eaaf62011-09-03 20:22:03 +03003399 beacon->data,
3400 beacon->len,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03003401 min_rate);
Arik Nemtsov68eaaf62011-09-03 20:22:03 +03003402 else
3403 ret = wl1271_cmd_template_set(wl,
3404 CMD_TEMPL_PROBE_RESPONSE,
3405 beacon->data,
3406 beacon->len, 0,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03003407 min_rate);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003408 dev_kfree_skb(beacon);
3409 if (ret < 0)
3410 goto out;
3411 }
3412
3413out:
3414 return ret;
3415}
3416
3417/* AP mode changes */
3418static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003419 struct ieee80211_vif *vif,
3420 struct ieee80211_bss_conf *bss_conf,
3421 u32 changed)
3422{
Eliad Peller87fbcb02011-10-05 11:55:41 +02003423 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003424 int ret = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003425
Arik Nemtsove78a2872010-10-16 19:07:21 +02003426 if ((changed & BSS_CHANGED_BASIC_RATES)) {
3427 u32 rates = bss_conf->basic_rates;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003428
Eliad Peller87fbcb02011-10-05 11:55:41 +02003429 wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates,
Eliad Peller1b92f152011-10-10 10:13:09 +02003430 wlvif->band);
Eliad Pellerd2d66c52011-10-05 11:55:43 +02003431 wlvif->basic_rate = wl1271_tx_min_rate_get(wl,
Eliad Peller87fbcb02011-10-05 11:55:41 +02003432 wlvif->basic_rate_set);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003433
Eliad Peller87fbcb02011-10-05 11:55:41 +02003434 ret = wl1271_init_ap_rates(wl, wlvif);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003435 if (ret < 0) {
Arik Nemtsov70f47422011-04-18 14:15:25 +03003436 wl1271_error("AP rate policy change failed %d", ret);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003437 goto out;
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02003438 }
Arik Nemtsovc45a85b2011-04-18 14:15:26 +03003439
Eliad Peller784f6942011-10-05 11:55:39 +02003440 ret = wl1271_ap_init_templates(wl, vif);
Arik Nemtsovc45a85b2011-04-18 14:15:26 +03003441 if (ret < 0)
3442 goto out;
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02003443 }
3444
Arik Nemtsove78a2872010-10-16 19:07:21 +02003445 ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed);
3446 if (ret < 0)
3447 goto out;
3448
3449 if ((changed & BSS_CHANGED_BEACON_ENABLED)) {
3450 if (bss_conf->enable_beacon) {
Eliad Peller53d40d02011-10-10 10:13:02 +02003451 if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
Eliad Peller87fbcb02011-10-05 11:55:41 +02003452 ret = wl12xx_cmd_role_start_ap(wl, wlvif);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003453 if (ret < 0)
3454 goto out;
3455
Eliad Pellera8ab39a2011-10-05 11:55:54 +02003456 ret = wl1271_ap_init_hwenc(wl, wlvif);
Arik Nemtsov7f179b42010-10-16 21:39:06 +02003457 if (ret < 0)
3458 goto out;
Arik Nemtsovcf420392011-08-14 13:17:37 +03003459
Eliad Peller53d40d02011-10-10 10:13:02 +02003460 set_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags);
Arik Nemtsovcf420392011-08-14 13:17:37 +03003461 wl1271_debug(DEBUG_AP, "started AP");
Arik Nemtsove78a2872010-10-16 19:07:21 +02003462 }
3463 } else {
Eliad Peller53d40d02011-10-10 10:13:02 +02003464 if (test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
Eliad Peller0603d892011-10-05 11:55:51 +02003465 ret = wl12xx_cmd_role_stop_ap(wl, wlvif);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003466 if (ret < 0)
3467 goto out;
3468
Eliad Peller53d40d02011-10-10 10:13:02 +02003469 clear_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003470 wl1271_debug(DEBUG_AP, "stopped AP");
3471 }
3472 }
3473 }
3474
Eliad Peller0603d892011-10-05 11:55:51 +02003475 ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003476 if (ret < 0)
3477 goto out;
Arik Nemtsov0b932ab2011-08-14 13:17:27 +03003478
3479 /* Handle HT information change */
3480 if ((changed & BSS_CHANGED_HT) &&
3481 (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
Eliad Peller0603d892011-10-05 11:55:51 +02003482 ret = wl1271_acx_set_ht_information(wl, wlvif,
Arik Nemtsov0b932ab2011-08-14 13:17:27 +03003483 bss_conf->ht_operation_mode);
3484 if (ret < 0) {
3485 wl1271_warning("Set ht information failed %d", ret);
3486 goto out;
3487 }
3488 }
3489
Arik Nemtsove78a2872010-10-16 19:07:21 +02003490out:
3491 return;
3492}
3493
3494/* STA/IBSS mode changes */
3495static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
3496 struct ieee80211_vif *vif,
3497 struct ieee80211_bss_conf *bss_conf,
3498 u32 changed)
3499{
Eliad Peller87fbcb02011-10-05 11:55:41 +02003500 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003501 bool do_join = false, set_assoc = false;
Eliad Peller536129c2011-10-05 11:55:45 +02003502 bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
Eliad Peller227e81e2011-08-14 13:17:26 +03003503 bool ibss_joined = false;
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003504 u32 sta_rate_set = 0;
Arik Nemtsove78a2872010-10-16 19:07:21 +02003505 int ret;
Luciano Coelho2d6e4e762011-01-11 19:07:21 +01003506 struct ieee80211_sta *sta;
Arik Nemtsova1008852011-02-12 23:24:20 +02003507 bool sta_exists = false;
3508 struct ieee80211_sta_ht_cap sta_ht_cap;
Arik Nemtsove78a2872010-10-16 19:07:21 +02003509
3510 if (is_ibss) {
3511 ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf,
3512 changed);
3513 if (ret < 0)
3514 goto out;
3515 }
3516
Eliad Peller227e81e2011-08-14 13:17:26 +03003517 if (changed & BSS_CHANGED_IBSS) {
3518 if (bss_conf->ibss_joined) {
Eliad Pellereee514e2011-10-10 10:13:01 +02003519 set_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags);
Eliad Peller227e81e2011-08-14 13:17:26 +03003520 ibss_joined = true;
3521 } else {
Eliad Pellereee514e2011-10-10 10:13:01 +02003522 if (test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED,
3523 &wlvif->flags)) {
Eliad Peller0603d892011-10-05 11:55:51 +02003524 wl1271_unjoin(wl, wlvif);
Eliad Peller7edebf52011-10-05 11:55:52 +02003525 wl12xx_cmd_role_start_dev(wl, wlvif);
Eliad Peller1b92f152011-10-10 10:13:09 +02003526 wl12xx_roc(wl, wlvif, wlvif->dev_role_id);
Eliad Peller227e81e2011-08-14 13:17:26 +03003527 }
3528 }
3529 }
3530
3531 if ((changed & BSS_CHANGED_BEACON_INT) && ibss_joined)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003532 do_join = true;
3533
3534 /* Need to update the SSID (for filtering etc) */
Eliad Peller227e81e2011-08-14 13:17:26 +03003535 if ((changed & BSS_CHANGED_BEACON) && ibss_joined)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003536 do_join = true;
3537
Eliad Peller227e81e2011-08-14 13:17:26 +03003538 if ((changed & BSS_CHANGED_BEACON_ENABLED) && ibss_joined) {
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02003539 wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
3540 bss_conf->enable_beacon ? "enabled" : "disabled");
3541
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02003542 do_join = true;
3543 }
3544
Arik Nemtsove78a2872010-10-16 19:07:21 +02003545 if ((changed & BSS_CHANGED_CQM)) {
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03003546 bool enable = false;
3547 if (bss_conf->cqm_rssi_thold)
3548 enable = true;
Eliad Peller0603d892011-10-05 11:55:51 +02003549 ret = wl1271_acx_rssi_snr_trigger(wl, wlvif, enable,
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03003550 bss_conf->cqm_rssi_thold,
3551 bss_conf->cqm_rssi_hyst);
3552 if (ret < 0)
3553 goto out;
Eliad Peller04324d92011-10-05 11:56:03 +02003554 wlvif->rssi_thold = bss_conf->cqm_rssi_thold;
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03003555 }
3556
Eliad Pellercdf09492011-10-05 11:55:44 +02003557 if (changed & BSS_CHANGED_BSSID)
3558 if (!is_zero_ether_addr(bss_conf->bssid)) {
Eliad Pellerd2d66c52011-10-05 11:55:43 +02003559 ret = wl12xx_cmd_build_null_data(wl, wlvif);
Eliad Pellerfa287b82010-12-26 09:27:50 +01003560 if (ret < 0)
3561 goto out;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02003562
Eliad Peller784f6942011-10-05 11:55:39 +02003563 ret = wl1271_build_qos_null_data(wl, vif);
Eliad Pellerfa287b82010-12-26 09:27:50 +01003564 if (ret < 0)
3565 goto out;
Saravanan Dhanabal141418c2010-04-28 09:50:00 +03003566
Eliad Pellerfa287b82010-12-26 09:27:50 +01003567 /* Need to update the BSSID (for filtering etc) */
3568 do_join = true;
3569 }
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02003570
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03003571 if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) {
3572 rcu_read_lock();
3573 sta = ieee80211_find_sta(vif, bss_conf->bssid);
3574 if (!sta)
3575 goto sta_not_found;
3576
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003577 /* save the supp_rates of the ap */
3578 sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band];
3579 if (sta->ht_cap.ht_supported)
3580 sta_rate_set |=
3581 (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET);
Arik Nemtsova1008852011-02-12 23:24:20 +02003582 sta_ht_cap = sta->ht_cap;
3583 sta_exists = true;
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003584
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03003585sta_not_found:
3586 rcu_read_unlock();
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003587 }
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003588
Arik Nemtsove78a2872010-10-16 19:07:21 +02003589 if ((changed & BSS_CHANGED_ASSOC)) {
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003590 if (bss_conf->assoc) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003591 u32 rates;
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003592 int ieoffset;
Eliad Peller6840e372011-10-05 11:55:50 +02003593 wlvif->aid = bss_conf->aid;
Juuso Oikarinen69e54342010-05-07 11:39:00 +03003594 set_assoc = true;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003595
Eliad Peller74ec8392011-10-05 11:56:02 +02003596 wlvif->ps_poll_failures = 0;
Juuso Oikarinen90494a92010-07-08 17:50:00 +03003597
Luciano Coelhoae751ba2009-10-12 15:08:57 +03003598 /*
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003599 * use basic rates from AP, and determine lowest rate
3600 * to use with control frames.
3601 */
3602 rates = bss_conf->basic_rates;
Eliad Peller87fbcb02011-10-05 11:55:41 +02003603 wlvif->basic_rate_set =
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03003604 wl1271_tx_enabled_rates_get(wl, rates,
Eliad Peller1b92f152011-10-10 10:13:09 +02003605 wlvif->band);
Eliad Pellerd2d66c52011-10-05 11:55:43 +02003606 wlvif->basic_rate =
Eliad Peller87fbcb02011-10-05 11:55:41 +02003607 wl1271_tx_min_rate_get(wl,
3608 wlvif->basic_rate_set);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003609 if (sta_rate_set)
Eliad Peller30d0c8f2011-10-05 11:55:42 +02003610 wlvif->rate_set =
3611 wl1271_tx_enabled_rates_get(wl,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03003612 sta_rate_set,
Eliad Peller1b92f152011-10-10 10:13:09 +02003613 wlvif->band);
Eliad Peller30d0c8f2011-10-05 11:55:42 +02003614 ret = wl1271_acx_sta_rate_policies(wl, wlvif);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003615 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003616 goto out;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003617
3618 /*
Luciano Coelhoae751ba2009-10-12 15:08:57 +03003619 * with wl1271, we don't need to update the
3620 * beacon_int and dtim_period, because the firmware
3621 * updates it by itself when the first beacon is
3622 * received after a join.
3623 */
Eliad Peller6840e372011-10-05 11:55:50 +02003624 ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003625 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003626 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003627
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02003628 /*
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003629 * Get a template for hardware connection maintenance
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02003630 */
Eliad Pellerbddb29b2011-10-05 11:55:49 +02003631 dev_kfree_skb(wlvif->probereq);
3632 wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
Eliad Peller83587502011-10-10 10:12:53 +02003633 wlvif,
Eliad Pellerbddb29b2011-10-05 11:55:49 +02003634 NULL);
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003635 ieoffset = offsetof(struct ieee80211_mgmt,
3636 u.probe_req.variable);
Eliad Pellerbddb29b2011-10-05 11:55:49 +02003637 wl1271_ssid_set(vif, wlvif->probereq, ieoffset);
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02003638
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02003639 /* enable the connection monitoring feature */
Eliad Peller0603d892011-10-05 11:55:51 +02003640 ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003641 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003642 goto out;
Juuso Oikarinend94cd292009-10-08 21:56:25 +03003643 } else {
3644 /* use defaults when not associated */
Eliad Peller30df14d2011-04-05 19:13:28 +03003645 bool was_assoc =
Eliad Pellerba8447f2011-10-10 10:13:00 +02003646 !!test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED,
3647 &wlvif->flags);
Eliad Peller251c1772011-08-14 13:17:17 +03003648 bool was_ifup =
Eliad Peller8181aec2011-10-10 10:13:04 +02003649 !!test_and_clear_bit(WLVIF_FLAG_STA_STATE_SENT,
3650 &wlvif->flags);
Eliad Peller6840e372011-10-05 11:55:50 +02003651 wlvif->aid = 0;
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02003652
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003653 /* free probe-request template */
Eliad Pellerbddb29b2011-10-05 11:55:49 +02003654 dev_kfree_skb(wlvif->probereq);
3655 wlvif->probereq = NULL;
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003656
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03003657 /* re-enable dynamic ps - just in case */
Juuso Oikarinenf532be62010-07-08 17:50:05 +03003658 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03003659
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003660 /* revert back to minimum rates for the current band */
Eliad Peller87fbcb02011-10-05 11:55:41 +02003661 wl1271_set_band_rate(wl, wlvif);
Eliad Pellerd2d66c52011-10-05 11:55:43 +02003662 wlvif->basic_rate =
Eliad Peller87fbcb02011-10-05 11:55:41 +02003663 wl1271_tx_min_rate_get(wl,
3664 wlvif->basic_rate_set);
Eliad Peller30d0c8f2011-10-05 11:55:42 +02003665 ret = wl1271_acx_sta_rate_policies(wl, wlvif);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003666 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003667 goto out;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003668
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02003669 /* disable connection monitor features */
Eliad Peller0603d892011-10-05 11:55:51 +02003670 ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
Juuso Oikarinenc1899552010-03-26 12:53:32 +02003671
3672 /* Disable the keep-alive feature */
Eliad Peller0603d892011-10-05 11:55:51 +02003673 ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02003674 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003675 goto out;
Juuso Oikarinenb84a7d32010-11-22 12:59:08 +02003676
3677 /* restore the bssid filter and go to dummy bssid */
Eliad Peller30df14d2011-04-05 19:13:28 +03003678 if (was_assoc) {
Eliad Peller251c1772011-08-14 13:17:17 +03003679 u32 conf_flags = wl->hw->conf.flags;
3680 /*
3681 * we might have to disable roc, if there was
3682 * no IF_OPER_UP notification.
3683 */
3684 if (!was_ifup) {
Eliad Peller0603d892011-10-05 11:55:51 +02003685 ret = wl12xx_croc(wl, wlvif->role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03003686 if (ret < 0)
3687 goto out;
3688 }
3689 /*
3690 * (we also need to disable roc in case of
3691 * roaming on the same channel. until we will
3692 * have a better flow...)
3693 */
Eliad Peller7edebf52011-10-05 11:55:52 +02003694 if (test_bit(wlvif->dev_role_id, wl->roc_map)) {
3695 ret = wl12xx_croc(wl,
3696 wlvif->dev_role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03003697 if (ret < 0)
3698 goto out;
3699 }
3700
Eliad Peller0603d892011-10-05 11:55:51 +02003701 wl1271_unjoin(wl, wlvif);
Eliad Peller251c1772011-08-14 13:17:17 +03003702 if (!(conf_flags & IEEE80211_CONF_IDLE)) {
Eliad Peller7edebf52011-10-05 11:55:52 +02003703 wl12xx_cmd_role_start_dev(wl, wlvif);
Eliad Peller1b92f152011-10-10 10:13:09 +02003704 wl12xx_roc(wl, wlvif,
3705 wlvif->dev_role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03003706 }
Eliad Peller30df14d2011-04-05 19:13:28 +03003707 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003708 }
3709 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03003710
Eliad Pellerd192d262011-05-24 14:33:08 +03003711 if (changed & BSS_CHANGED_IBSS) {
3712 wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d",
3713 bss_conf->ibss_joined);
3714
3715 if (bss_conf->ibss_joined) {
3716 u32 rates = bss_conf->basic_rates;
Eliad Peller87fbcb02011-10-05 11:55:41 +02003717 wlvif->basic_rate_set =
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03003718 wl1271_tx_enabled_rates_get(wl, rates,
Eliad Peller1b92f152011-10-10 10:13:09 +02003719 wlvif->band);
Eliad Pellerd2d66c52011-10-05 11:55:43 +02003720 wlvif->basic_rate =
Eliad Peller87fbcb02011-10-05 11:55:41 +02003721 wl1271_tx_min_rate_get(wl,
3722 wlvif->basic_rate_set);
Eliad Pellerd192d262011-05-24 14:33:08 +03003723
Shahar Levi06b660e2011-09-05 13:54:36 +03003724 /* by default, use 11b + OFDM rates */
Eliad Peller30d0c8f2011-10-05 11:55:42 +02003725 wlvif->rate_set = CONF_TX_IBSS_DEFAULT_RATES;
3726 ret = wl1271_acx_sta_rate_policies(wl, wlvif);
Eliad Pellerd192d262011-05-24 14:33:08 +03003727 if (ret < 0)
3728 goto out;
3729 }
3730 }
3731
Eliad Peller0603d892011-10-05 11:55:51 +02003732 ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003733 if (ret < 0)
3734 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003735
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03003736 if (changed & BSS_CHANGED_ARP_FILTER) {
3737 __be32 addr = bss_conf->arp_addr_list[0];
Eliad Peller536129c2011-10-05 11:55:45 +02003738 WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS);
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03003739
Eliad Pellerc5312772010-12-09 11:31:27 +02003740 if (bss_conf->arp_addr_cnt == 1 &&
3741 bss_conf->arp_filter_enabled) {
3742 /*
3743 * The template should have been configured only upon
3744 * association. however, it seems that the correct ip
3745 * isn't being set (when sending), so we have to
3746 * reconfigure the template upon every ip change.
3747 */
Eliad Pellerd2d66c52011-10-05 11:55:43 +02003748 ret = wl1271_cmd_build_arp_rsp(wl, wlvif, addr);
Eliad Pellerc5312772010-12-09 11:31:27 +02003749 if (ret < 0) {
3750 wl1271_warning("build arp rsp failed: %d", ret);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003751 goto out;
Eliad Pellerc5312772010-12-09 11:31:27 +02003752 }
3753
Eliad Peller0603d892011-10-05 11:55:51 +02003754 ret = wl1271_acx_arp_ip_filter(wl, wlvif,
Eliad Pellere5e2f242011-01-24 19:19:03 +01003755 ACX_ARP_FILTER_ARP_FILTERING,
Eliad Pellerc5312772010-12-09 11:31:27 +02003756 addr);
3757 } else
Eliad Peller0603d892011-10-05 11:55:51 +02003758 ret = wl1271_acx_arp_ip_filter(wl, wlvif, 0, addr);
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03003759
3760 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003761 goto out;
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03003762 }
3763
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02003764 if (do_join) {
Eliad Peller87fbcb02011-10-05 11:55:41 +02003765 ret = wl1271_join(wl, wlvif, set_assoc);
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02003766 if (ret < 0) {
3767 wl1271_warning("cmd join failed %d", ret);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003768 goto out;
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02003769 }
Eliad Peller251c1772011-08-14 13:17:17 +03003770
3771 /* ROC until connected (after EAPOL exchange) */
3772 if (!is_ibss) {
Eliad Peller1b92f152011-10-10 10:13:09 +02003773 ret = wl12xx_roc(wl, wlvif, wlvif->role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03003774 if (ret < 0)
3775 goto out;
3776
Eliad Pellerba8447f2011-10-10 10:13:00 +02003777 wl1271_check_operstate(wl, wlvif,
Eliad Peller251c1772011-08-14 13:17:17 +03003778 ieee80211_get_operstate(vif));
3779 }
3780 /*
3781 * stop device role if started (we might already be in
3782 * STA role). TODO: make it better.
3783 */
Eliad Peller7edebf52011-10-05 11:55:52 +02003784 if (wlvif->dev_role_id != WL12XX_INVALID_ROLE_ID) {
3785 ret = wl12xx_croc(wl, wlvif->dev_role_id);
Eliad Peller251c1772011-08-14 13:17:17 +03003786 if (ret < 0)
3787 goto out;
3788
Eliad Peller7edebf52011-10-05 11:55:52 +02003789 ret = wl12xx_cmd_role_stop_dev(wl, wlvif);
Eliad Peller251c1772011-08-14 13:17:17 +03003790 if (ret < 0)
3791 goto out;
3792 }
Eliad Peller05dba352011-08-23 16:37:01 +03003793
3794 /* If we want to go in PSM but we're not there yet */
Eliad Pellerc29bb002011-10-10 10:13:03 +02003795 if (test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags) &&
3796 !test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) {
Eliad Peller05dba352011-08-23 16:37:01 +03003797 enum wl1271_cmd_ps_mode mode;
3798
3799 mode = STATION_POWER_SAVE_MODE;
Eliad Peller0603d892011-10-05 11:55:51 +02003800 ret = wl1271_ps_set_mode(wl, wlvif, mode,
Eliad Pellerd2d66c52011-10-05 11:55:43 +02003801 wlvif->basic_rate,
Eliad Peller05dba352011-08-23 16:37:01 +03003802 true);
3803 if (ret < 0)
3804 goto out;
3805 }
Juuso Oikarinenc1899552010-03-26 12:53:32 +02003806 }
3807
Arik Nemtsov0b932ab2011-08-14 13:17:27 +03003808 /* Handle new association with HT. Do this after join. */
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03003809 if (sta_exists) {
3810 if ((changed & BSS_CHANGED_HT) &&
3811 (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
Arik Nemtsov0b932ab2011-08-14 13:17:27 +03003812 ret = wl1271_acx_set_ht_capabilities(wl,
3813 &sta_ht_cap,
3814 true,
Eliad Peller154da672011-10-05 11:55:53 +02003815 wlvif->sta.hlid);
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03003816 if (ret < 0) {
3817 wl1271_warning("Set ht cap true failed %d",
3818 ret);
3819 goto out;
3820 }
3821 }
3822 /* handle new association without HT and disassociation */
3823 else if (changed & BSS_CHANGED_ASSOC) {
Arik Nemtsov0b932ab2011-08-14 13:17:27 +03003824 ret = wl1271_acx_set_ht_capabilities(wl,
3825 &sta_ht_cap,
3826 false,
Eliad Peller154da672011-10-05 11:55:53 +02003827 wlvif->sta.hlid);
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03003828 if (ret < 0) {
3829 wl1271_warning("Set ht cap false failed %d",
3830 ret);
3831 goto out;
3832 }
3833 }
3834 }
3835
Arik Nemtsov0b932ab2011-08-14 13:17:27 +03003836 /* Handle HT information change. Done after join. */
3837 if ((changed & BSS_CHANGED_HT) &&
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03003838 (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
Eliad Peller0603d892011-10-05 11:55:51 +02003839 ret = wl1271_acx_set_ht_information(wl, wlvif,
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03003840 bss_conf->ht_operation_mode);
3841 if (ret < 0) {
3842 wl1271_warning("Set ht information failed %d", ret);
3843 goto out;
3844 }
3845 }
3846
Arik Nemtsove78a2872010-10-16 19:07:21 +02003847out:
3848 return;
3849}
3850
3851static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
3852 struct ieee80211_vif *vif,
3853 struct ieee80211_bss_conf *bss_conf,
3854 u32 changed)
3855{
3856 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02003857 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
3858 bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003859 int ret;
3860
3861 wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
3862 (int)changed);
3863
3864 mutex_lock(&wl->mutex);
3865
3866 if (unlikely(wl->state == WL1271_STATE_OFF))
3867 goto out;
3868
Eliad Peller10c8cd02011-10-10 10:13:06 +02003869 if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
3870 goto out;
3871
Ido Yariva6208652011-03-01 15:14:41 +02003872 ret = wl1271_ps_elp_wakeup(wl);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003873 if (ret < 0)
3874 goto out;
3875
3876 if (is_ap)
3877 wl1271_bss_info_changed_ap(wl, vif, bss_conf, changed);
3878 else
3879 wl1271_bss_info_changed_sta(wl, vif, bss_conf, changed);
3880
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003881 wl1271_ps_elp_sleep(wl);
3882
3883out:
3884 mutex_unlock(&wl->mutex);
3885}
3886
Eliad Peller8a3a3c82011-10-02 10:15:52 +02003887static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
3888 struct ieee80211_vif *vif, u16 queue,
Kalle Valoc6999d82010-02-18 13:25:41 +02003889 const struct ieee80211_tx_queue_params *params)
3890{
3891 struct wl1271 *wl = hw->priv;
Eliad Peller0603d892011-10-05 11:55:51 +02003892 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Kalle Valo4695dc92010-03-18 12:26:38 +02003893 u8 ps_scheme;
Arik Nemtsov488fc542010-10-16 20:33:45 +02003894 int ret = 0;
Kalle Valoc6999d82010-02-18 13:25:41 +02003895
3896 mutex_lock(&wl->mutex);
3897
3898 wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
3899
Kalle Valo4695dc92010-03-18 12:26:38 +02003900 if (params->uapsd)
3901 ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
3902 else
3903 ps_scheme = CONF_PS_SCHEME_LEGACY;
3904
Arik Nemtsov488fc542010-10-16 20:33:45 +02003905 if (wl->state == WL1271_STATE_OFF) {
3906 /*
3907 * If the state is off, the parameters will be recorded and
3908 * configured on init. This happens in AP-mode.
3909 */
3910 struct conf_tx_ac_category *conf_ac =
3911 &wl->conf.tx.ac_conf[wl1271_tx_get_queue(queue)];
3912 struct conf_tx_tid *conf_tid =
3913 &wl->conf.tx.tid_conf[wl1271_tx_get_queue(queue)];
3914
3915 conf_ac->ac = wl1271_tx_get_queue(queue);
3916 conf_ac->cw_min = (u8)params->cw_min;
3917 conf_ac->cw_max = params->cw_max;
3918 conf_ac->aifsn = params->aifs;
3919 conf_ac->tx_op_limit = params->txop << 5;
3920
3921 conf_tid->queue_id = wl1271_tx_get_queue(queue);
3922 conf_tid->channel_type = CONF_CHANNEL_TYPE_EDCF;
3923 conf_tid->tsid = wl1271_tx_get_queue(queue);
3924 conf_tid->ps_scheme = ps_scheme;
3925 conf_tid->ack_policy = CONF_ACK_POLICY_LEGACY;
3926 conf_tid->apsd_conf[0] = 0;
3927 conf_tid->apsd_conf[1] = 0;
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003928 goto out;
3929 }
Arik Nemtsov488fc542010-10-16 20:33:45 +02003930
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003931 ret = wl1271_ps_elp_wakeup(wl);
3932 if (ret < 0)
3933 goto out;
Arik Nemtsov488fc542010-10-16 20:33:45 +02003934
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003935 /*
3936 * the txop is confed in units of 32us by the mac80211,
3937 * we need us
3938 */
Eliad Peller0603d892011-10-05 11:55:51 +02003939 ret = wl1271_acx_ac_cfg(wl, wlvif, wl1271_tx_get_queue(queue),
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003940 params->cw_min, params->cw_max,
3941 params->aifs, params->txop << 5);
3942 if (ret < 0)
3943 goto out_sleep;
3944
Eliad Peller0603d892011-10-05 11:55:51 +02003945 ret = wl1271_acx_tid_cfg(wl, wlvif, wl1271_tx_get_queue(queue),
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003946 CONF_CHANNEL_TYPE_EDCF,
3947 wl1271_tx_get_queue(queue),
3948 ps_scheme, CONF_ACK_POLICY_LEGACY,
3949 0, 0);
Kalle Valoc82c1dd2010-02-18 13:25:47 +02003950
3951out_sleep:
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003952 wl1271_ps_elp_sleep(wl);
Kalle Valoc6999d82010-02-18 13:25:41 +02003953
3954out:
3955 mutex_unlock(&wl->mutex);
3956
3957 return ret;
3958}
3959
Eliad Peller37a41b42011-09-21 14:06:11 +03003960static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw,
3961 struct ieee80211_vif *vif)
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03003962{
3963
3964 struct wl1271 *wl = hw->priv;
3965 u64 mactime = ULLONG_MAX;
3966 int ret;
3967
3968 wl1271_debug(DEBUG_MAC80211, "mac80211 get tsf");
3969
3970 mutex_lock(&wl->mutex);
3971
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02003972 if (unlikely(wl->state == WL1271_STATE_OFF))
3973 goto out;
3974
Ido Yariva6208652011-03-01 15:14:41 +02003975 ret = wl1271_ps_elp_wakeup(wl);
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03003976 if (ret < 0)
3977 goto out;
3978
3979 ret = wl1271_acx_tsf_info(wl, &mactime);
3980 if (ret < 0)
3981 goto out_sleep;
3982
3983out_sleep:
3984 wl1271_ps_elp_sleep(wl);
3985
3986out:
3987 mutex_unlock(&wl->mutex);
3988 return mactime;
3989}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003990
John W. Linvilleece550d2010-07-28 16:41:06 -04003991static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
3992 struct survey_info *survey)
3993{
3994 struct wl1271 *wl = hw->priv;
3995 struct ieee80211_conf *conf = &hw->conf;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02003996
John W. Linvilleece550d2010-07-28 16:41:06 -04003997 if (idx != 0)
3998 return -ENOENT;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02003999
John W. Linvilleece550d2010-07-28 16:41:06 -04004000 survey->channel = conf->channel;
4001 survey->filled = SURVEY_INFO_NOISE_DBM;
4002 survey->noise = wl->noise;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02004003
John W. Linvilleece550d2010-07-28 16:41:06 -04004004 return 0;
4005}
4006
Arik Nemtsov409622e2011-02-23 00:22:29 +02004007static int wl1271_allocate_sta(struct wl1271 *wl,
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004008 struct wl12xx_vif *wlvif,
4009 struct ieee80211_sta *sta)
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004010{
4011 struct wl1271_station *wl_sta;
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004012 int ret;
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004013
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004014
4015 if (wl->active_sta_count >= AP_MAX_STATIONS) {
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004016 wl1271_warning("could not allocate HLID - too much stations");
4017 return -EBUSY;
4018 }
4019
4020 wl_sta = (struct wl1271_station *)sta->drv_priv;
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004021 ret = wl12xx_allocate_link(wl, wlvif, &wl_sta->hlid);
4022 if (ret < 0) {
4023 wl1271_warning("could not allocate HLID - too many links");
4024 return -EBUSY;
4025 }
4026
4027 set_bit(wl_sta->hlid, wlvif->ap.sta_hlid_map);
Arik Nemtsovb622d992011-02-23 00:22:31 +02004028 memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN);
Arik Nemtsovda032092011-08-25 12:43:15 +03004029 wl->active_sta_count++;
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004030 return 0;
4031}
4032
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004033void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004034{
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004035 if (!test_bit(hlid, wlvif->ap.sta_hlid_map))
Arik Nemtsovf1acea92011-08-25 12:43:17 +03004036 return;
4037
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004038 clear_bit(hlid, wlvif->ap.sta_hlid_map);
Arik Nemtsovb622d992011-02-23 00:22:31 +02004039 memset(wl->links[hlid].addr, 0, ETH_ALEN);
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03004040 wl->links[hlid].ba_bitmap = 0;
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02004041 wl1271_tx_reset_link_queues(wl, hlid);
Arik Nemtsovb622d992011-02-23 00:22:31 +02004042 __clear_bit(hlid, &wl->ap_ps_map);
4043 __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004044 wl12xx_free_link(wl, wlvif, &hlid);
Arik Nemtsovda032092011-08-25 12:43:15 +03004045 wl->active_sta_count--;
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004046}
4047
4048static int wl1271_op_sta_add(struct ieee80211_hw *hw,
4049 struct ieee80211_vif *vif,
4050 struct ieee80211_sta *sta)
4051{
4052 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02004053 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004054 struct wl1271_station *wl_sta;
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004055 int ret = 0;
4056 u8 hlid;
4057
4058 mutex_lock(&wl->mutex);
4059
4060 if (unlikely(wl->state == WL1271_STATE_OFF))
4061 goto out;
4062
Eliad Peller536129c2011-10-05 11:55:45 +02004063 if (wlvif->bss_type != BSS_TYPE_AP_BSS)
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004064 goto out;
4065
4066 wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);
4067
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004068 ret = wl1271_allocate_sta(wl, wlvif, sta);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004069 if (ret < 0)
4070 goto out;
4071
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004072 wl_sta = (struct wl1271_station *)sta->drv_priv;
4073 hlid = wl_sta->hlid;
4074
Ido Yariva6208652011-03-01 15:14:41 +02004075 ret = wl1271_ps_elp_wakeup(wl);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004076 if (ret < 0)
Arik Nemtsov409622e2011-02-23 00:22:29 +02004077 goto out_free_sta;
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004078
Eliad Peller1b92f152011-10-10 10:13:09 +02004079 ret = wl12xx_cmd_add_peer(wl, wlvif, sta, hlid);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004080 if (ret < 0)
4081 goto out_sleep;
4082
Eliad Pellerb67476e2011-08-14 13:17:23 +03004083 ret = wl12xx_cmd_set_peer_state(wl, hlid);
4084 if (ret < 0)
4085 goto out_sleep;
4086
Arik Nemtsov0b932ab2011-08-14 13:17:27 +03004087 ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true, hlid);
4088 if (ret < 0)
4089 goto out_sleep;
4090
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004091out_sleep:
4092 wl1271_ps_elp_sleep(wl);
4093
Arik Nemtsov409622e2011-02-23 00:22:29 +02004094out_free_sta:
4095 if (ret < 0)
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004096 wl1271_free_sta(wl, wlvif, hlid);
Arik Nemtsov409622e2011-02-23 00:22:29 +02004097
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004098out:
4099 mutex_unlock(&wl->mutex);
4100 return ret;
4101}
4102
4103static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
4104 struct ieee80211_vif *vif,
4105 struct ieee80211_sta *sta)
4106{
4107 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02004108 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004109 struct wl1271_station *wl_sta;
4110 int ret = 0, id;
4111
4112 mutex_lock(&wl->mutex);
4113
4114 if (unlikely(wl->state == WL1271_STATE_OFF))
4115 goto out;
4116
Eliad Peller536129c2011-10-05 11:55:45 +02004117 if (wlvif->bss_type != BSS_TYPE_AP_BSS)
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004118 goto out;
4119
4120 wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
4121
4122 wl_sta = (struct wl1271_station *)sta->drv_priv;
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004123 id = wl_sta->hlid;
4124 if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map)))
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004125 goto out;
4126
Ido Yariva6208652011-03-01 15:14:41 +02004127 ret = wl1271_ps_elp_wakeup(wl);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004128 if (ret < 0)
4129 goto out;
4130
Eliad Pellerc690ec82011-08-14 13:17:07 +03004131 ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004132 if (ret < 0)
4133 goto out_sleep;
4134
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004135 wl1271_free_sta(wl, wlvif, wl_sta->hlid);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004136
4137out_sleep:
4138 wl1271_ps_elp_sleep(wl);
4139
4140out:
4141 mutex_unlock(&wl->mutex);
4142 return ret;
4143}
4144
Luciano Coelho4623ec72011-03-21 19:26:41 +02004145static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
4146 struct ieee80211_vif *vif,
4147 enum ieee80211_ampdu_mlme_action action,
4148 struct ieee80211_sta *sta, u16 tid, u16 *ssn,
4149 u8 buf_size)
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004150{
4151 struct wl1271 *wl = hw->priv;
Eliad Peller536129c2011-10-05 11:55:45 +02004152 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004153 int ret;
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03004154 u8 hlid, *ba_bitmap;
4155
4156 wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu action %d tid %d", action,
4157 tid);
4158
4159 /* sanity check - the fields in FW are only 8bits wide */
4160 if (WARN_ON(tid > 0xFF))
4161 return -ENOTSUPP;
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004162
4163 mutex_lock(&wl->mutex);
4164
4165 if (unlikely(wl->state == WL1271_STATE_OFF)) {
4166 ret = -EAGAIN;
4167 goto out;
4168 }
4169
Eliad Peller536129c2011-10-05 11:55:45 +02004170 if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
Eliad Peller154da672011-10-05 11:55:53 +02004171 hlid = wlvif->sta.hlid;
Eliad Pellerd0802ab2011-10-05 11:56:04 +02004172 ba_bitmap = &wlvif->sta.ba_rx_bitmap;
Eliad Peller536129c2011-10-05 11:55:45 +02004173 } else if (wlvif->bss_type == BSS_TYPE_AP_BSS) {
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03004174 struct wl1271_station *wl_sta;
4175
4176 wl_sta = (struct wl1271_station *)sta->drv_priv;
4177 hlid = wl_sta->hlid;
4178 ba_bitmap = &wl->links[hlid].ba_bitmap;
4179 } else {
4180 ret = -EINVAL;
4181 goto out;
4182 }
4183
Ido Yariva6208652011-03-01 15:14:41 +02004184 ret = wl1271_ps_elp_wakeup(wl);
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004185 if (ret < 0)
4186 goto out;
4187
Shahar Levi70559a02011-05-22 16:10:22 +03004188 wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu: Rx tid %d action %d",
4189 tid, action);
4190
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004191 switch (action) {
4192 case IEEE80211_AMPDU_RX_START:
Eliad Pellerd0802ab2011-10-05 11:56:04 +02004193 if (!wlvif->ba_support || !wlvif->ba_allowed) {
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004194 ret = -ENOTSUPP;
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03004195 break;
4196 }
4197
4198 if (wl->ba_rx_session_count >= RX_BA_MAX_SESSIONS) {
4199 ret = -EBUSY;
4200 wl1271_error("exceeded max RX BA sessions");
4201 break;
4202 }
4203
4204 if (*ba_bitmap & BIT(tid)) {
4205 ret = -EINVAL;
4206 wl1271_error("cannot enable RX BA session on active "
4207 "tid: %d", tid);
4208 break;
4209 }
4210
4211 ret = wl12xx_acx_set_ba_receiver_session(wl, tid, *ssn, true,
4212 hlid);
4213 if (!ret) {
4214 *ba_bitmap |= BIT(tid);
4215 wl->ba_rx_session_count++;
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004216 }
4217 break;
4218
4219 case IEEE80211_AMPDU_RX_STOP:
Arik Nemtsov0f9c8252011-08-17 10:45:49 +03004220 if (!(*ba_bitmap & BIT(tid))) {
4221 ret = -EINVAL;
4222 wl1271_error("no active RX BA session on tid: %d",
4223 tid);
4224 break;
4225 }
4226
4227 ret = wl12xx_acx_set_ba_receiver_session(wl, tid, 0, false,
4228 hlid);
4229 if (!ret) {
4230 *ba_bitmap &= ~BIT(tid);
4231 wl->ba_rx_session_count--;
4232 }
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004233 break;
4234
4235 /*
4236 * The BA initiator session management in FW independently.
4237 * Falling break here on purpose for all TX APDU commands.
4238 */
4239 case IEEE80211_AMPDU_TX_START:
4240 case IEEE80211_AMPDU_TX_STOP:
4241 case IEEE80211_AMPDU_TX_OPERATIONAL:
4242 ret = -EINVAL;
4243 break;
4244
4245 default:
4246 wl1271_error("Incorrect ampdu action id=%x\n", action);
4247 ret = -EINVAL;
4248 }
4249
4250 wl1271_ps_elp_sleep(wl);
4251
4252out:
4253 mutex_unlock(&wl->mutex);
4254
4255 return ret;
4256}
4257
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03004258static int wl12xx_set_bitrate_mask(struct ieee80211_hw *hw,
4259 struct ieee80211_vif *vif,
4260 const struct cfg80211_bitrate_mask *mask)
4261{
Eliad Peller83587502011-10-10 10:12:53 +02004262 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03004263 struct wl1271 *wl = hw->priv;
4264 int i;
4265
4266 wl1271_debug(DEBUG_MAC80211, "mac80211 set_bitrate_mask 0x%x 0x%x",
4267 mask->control[NL80211_BAND_2GHZ].legacy,
4268 mask->control[NL80211_BAND_5GHZ].legacy);
4269
4270 mutex_lock(&wl->mutex);
4271
4272 for (i = 0; i < IEEE80211_NUM_BANDS; i++)
Eliad Peller83587502011-10-10 10:12:53 +02004273 wlvif->bitrate_masks[i] =
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03004274 wl1271_tx_enabled_rates_get(wl,
4275 mask->control[i].legacy,
4276 i);
4277 mutex_unlock(&wl->mutex);
4278
4279 return 0;
4280}
4281
Shahar Levi6d158ff2011-09-08 13:01:33 +03004282static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
4283 struct ieee80211_channel_switch *ch_switch)
4284{
4285 struct wl1271 *wl = hw->priv;
Eliad Peller52630c52011-10-10 10:13:08 +02004286 struct wl12xx_vif *wlvif;
Shahar Levi6d158ff2011-09-08 13:01:33 +03004287 int ret;
4288
4289 wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch");
4290
4291 mutex_lock(&wl->mutex);
4292
4293 if (unlikely(wl->state == WL1271_STATE_OFF)) {
4294 mutex_unlock(&wl->mutex);
4295 ieee80211_chswitch_done(wl->vif, false);
4296 return;
4297 }
4298
4299 ret = wl1271_ps_elp_wakeup(wl);
4300 if (ret < 0)
4301 goto out;
4302
Eliad Peller52630c52011-10-10 10:13:08 +02004303 /* TODO: change mac80211 to pass vif as param */
4304 wl12xx_for_each_wlvif_sta(wl, wlvif) {
4305 ret = wl12xx_cmd_channel_switch(wl, ch_switch);
Shahar Levi6d158ff2011-09-08 13:01:33 +03004306
Eliad Peller52630c52011-10-10 10:13:08 +02004307 if (!ret)
4308 set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
4309 }
Shahar Levi6d158ff2011-09-08 13:01:33 +03004310
4311 wl1271_ps_elp_sleep(wl);
4312
4313out:
4314 mutex_unlock(&wl->mutex);
4315}
4316
Arik Nemtsov33437892011-04-26 23:35:39 +03004317static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
4318{
4319 struct wl1271 *wl = hw->priv;
4320 bool ret = false;
4321
4322 mutex_lock(&wl->mutex);
4323
4324 if (unlikely(wl->state == WL1271_STATE_OFF))
4325 goto out;
4326
4327 /* packets are considered pending if in the TX queue or the FW */
Arik Nemtsovf1a46382011-07-07 14:25:23 +03004328 ret = (wl1271_tx_total_queue_count(wl) > 0) || (wl->tx_frames_cnt > 0);
Arik Nemtsov33437892011-04-26 23:35:39 +03004329out:
4330 mutex_unlock(&wl->mutex);
4331
4332 return ret;
4333}
4334
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004335/* can't be const, mac80211 writes to this */
4336static struct ieee80211_rate wl1271_rates[] = {
4337 { .bitrate = 10,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004338 .hw_value = CONF_HW_BIT_RATE_1MBPS,
4339 .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004340 { .bitrate = 20,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004341 .hw_value = CONF_HW_BIT_RATE_2MBPS,
4342 .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004343 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
4344 { .bitrate = 55,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004345 .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
4346 .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004347 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
4348 { .bitrate = 110,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004349 .hw_value = CONF_HW_BIT_RATE_11MBPS,
4350 .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004351 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
4352 { .bitrate = 60,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004353 .hw_value = CONF_HW_BIT_RATE_6MBPS,
4354 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004355 { .bitrate = 90,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004356 .hw_value = CONF_HW_BIT_RATE_9MBPS,
4357 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004358 { .bitrate = 120,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004359 .hw_value = CONF_HW_BIT_RATE_12MBPS,
4360 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004361 { .bitrate = 180,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004362 .hw_value = CONF_HW_BIT_RATE_18MBPS,
4363 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004364 { .bitrate = 240,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004365 .hw_value = CONF_HW_BIT_RATE_24MBPS,
4366 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004367 { .bitrate = 360,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004368 .hw_value = CONF_HW_BIT_RATE_36MBPS,
4369 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004370 { .bitrate = 480,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004371 .hw_value = CONF_HW_BIT_RATE_48MBPS,
4372 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004373 { .bitrate = 540,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03004374 .hw_value = CONF_HW_BIT_RATE_54MBPS,
4375 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004376};
4377
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01004378/* can't be const, mac80211 writes to this */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004379static struct ieee80211_channel wl1271_channels[] = {
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02004380 { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02004381 { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01004382 { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
4383 { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
4384 { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02004385 { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01004386 { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
4387 { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
4388 { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02004389 { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01004390 { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
4391 { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
4392 { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
Arik Nemtsov6c89b7b2011-01-18 20:39:52 +01004393 { .hw_value = 14, .center_freq = 2484, .max_power = 25 },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004394};
4395
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004396/* mapping to indexes for wl1271_rates */
Tobias Klausera0ea9492010-05-20 10:38:11 +02004397static const u8 wl1271_rate_to_idx_2ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004398 /* MCS rates are used only with 11n */
Shahar Levi18357852010-10-13 16:09:41 +02004399 7, /* CONF_HW_RXTX_RATE_MCS7 */
4400 6, /* CONF_HW_RXTX_RATE_MCS6 */
4401 5, /* CONF_HW_RXTX_RATE_MCS5 */
4402 4, /* CONF_HW_RXTX_RATE_MCS4 */
4403 3, /* CONF_HW_RXTX_RATE_MCS3 */
4404 2, /* CONF_HW_RXTX_RATE_MCS2 */
4405 1, /* CONF_HW_RXTX_RATE_MCS1 */
4406 0, /* CONF_HW_RXTX_RATE_MCS0 */
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004407
4408 11, /* CONF_HW_RXTX_RATE_54 */
4409 10, /* CONF_HW_RXTX_RATE_48 */
4410 9, /* CONF_HW_RXTX_RATE_36 */
4411 8, /* CONF_HW_RXTX_RATE_24 */
4412
4413 /* TI-specific rate */
4414 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
4415
4416 7, /* CONF_HW_RXTX_RATE_18 */
4417 6, /* CONF_HW_RXTX_RATE_12 */
4418 3, /* CONF_HW_RXTX_RATE_11 */
4419 5, /* CONF_HW_RXTX_RATE_9 */
4420 4, /* CONF_HW_RXTX_RATE_6 */
4421 2, /* CONF_HW_RXTX_RATE_5_5 */
4422 1, /* CONF_HW_RXTX_RATE_2 */
4423 0 /* CONF_HW_RXTX_RATE_1 */
4424};
4425
Shahar Levie8b03a22010-10-13 16:09:39 +02004426/* 11n STA capabilities */
4427#define HW_RX_HIGHEST_RATE 72
4428
Shahar Levi00d20102010-11-08 11:20:10 +00004429#define WL12XX_HT_CAP { \
Shahar Levi871d0c32011-03-13 11:24:40 +02004430 .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | \
4431 (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), \
Shahar Levie8b03a22010-10-13 16:09:39 +02004432 .ht_supported = true, \
4433 .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, \
4434 .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \
4435 .mcs = { \
4436 .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \
4437 .rx_highest = cpu_to_le16(HW_RX_HIGHEST_RATE), \
4438 .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \
4439 }, \
4440}
4441
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004442/* can't be const, mac80211 writes to this */
4443static struct ieee80211_supported_band wl1271_band_2ghz = {
4444 .channels = wl1271_channels,
4445 .n_channels = ARRAY_SIZE(wl1271_channels),
4446 .bitrates = wl1271_rates,
4447 .n_bitrates = ARRAY_SIZE(wl1271_rates),
Shahar Levi00d20102010-11-08 11:20:10 +00004448 .ht_cap = WL12XX_HT_CAP,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004449};
4450
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03004451/* 5 GHz data rates for WL1273 */
4452static struct ieee80211_rate wl1271_rates_5ghz[] = {
4453 { .bitrate = 60,
4454 .hw_value = CONF_HW_BIT_RATE_6MBPS,
4455 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
4456 { .bitrate = 90,
4457 .hw_value = CONF_HW_BIT_RATE_9MBPS,
4458 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
4459 { .bitrate = 120,
4460 .hw_value = CONF_HW_BIT_RATE_12MBPS,
4461 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
4462 { .bitrate = 180,
4463 .hw_value = CONF_HW_BIT_RATE_18MBPS,
4464 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
4465 { .bitrate = 240,
4466 .hw_value = CONF_HW_BIT_RATE_24MBPS,
4467 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
4468 { .bitrate = 360,
4469 .hw_value = CONF_HW_BIT_RATE_36MBPS,
4470 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
4471 { .bitrate = 480,
4472 .hw_value = CONF_HW_BIT_RATE_48MBPS,
4473 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
4474 { .bitrate = 540,
4475 .hw_value = CONF_HW_BIT_RATE_54MBPS,
4476 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
4477};
4478
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01004479/* 5 GHz band channels for WL1273 */
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03004480static struct ieee80211_channel wl1271_channels_5ghz[] = {
Arik Nemtsov6cfa5cf2011-06-27 22:06:33 +03004481 { .hw_value = 7, .center_freq = 5035, .max_power = 25 },
4482 { .hw_value = 8, .center_freq = 5040, .max_power = 25 },
4483 { .hw_value = 9, .center_freq = 5045, .max_power = 25 },
4484 { .hw_value = 11, .center_freq = 5055, .max_power = 25 },
4485 { .hw_value = 12, .center_freq = 5060, .max_power = 25 },
4486 { .hw_value = 16, .center_freq = 5080, .max_power = 25 },
4487 { .hw_value = 34, .center_freq = 5170, .max_power = 25 },
4488 { .hw_value = 36, .center_freq = 5180, .max_power = 25 },
4489 { .hw_value = 38, .center_freq = 5190, .max_power = 25 },
4490 { .hw_value = 40, .center_freq = 5200, .max_power = 25 },
4491 { .hw_value = 42, .center_freq = 5210, .max_power = 25 },
4492 { .hw_value = 44, .center_freq = 5220, .max_power = 25 },
4493 { .hw_value = 46, .center_freq = 5230, .max_power = 25 },
4494 { .hw_value = 48, .center_freq = 5240, .max_power = 25 },
4495 { .hw_value = 52, .center_freq = 5260, .max_power = 25 },
4496 { .hw_value = 56, .center_freq = 5280, .max_power = 25 },
4497 { .hw_value = 60, .center_freq = 5300, .max_power = 25 },
4498 { .hw_value = 64, .center_freq = 5320, .max_power = 25 },
4499 { .hw_value = 100, .center_freq = 5500, .max_power = 25 },
4500 { .hw_value = 104, .center_freq = 5520, .max_power = 25 },
4501 { .hw_value = 108, .center_freq = 5540, .max_power = 25 },
4502 { .hw_value = 112, .center_freq = 5560, .max_power = 25 },
4503 { .hw_value = 116, .center_freq = 5580, .max_power = 25 },
4504 { .hw_value = 120, .center_freq = 5600, .max_power = 25 },
4505 { .hw_value = 124, .center_freq = 5620, .max_power = 25 },
4506 { .hw_value = 128, .center_freq = 5640, .max_power = 25 },
4507 { .hw_value = 132, .center_freq = 5660, .max_power = 25 },
4508 { .hw_value = 136, .center_freq = 5680, .max_power = 25 },
4509 { .hw_value = 140, .center_freq = 5700, .max_power = 25 },
4510 { .hw_value = 149, .center_freq = 5745, .max_power = 25 },
4511 { .hw_value = 153, .center_freq = 5765, .max_power = 25 },
4512 { .hw_value = 157, .center_freq = 5785, .max_power = 25 },
4513 { .hw_value = 161, .center_freq = 5805, .max_power = 25 },
4514 { .hw_value = 165, .center_freq = 5825, .max_power = 25 },
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03004515};
4516
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004517/* mapping to indexes for wl1271_rates_5ghz */
Tobias Klausera0ea9492010-05-20 10:38:11 +02004518static const u8 wl1271_rate_to_idx_5ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004519 /* MCS rates are used only with 11n */
Shahar Levi18357852010-10-13 16:09:41 +02004520 7, /* CONF_HW_RXTX_RATE_MCS7 */
4521 6, /* CONF_HW_RXTX_RATE_MCS6 */
4522 5, /* CONF_HW_RXTX_RATE_MCS5 */
4523 4, /* CONF_HW_RXTX_RATE_MCS4 */
4524 3, /* CONF_HW_RXTX_RATE_MCS3 */
4525 2, /* CONF_HW_RXTX_RATE_MCS2 */
4526 1, /* CONF_HW_RXTX_RATE_MCS1 */
4527 0, /* CONF_HW_RXTX_RATE_MCS0 */
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004528
4529 7, /* CONF_HW_RXTX_RATE_54 */
4530 6, /* CONF_HW_RXTX_RATE_48 */
4531 5, /* CONF_HW_RXTX_RATE_36 */
4532 4, /* CONF_HW_RXTX_RATE_24 */
4533
4534 /* TI-specific rate */
4535 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
4536
4537 3, /* CONF_HW_RXTX_RATE_18 */
4538 2, /* CONF_HW_RXTX_RATE_12 */
4539 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_11 */
4540 1, /* CONF_HW_RXTX_RATE_9 */
4541 0, /* CONF_HW_RXTX_RATE_6 */
4542 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_5_5 */
4543 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_2 */
4544 CONF_HW_RXTX_RATE_UNSUPPORTED /* CONF_HW_RXTX_RATE_1 */
4545};
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03004546
4547static struct ieee80211_supported_band wl1271_band_5ghz = {
4548 .channels = wl1271_channels_5ghz,
4549 .n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
4550 .bitrates = wl1271_rates_5ghz,
4551 .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
Shahar Levi00d20102010-11-08 11:20:10 +00004552 .ht_cap = WL12XX_HT_CAP,
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03004553};
4554
Tobias Klausera0ea9492010-05-20 10:38:11 +02004555static const u8 *wl1271_band_rate_to_idx[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004556 [IEEE80211_BAND_2GHZ] = wl1271_rate_to_idx_2ghz,
4557 [IEEE80211_BAND_5GHZ] = wl1271_rate_to_idx_5ghz
4558};
4559
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004560static const struct ieee80211_ops wl1271_ops = {
4561 .start = wl1271_op_start,
4562 .stop = wl1271_op_stop,
4563 .add_interface = wl1271_op_add_interface,
4564 .remove_interface = wl1271_op_remove_interface,
Luciano Coelhof634a4e2011-05-18 16:51:26 -04004565#ifdef CONFIG_PM
Eliad Peller402e48612011-05-13 11:57:09 +03004566 .suspend = wl1271_op_suspend,
4567 .resume = wl1271_op_resume,
Luciano Coelhof634a4e2011-05-18 16:51:26 -04004568#endif
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004569 .config = wl1271_op_config,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03004570 .prepare_multicast = wl1271_op_prepare_multicast,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004571 .configure_filter = wl1271_op_configure_filter,
4572 .tx = wl1271_op_tx,
4573 .set_key = wl1271_op_set_key,
4574 .hw_scan = wl1271_op_hw_scan,
Eliad Peller73ecce32011-06-27 13:06:45 +03004575 .cancel_hw_scan = wl1271_op_cancel_hw_scan,
Luciano Coelho33c2c062011-05-10 14:46:02 +03004576 .sched_scan_start = wl1271_op_sched_scan_start,
4577 .sched_scan_stop = wl1271_op_sched_scan_stop,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004578 .bss_info_changed = wl1271_op_bss_info_changed,
Arik Nemtsov68d069c2010-11-08 10:51:07 +01004579 .set_frag_threshold = wl1271_op_set_frag_threshold,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004580 .set_rts_threshold = wl1271_op_set_rts_threshold,
Kalle Valoc6999d82010-02-18 13:25:41 +02004581 .conf_tx = wl1271_op_conf_tx,
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03004582 .get_tsf = wl1271_op_get_tsf,
John W. Linvilleece550d2010-07-28 16:41:06 -04004583 .get_survey = wl1271_op_get_survey,
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004584 .sta_add = wl1271_op_sta_add,
4585 .sta_remove = wl1271_op_sta_remove,
Levi, Shaharbbba3e62011-01-23 07:27:23 +01004586 .ampdu_action = wl1271_op_ampdu_action,
Arik Nemtsov33437892011-04-26 23:35:39 +03004587 .tx_frames_pending = wl1271_tx_frames_pending,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +03004588 .set_bitrate_mask = wl12xx_set_bitrate_mask,
Shahar Levi6d158ff2011-09-08 13:01:33 +03004589 .channel_switch = wl12xx_op_channel_switch,
Kalle Valoc8c90872010-02-18 13:25:53 +02004590 CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004591};
4592
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004593
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02004594u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band)
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004595{
4596 u8 idx;
4597
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02004598 BUG_ON(band >= sizeof(wl1271_band_rate_to_idx)/sizeof(u8 *));
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004599
4600 if (unlikely(rate >= CONF_HW_RXTX_RATE_MAX)) {
4601 wl1271_error("Illegal RX rate from HW: %d", rate);
4602 return 0;
4603 }
4604
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02004605 idx = wl1271_band_rate_to_idx[band][rate];
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02004606 if (unlikely(idx == CONF_HW_RXTX_RATE_UNSUPPORTED)) {
4607 wl1271_error("Unsupported RX rate from HW: %d", rate);
4608 return 0;
4609 }
4610
4611 return idx;
4612}
4613
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02004614static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
4615 struct device_attribute *attr,
4616 char *buf)
4617{
4618 struct wl1271 *wl = dev_get_drvdata(dev);
4619 ssize_t len;
4620
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02004621 len = PAGE_SIZE;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02004622
4623 mutex_lock(&wl->mutex);
4624 len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
4625 wl->sg_enabled);
4626 mutex_unlock(&wl->mutex);
4627
4628 return len;
4629
4630}
4631
4632static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
4633 struct device_attribute *attr,
4634 const char *buf, size_t count)
4635{
4636 struct wl1271 *wl = dev_get_drvdata(dev);
4637 unsigned long res;
4638 int ret;
4639
Luciano Coelho6277ed62011-04-01 17:49:54 +03004640 ret = kstrtoul(buf, 10, &res);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02004641 if (ret < 0) {
4642 wl1271_warning("incorrect value written to bt_coex_mode");
4643 return count;
4644 }
4645
4646 mutex_lock(&wl->mutex);
4647
4648 res = !!res;
4649
4650 if (res == wl->sg_enabled)
4651 goto out;
4652
4653 wl->sg_enabled = res;
4654
4655 if (wl->state == WL1271_STATE_OFF)
4656 goto out;
4657
Ido Yariva6208652011-03-01 15:14:41 +02004658 ret = wl1271_ps_elp_wakeup(wl);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02004659 if (ret < 0)
4660 goto out;
4661
4662 wl1271_acx_sg_enable(wl, wl->sg_enabled);
4663 wl1271_ps_elp_sleep(wl);
4664
4665 out:
4666 mutex_unlock(&wl->mutex);
4667 return count;
4668}
4669
4670static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
4671 wl1271_sysfs_show_bt_coex_state,
4672 wl1271_sysfs_store_bt_coex_state);
4673
Juuso Oikarinend717fd62010-05-07 11:38:58 +03004674static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
4675 struct device_attribute *attr,
4676 char *buf)
4677{
4678 struct wl1271 *wl = dev_get_drvdata(dev);
4679 ssize_t len;
4680
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02004681 len = PAGE_SIZE;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03004682
4683 mutex_lock(&wl->mutex);
4684 if (wl->hw_pg_ver >= 0)
4685 len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
4686 else
4687 len = snprintf(buf, len, "n/a\n");
4688 mutex_unlock(&wl->mutex);
4689
4690 return len;
4691}
4692
Gery Kahn6f07b722011-07-18 14:21:49 +03004693static DEVICE_ATTR(hw_pg_ver, S_IRUGO,
Juuso Oikarinend717fd62010-05-07 11:38:58 +03004694 wl1271_sysfs_show_hw_pg_ver, NULL);
4695
Ido Yariv95dac04f2011-06-06 14:57:06 +03004696static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
4697 struct bin_attribute *bin_attr,
4698 char *buffer, loff_t pos, size_t count)
4699{
4700 struct device *dev = container_of(kobj, struct device, kobj);
4701 struct wl1271 *wl = dev_get_drvdata(dev);
4702 ssize_t len;
4703 int ret;
4704
4705 ret = mutex_lock_interruptible(&wl->mutex);
4706 if (ret < 0)
4707 return -ERESTARTSYS;
4708
4709 /* Let only one thread read the log at a time, blocking others */
4710 while (wl->fwlog_size == 0) {
4711 DEFINE_WAIT(wait);
4712
4713 prepare_to_wait_exclusive(&wl->fwlog_waitq,
4714 &wait,
4715 TASK_INTERRUPTIBLE);
4716
4717 if (wl->fwlog_size != 0) {
4718 finish_wait(&wl->fwlog_waitq, &wait);
4719 break;
4720 }
4721
4722 mutex_unlock(&wl->mutex);
4723
4724 schedule();
4725 finish_wait(&wl->fwlog_waitq, &wait);
4726
4727 if (signal_pending(current))
4728 return -ERESTARTSYS;
4729
4730 ret = mutex_lock_interruptible(&wl->mutex);
4731 if (ret < 0)
4732 return -ERESTARTSYS;
4733 }
4734
4735 /* Check if the fwlog is still valid */
4736 if (wl->fwlog_size < 0) {
4737 mutex_unlock(&wl->mutex);
4738 return 0;
4739 }
4740
4741 /* Seeking is not supported - old logs are not kept. Disregard pos. */
4742 len = min(count, (size_t)wl->fwlog_size);
4743 wl->fwlog_size -= len;
4744 memcpy(buffer, wl->fwlog, len);
4745
4746 /* Make room for new messages */
4747 memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
4748
4749 mutex_unlock(&wl->mutex);
4750
4751 return len;
4752}
4753
4754static struct bin_attribute fwlog_attr = {
4755 .attr = {.name = "fwlog", .mode = S_IRUSR},
4756 .read = wl1271_sysfs_read_fwlog,
4757};
4758
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02004759int wl1271_register_hw(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004760{
4761 int ret;
4762
4763 if (wl->mac80211_registered)
4764 return 0;
4765
Arik Nemtsov31d26ec2010-10-16 21:49:52 +02004766 ret = wl1271_fetch_nvs(wl);
4767 if (ret == 0) {
Shahar Levibc765bf2011-03-06 16:32:10 +02004768 /* NOTE: The wl->nvs->nvs element must be first, in
4769 * order to simplify the casting, we assume it is at
4770 * the beginning of the wl->nvs structure.
4771 */
4772 u8 *nvs_ptr = (u8 *)wl->nvs;
Arik Nemtsov31d26ec2010-10-16 21:49:52 +02004773
4774 wl->mac_addr[0] = nvs_ptr[11];
4775 wl->mac_addr[1] = nvs_ptr[10];
4776 wl->mac_addr[2] = nvs_ptr[6];
4777 wl->mac_addr[3] = nvs_ptr[5];
4778 wl->mac_addr[4] = nvs_ptr[4];
4779 wl->mac_addr[5] = nvs_ptr[3];
4780 }
4781
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004782 SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
4783
4784 ret = ieee80211_register_hw(wl->hw);
4785 if (ret < 0) {
4786 wl1271_error("unable to register mac80211 hw: %d", ret);
4787 return ret;
4788 }
4789
4790 wl->mac80211_registered = true;
4791
Eliad Pellerd60080a2010-11-24 12:53:16 +02004792 wl1271_debugfs_init(wl);
4793
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03004794 register_netdevice_notifier(&wl1271_dev_notifier);
4795
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004796 wl1271_notice("loaded");
4797
4798 return 0;
4799}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02004800EXPORT_SYMBOL_GPL(wl1271_register_hw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004801
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004802void wl1271_unregister_hw(struct wl1271 *wl)
4803{
Juuso Oikarinen4ae3fa82011-01-14 12:48:46 +01004804 if (wl->state == WL1271_STATE_PLT)
4805 __wl1271_plt_stop(wl);
4806
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03004807 unregister_netdevice_notifier(&wl1271_dev_notifier);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004808 ieee80211_unregister_hw(wl->hw);
4809 wl->mac80211_registered = false;
4810
4811}
4812EXPORT_SYMBOL_GPL(wl1271_unregister_hw);
4813
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02004814int wl1271_init_ieee80211(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004815{
Juuso Oikarinen7a557242010-09-27 12:42:07 +02004816 static const u32 cipher_suites[] = {
4817 WLAN_CIPHER_SUITE_WEP40,
4818 WLAN_CIPHER_SUITE_WEP104,
4819 WLAN_CIPHER_SUITE_TKIP,
4820 WLAN_CIPHER_SUITE_CCMP,
4821 WL1271_CIPHER_SUITE_GEM,
4822 };
4823
Juuso Oikarinen1e2b7972009-10-08 21:56:20 +03004824 /* The tx descriptor buffer and the TKIP space. */
4825 wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
4826 sizeof(struct wl1271_tx_hw_descr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004827
4828 /* unit us */
4829 /* FIXME: find a proper value */
4830 wl->hw->channel_change_time = 10000;
Juuso Oikarinen50c500a2010-04-01 11:38:22 +03004831 wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004832
4833 wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
Juuso Oikarinen03442a32009-11-23 23:22:14 +02004834 IEEE80211_HW_BEACON_FILTER |
Juuso Oikarinen0a343322010-02-22 08:38:41 +02004835 IEEE80211_HW_SUPPORTS_PS |
Kalle Valo4695dc92010-03-18 12:26:38 +02004836 IEEE80211_HW_SUPPORTS_UAPSD |
Juuso Oikarinena9af0922010-03-26 12:53:30 +02004837 IEEE80211_HW_HAS_RATE_CONTROL |
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03004838 IEEE80211_HW_CONNECTION_MONITOR |
Eliad Peller62c07402011-02-02 11:20:05 +02004839 IEEE80211_HW_SUPPORTS_CQM_RSSI |
Luciano Coelho25eaea302011-05-02 12:37:33 +03004840 IEEE80211_HW_REPORTS_TX_ACK_STATUS |
Shahar Levifcd23b62011-05-11 12:12:56 +03004841 IEEE80211_HW_SPECTRUM_MGMT |
Arik Nemtsov93f8c8e2011-08-30 09:34:01 +03004842 IEEE80211_HW_AP_LINK_PS |
4843 IEEE80211_HW_AMPDU_AGGREGATION |
4844 IEEE80211_HW_TX_AMPDU_SETUP_IN_HW;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004845
Juuso Oikarinen7a557242010-09-27 12:42:07 +02004846 wl->hw->wiphy->cipher_suites = cipher_suites;
4847 wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
4848
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02004849 wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
Eliad Peller045c7452011-08-28 15:23:01 +03004850 BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) |
4851 BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004852 wl->hw->wiphy->max_scan_ssids = 1;
Luciano Coelho221737d2011-09-02 14:28:22 +03004853 wl->hw->wiphy->max_sched_scan_ssids = 16;
4854 wl->hw->wiphy->max_match_sets = 16;
Guy Eilamea559b42010-12-09 16:54:59 +02004855 /*
4856 * Maximum length of elements in scanning probe request templates
4857 * should be the maximum length possible for a template, without
4858 * the IEEE80211 header of the template
4859 */
Eliad Peller154037d2011-08-14 13:17:12 +03004860 wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_DFLT_SIZE -
Guy Eilamea559b42010-12-09 16:54:59 +02004861 sizeof(struct ieee80211_header);
Luciano Coelhoa8aaaf52011-01-11 18:25:18 +01004862
Luciano Coelhoc9e79a42011-09-27 16:22:35 +03004863 wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_DFLT_SIZE -
4864 sizeof(struct ieee80211_header);
4865
Eliad Peller1ec23f72011-08-25 14:26:54 +03004866 wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
4867
Luciano Coelho4a31c112011-03-21 23:16:14 +02004868 /* make sure all our channels fit in the scanned_ch bitmask */
4869 BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
4870 ARRAY_SIZE(wl1271_channels_5ghz) >
4871 WL1271_MAX_CHANNELS);
Luciano Coelhoa8aaaf52011-01-11 18:25:18 +01004872 /*
4873 * We keep local copies of the band structs because we need to
4874 * modify them on a per-device basis.
4875 */
4876 memcpy(&wl->bands[IEEE80211_BAND_2GHZ], &wl1271_band_2ghz,
4877 sizeof(wl1271_band_2ghz));
4878 memcpy(&wl->bands[IEEE80211_BAND_5GHZ], &wl1271_band_5ghz,
4879 sizeof(wl1271_band_5ghz));
4880
4881 wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
4882 &wl->bands[IEEE80211_BAND_2GHZ];
4883 wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
4884 &wl->bands[IEEE80211_BAND_5GHZ];
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03004885
Kalle Valo12bd8942010-03-18 12:26:33 +02004886 wl->hw->queues = 4;
Juuso Oikarinen31627dc2010-03-26 12:53:12 +02004887 wl->hw->max_rates = 1;
Kalle Valo12bd8942010-03-18 12:26:33 +02004888
Juuso Oikarinenb7417d92010-11-10 11:27:19 +01004889 wl->hw->wiphy->reg_notifier = wl1271_reg_notify;
4890
Teemu Paasikivi8197b712010-02-22 08:38:23 +02004891 SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004892
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004893 wl->hw->sta_data_size = sizeof(struct wl1271_station);
Eliad Peller87fbcb02011-10-05 11:55:41 +02004894 wl->hw->vif_data_size = sizeof(struct wl12xx_vif);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004895
Luciano Coelho4c9cfa72011-01-12 14:27:03 +01004896 wl->hw->max_rx_aggregation_subframes = 8;
4897
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004898 return 0;
4899}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02004900EXPORT_SYMBOL_GPL(wl1271_init_ieee80211);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004901
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004902#define WL1271_DEFAULT_CHANNEL 0
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02004903
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02004904struct ieee80211_hw *wl1271_alloc_hw(void)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004905{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004906 struct ieee80211_hw *hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004907 struct platform_device *plat_dev = NULL;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004908 struct wl1271 *wl;
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02004909 int i, j, ret;
Ido Yariv1f37cbc2010-09-30 13:28:27 +02004910 unsigned int order;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004911
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004912 BUILD_BUG_ON(AP_MAX_STATIONS > WL12XX_MAX_LINKS);
Arik Nemtsovf80c2d12011-09-22 09:52:05 +03004913
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004914 hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
4915 if (!hw) {
4916 wl1271_error("could not alloc ieee80211_hw");
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02004917 ret = -ENOMEM;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004918 goto err_hw_alloc;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004919 }
4920
Julia Lawall929ebd32010-05-15 23:16:39 +02004921 plat_dev = kmemdup(&wl1271_device, sizeof(wl1271_device), GFP_KERNEL);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004922 if (!plat_dev) {
4923 wl1271_error("could not allocate platform_device");
4924 ret = -ENOMEM;
4925 goto err_plat_alloc;
4926 }
4927
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004928 wl = hw->priv;
4929 memset(wl, 0, sizeof(*wl));
4930
Juuso Oikarinen01c09162009-10-13 12:47:55 +03004931 INIT_LIST_HEAD(&wl->list);
Eliad Peller87627212011-10-10 10:12:54 +02004932 INIT_LIST_HEAD(&wl->wlvif_list);
Juuso Oikarinen01c09162009-10-13 12:47:55 +03004933
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004934 wl->hw = hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004935 wl->plat_dev = plat_dev;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004936
Juuso Oikarinen6742f552010-12-13 09:52:37 +02004937 for (i = 0; i < NUM_TX_QUEUES; i++)
Eliad Pellerc7ffb902011-10-05 11:56:05 +02004938 for (j = 0; j < WL12XX_MAX_LINKS; j++)
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02004939 skb_queue_head_init(&wl->links[j].tx_queue[i]);
4940
Ido Yariva6208652011-03-01 15:14:41 +02004941 skb_queue_head_init(&wl->deferred_rx_queue);
4942 skb_queue_head_init(&wl->deferred_tx_queue);
4943
Juuso Oikarinen37b70a82009-10-08 21:56:21 +03004944 INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
Ido Yariva6208652011-03-01 15:14:41 +02004945 INIT_WORK(&wl->netstack_work, wl1271_netstack_work);
Juuso Oikarinen117b38d2010-09-30 10:43:28 +02004946 INIT_WORK(&wl->tx_work, wl1271_tx_work);
4947 INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
4948 INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
Eliad Peller77ddaa12011-05-15 11:10:29 +03004949
Eliad Peller92ef8962011-06-07 12:50:46 +03004950 wl->freezable_wq = create_freezable_workqueue("wl12xx_wq");
4951 if (!wl->freezable_wq) {
4952 ret = -ENOMEM;
4953 goto err_hw;
4954 }
4955
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004956 wl->channel = WL1271_DEFAULT_CHANNEL;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004957 wl->rx_counter = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004958 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03004959 wl->band = IEEE80211_BAND_2GHZ;
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03004960 wl->vif = NULL;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02004961 wl->flags = 0;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02004962 wl->sg_enabled = true;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03004963 wl->hw_pg_ver = -1;
Arik Nemtsovb622d992011-02-23 00:22:31 +02004964 wl->ap_ps_map = 0;
4965 wl->ap_fw_ps_map = 0;
Ido Yariv606ea9f2011-03-01 15:14:39 +02004966 wl->quirks = 0;
Ido Yariv341b7cd2011-03-31 10:07:01 +02004967 wl->platform_quirks = 0;
Luciano Coelho33c2c062011-05-10 14:46:02 +03004968 wl->sched_scanning = false;
Guy Eilame9eb8cb2011-08-16 19:49:12 +03004969 wl->tx_spare_blocks = TX_HW_BLOCK_SPARE_DEFAULT;
Eliad Pellerf4df1bd2011-08-14 13:17:15 +03004970 wl->system_hlid = WL12XX_SYSTEM_HLID;
Arik Nemtsovda032092011-08-25 12:43:15 +03004971 wl->active_sta_count = 0;
Ido Yariv95dac04f2011-06-06 14:57:06 +03004972 wl->fwlog_size = 0;
4973 init_waitqueue_head(&wl->fwlog_waitq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004974
Eliad Pellerf4df1bd2011-08-14 13:17:15 +03004975 /* The system link is always allocated */
4976 __set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
4977
Ido Yariv25eeb9e2010-10-12 16:20:06 +02004978 memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
Juuso Oikarinenbe7078c2009-10-08 21:56:26 +03004979 for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004980 wl->tx_frames[i] = NULL;
4981
4982 spin_lock_init(&wl->wl_lock);
4983
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004984 wl->state = WL1271_STATE_OFF;
4985 mutex_init(&wl->mutex);
4986
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02004987 /* Apply default driver configuration. */
4988 wl1271_conf_init(wl);
4989
Ido Yariv1f37cbc2010-09-30 13:28:27 +02004990 order = get_order(WL1271_AGGR_BUFFER_SIZE);
4991 wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
4992 if (!wl->aggr_buf) {
4993 ret = -ENOMEM;
Eliad Peller92ef8962011-06-07 12:50:46 +03004994 goto err_wq;
Ido Yariv1f37cbc2010-09-30 13:28:27 +02004995 }
4996
Ido Yariv990f5de2011-03-31 10:06:59 +02004997 wl->dummy_packet = wl12xx_alloc_dummy_packet(wl);
4998 if (!wl->dummy_packet) {
4999 ret = -ENOMEM;
5000 goto err_aggr;
5001 }
5002
Ido Yariv95dac04f2011-06-06 14:57:06 +03005003 /* Allocate one page for the FW log */
5004 wl->fwlog = (u8 *)get_zeroed_page(GFP_KERNEL);
5005 if (!wl->fwlog) {
5006 ret = -ENOMEM;
5007 goto err_dummy_packet;
5008 }
5009
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005010 /* Register platform device */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005011 ret = platform_device_register(wl->plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005012 if (ret) {
5013 wl1271_error("couldn't register platform device");
Ido Yariv95dac04f2011-06-06 14:57:06 +03005014 goto err_fwlog;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005015 }
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005016 dev_set_drvdata(&wl->plat_dev->dev, wl);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005017
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02005018 /* Create sysfs file to control bt coex state */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005019 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02005020 if (ret < 0) {
5021 wl1271_error("failed to create sysfs file bt_coex_state");
5022 goto err_platform;
5023 }
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005024
Juuso Oikarinend717fd62010-05-07 11:38:58 +03005025 /* Create sysfs file to get HW PG version */
5026 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
5027 if (ret < 0) {
5028 wl1271_error("failed to create sysfs file hw_pg_ver");
5029 goto err_bt_coex_state;
5030 }
5031
Ido Yariv95dac04f2011-06-06 14:57:06 +03005032 /* Create sysfs file for the FW log */
5033 ret = device_create_bin_file(&wl->plat_dev->dev, &fwlog_attr);
5034 if (ret < 0) {
5035 wl1271_error("failed to create sysfs file fwlog");
5036 goto err_hw_pg_ver;
5037 }
5038
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02005039 return hw;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005040
Ido Yariv95dac04f2011-06-06 14:57:06 +03005041err_hw_pg_ver:
5042 device_remove_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
5043
Juuso Oikarinend717fd62010-05-07 11:38:58 +03005044err_bt_coex_state:
5045 device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
5046
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02005047err_platform:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005048 platform_device_unregister(wl->plat_dev);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02005049
Ido Yariv95dac04f2011-06-06 14:57:06 +03005050err_fwlog:
5051 free_page((unsigned long)wl->fwlog);
5052
Ido Yariv990f5de2011-03-31 10:06:59 +02005053err_dummy_packet:
5054 dev_kfree_skb(wl->dummy_packet);
5055
Ido Yariv1f37cbc2010-09-30 13:28:27 +02005056err_aggr:
5057 free_pages((unsigned long)wl->aggr_buf, order);
5058
Eliad Peller92ef8962011-06-07 12:50:46 +03005059err_wq:
5060 destroy_workqueue(wl->freezable_wq);
5061
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005062err_hw:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005063 wl1271_debugfs_exit(wl);
5064 kfree(plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005065
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005066err_plat_alloc:
5067 ieee80211_free_hw(hw);
5068
5069err_hw_alloc:
5070
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02005071 return ERR_PTR(ret);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02005072}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02005073EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02005074
5075int wl1271_free_hw(struct wl1271 *wl)
5076{
Ido Yariv95dac04f2011-06-06 14:57:06 +03005077 /* Unblock any fwlog readers */
5078 mutex_lock(&wl->mutex);
5079 wl->fwlog_size = -1;
5080 wake_up_interruptible_all(&wl->fwlog_waitq);
5081 mutex_unlock(&wl->mutex);
5082
5083 device_remove_bin_file(&wl->plat_dev->dev, &fwlog_attr);
Gery Kahn6f07b722011-07-18 14:21:49 +03005084
5085 device_remove_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
5086
5087 device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005088 platform_device_unregister(wl->plat_dev);
Ido Yariv95dac04f2011-06-06 14:57:06 +03005089 free_page((unsigned long)wl->fwlog);
Ido Yariv990f5de2011-03-31 10:06:59 +02005090 dev_kfree_skb(wl->dummy_packet);
Ido Yariv1f37cbc2010-09-30 13:28:27 +02005091 free_pages((unsigned long)wl->aggr_buf,
5092 get_order(WL1271_AGGR_BUFFER_SIZE));
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02005093 kfree(wl->plat_dev);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02005094
5095 wl1271_debugfs_exit(wl);
5096
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02005097 vfree(wl->fw);
5098 wl->fw = NULL;
5099 kfree(wl->nvs);
5100 wl->nvs = NULL;
5101
5102 kfree(wl->fw_status);
5103 kfree(wl->tx_res_if);
Eliad Peller92ef8962011-06-07 12:50:46 +03005104 destroy_workqueue(wl->freezable_wq);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02005105
5106 ieee80211_free_hw(wl->hw);
5107
5108 return 0;
5109}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02005110EXPORT_SYMBOL_GPL(wl1271_free_hw);
5111
Guy Eilam491bbd62011-01-12 10:33:29 +01005112u32 wl12xx_debug_level = DEBUG_NONE;
Eliad Peller17c17552010-12-12 12:15:35 +02005113EXPORT_SYMBOL_GPL(wl12xx_debug_level);
Guy Eilam491bbd62011-01-12 10:33:29 +01005114module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
Eliad Peller17c17552010-12-12 12:15:35 +02005115MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
5116
Ido Yariv95dac04f2011-06-06 14:57:06 +03005117module_param_named(fwlog, fwlog_param, charp, 0);
5118MODULE_PARM_DESC(keymap,
5119 "FW logger options: continuous, ondemand, dbgpins or disable");
5120
Eliad Peller2a5bff02011-08-25 18:10:59 +03005121module_param(bug_on_recovery, bool, S_IRUSR | S_IWUSR);
5122MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");
5123
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02005124MODULE_LICENSE("GPL");
Luciano Coelhob1a48ca2011-02-22 14:19:28 +02005125MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02005126MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");