blob: 5772a33d79ecab92e9bfaa6cbe188c442670363b [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>
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030033
Shahar Levi00d20102010-11-08 11:20:10 +000034#include "wl12xx.h"
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030035#include "wl12xx_80211.h"
Shahar Levi00d20102010-11-08 11:20:10 +000036#include "reg.h"
37#include "io.h"
38#include "event.h"
39#include "tx.h"
40#include "rx.h"
41#include "ps.h"
42#include "init.h"
43#include "debugfs.h"
44#include "cmd.h"
45#include "boot.h"
46#include "testmode.h"
47#include "scan.h"
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030048
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +020049#define WL1271_BOOT_RETRIES 3
50
Juuso Oikarinen8a080482009-10-13 12:47:44 +030051static struct conf_drv_settings default_conf = {
52 .sg = {
Juuso Oikarinen1b00f542010-03-18 12:26:30 +020053 .params = {
54 [CONF_SG_BT_PER_THRESHOLD] = 7500,
55 [CONF_SG_HV3_MAX_OVERRIDE] = 0,
56 [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400,
57 [CONF_SG_BT_LOAD_RATIO] = 50,
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +030058 [CONF_SG_AUTO_PS_MODE] = 1,
Juuso Oikarinen1b00f542010-03-18 12:26:30 +020059 [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
60 [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
61 [CONF_SG_ANTENNA_CONFIGURATION] = 0,
62 [CONF_SG_BEACON_MISS_PERCENT] = 60,
63 [CONF_SG_RATE_ADAPT_THRESH] = 12,
64 [CONF_SG_RATE_ADAPT_SNR] = 0,
65 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR] = 10,
66 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR] = 30,
67 [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR] = 8,
68 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR] = 20,
69 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR] = 50,
70 /* Note: with UPSD, this should be 4 */
71 [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR] = 8,
72 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR] = 7,
73 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR] = 25,
74 [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR] = 20,
75 /* Note: with UPDS, this should be 15 */
76 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR] = 8,
77 /* Note: with UPDS, this should be 50 */
78 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR] = 40,
79 /* Note: with UPDS, this should be 10 */
80 [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR] = 20,
81 [CONF_SG_RXT] = 1200,
82 [CONF_SG_TXT] = 1000,
83 [CONF_SG_ADAPTIVE_RXT_TXT] = 1,
84 [CONF_SG_PS_POLL_TIMEOUT] = 10,
85 [CONF_SG_UPSD_TIMEOUT] = 10,
86 [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR] = 7,
87 [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR] = 15,
88 [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR] = 15,
89 [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR] = 8,
90 [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR] = 20,
91 [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR] = 15,
92 [CONF_SG_WLAN_ACTIVE_BT_ACL_MIN_BR] = 20,
93 [CONF_SG_WLAN_ACTIVE_BT_ACL_MAX_BR] = 50,
94 [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_BR] = 10,
95 [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
96 [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP] = 800,
97 [CONF_SG_PASSIVE_SCAN_A2DP_BT_TIME] = 75,
98 [CONF_SG_PASSIVE_SCAN_A2DP_WLAN_TIME] = 15,
99 [CONF_SG_HV3_MAX_SERVED] = 6,
100 [CONF_SG_DHCP_TIME] = 5000,
101 [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
102 },
103 .state = CONF_SG_PROTECTIVE,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300104 },
105 .rx = {
106 .rx_msdu_life_time = 512000,
107 .packet_detection_threshold = 0,
108 .ps_poll_timeout = 15,
109 .upsd_timeout = 15,
110 .rts_threshold = 2347,
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200111 .rx_cca_threshold = 0,
112 .irq_blk_threshold = 0xFFFF,
113 .irq_pkt_threshold = 0,
114 .irq_timeout = 600,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300115 .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY,
116 },
117 .tx = {
118 .tx_energy_detection = 0,
Arik Nemtsov1e05a812010-10-16 17:44:51 +0200119 .sta_rc_conf = {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +0300120 .enabled_rates = 0,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300121 .short_retry_limit = 10,
122 .long_retry_limit = 10,
Arik Nemtsov1e05a812010-10-16 17:44:51 +0200123 .aflags = 0,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300124 },
125 .ac_conf_count = 4,
126 .ac_conf = {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200127 [CONF_TX_AC_BE] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300128 .ac = CONF_TX_AC_BE,
129 .cw_min = 15,
130 .cw_max = 63,
131 .aifsn = 3,
132 .tx_op_limit = 0,
133 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200134 [CONF_TX_AC_BK] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300135 .ac = CONF_TX_AC_BK,
136 .cw_min = 15,
137 .cw_max = 63,
138 .aifsn = 7,
139 .tx_op_limit = 0,
140 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200141 [CONF_TX_AC_VI] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300142 .ac = CONF_TX_AC_VI,
143 .cw_min = 15,
144 .cw_max = 63,
145 .aifsn = CONF_TX_AIFS_PIFS,
146 .tx_op_limit = 3008,
147 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200148 [CONF_TX_AC_VO] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300149 .ac = CONF_TX_AC_VO,
150 .cw_min = 15,
151 .cw_max = 63,
152 .aifsn = CONF_TX_AIFS_PIFS,
153 .tx_op_limit = 1504,
154 },
155 },
Arik Nemtsov1e05a812010-10-16 17:44:51 +0200156 .ap_rc_conf = {
157 [0] = {
158 .enabled_rates = CONF_TX_AP_ENABLED_RATES,
159 .short_retry_limit = 10,
160 .long_retry_limit = 10,
161 .aflags = 0,
162 },
163 [1] = {
164 .enabled_rates = CONF_TX_AP_ENABLED_RATES,
165 .short_retry_limit = 10,
166 .long_retry_limit = 10,
167 .aflags = 0,
168 },
169 [2] = {
170 .enabled_rates = CONF_TX_AP_ENABLED_RATES,
171 .short_retry_limit = 10,
172 .long_retry_limit = 10,
173 .aflags = 0,
174 },
175 [3] = {
176 .enabled_rates = CONF_TX_AP_ENABLED_RATES,
177 .short_retry_limit = 10,
178 .long_retry_limit = 10,
179 .aflags = 0,
180 },
181 },
182 .ap_mgmt_conf = {
183 .enabled_rates = CONF_TX_AP_DEFAULT_MGMT_RATES,
184 .short_retry_limit = 10,
185 .long_retry_limit = 10,
186 .aflags = 0,
187 },
188 .ap_bcst_conf = {
189 .enabled_rates = CONF_HW_BIT_RATE_1MBPS,
190 .short_retry_limit = 10,
191 .long_retry_limit = 10,
192 .aflags = 0,
193 },
Arik Nemtsov79b223f2010-10-16 17:52:59 +0200194 .ap_max_tx_retries = 100,
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200195 .tid_conf_count = 4,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300196 .tid_conf = {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200197 [CONF_TX_AC_BE] = {
198 .queue_id = CONF_TX_AC_BE,
199 .channel_type = CONF_CHANNEL_TYPE_EDCF,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300200 .tsid = CONF_TX_AC_BE,
201 .ps_scheme = CONF_PS_SCHEME_LEGACY,
202 .ack_policy = CONF_ACK_POLICY_LEGACY,
203 .apsd_conf = {0, 0},
204 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200205 [CONF_TX_AC_BK] = {
206 .queue_id = CONF_TX_AC_BK,
207 .channel_type = CONF_CHANNEL_TYPE_EDCF,
208 .tsid = CONF_TX_AC_BK,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300209 .ps_scheme = CONF_PS_SCHEME_LEGACY,
210 .ack_policy = CONF_ACK_POLICY_LEGACY,
211 .apsd_conf = {0, 0},
212 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200213 [CONF_TX_AC_VI] = {
214 .queue_id = CONF_TX_AC_VI,
215 .channel_type = CONF_CHANNEL_TYPE_EDCF,
216 .tsid = CONF_TX_AC_VI,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300217 .ps_scheme = CONF_PS_SCHEME_LEGACY,
218 .ack_policy = CONF_ACK_POLICY_LEGACY,
219 .apsd_conf = {0, 0},
220 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200221 [CONF_TX_AC_VO] = {
222 .queue_id = CONF_TX_AC_VO,
223 .channel_type = CONF_CHANNEL_TYPE_EDCF,
224 .tsid = CONF_TX_AC_VO,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300225 .ps_scheme = CONF_PS_SCHEME_LEGACY,
226 .ack_policy = CONF_ACK_POLICY_LEGACY,
227 .apsd_conf = {0, 0},
228 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300229 },
230 .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD,
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200231 .tx_compl_timeout = 700,
Juuso Oikarinenebba60c2010-04-01 11:38:20 +0300232 .tx_compl_threshold = 4,
233 .basic_rate = CONF_HW_BIT_RATE_1MBPS,
234 .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS,
Arik Nemtsov1e05a812010-10-16 17:44:51 +0200235 .tmpl_short_retry_limit = 10,
236 .tmpl_long_retry_limit = 10,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300237 },
238 .conn = {
239 .wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
Juuso Oikarinen50c500a2010-04-01 11:38:22 +0300240 .listen_interval = 1,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300241 .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED,
242 .bcn_filt_ie_count = 1,
243 .bcn_filt_ie = {
244 [0] = {
245 .ie = WLAN_EID_CHANNEL_SWITCH,
246 .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE,
247 }
248 },
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200249 .synch_fail_thold = 10,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300250 .bss_lose_timeout = 100,
251 .beacon_rx_timeout = 10000,
252 .broadcast_timeout = 20000,
253 .rx_broadcast_in_ps = 1,
Juuso Oikarinen90494a92010-07-08 17:50:00 +0300254 .ps_poll_threshold = 10,
255 .ps_poll_recovery_period = 700,
Juuso Oikarinen11f70f92009-10-13 12:47:46 +0300256 .bet_enable = CONF_BET_MODE_ENABLE,
Juuso Oikarinen84502562009-11-23 23:22:12 +0200257 .bet_max_consecutive = 10,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +0200258 .psm_entry_retries = 5,
Eliad Pelleree608332011-02-02 09:59:34 +0200259 .psm_exit_retries = 255,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +0200260 .psm_entry_nullfunc_retries = 3,
261 .psm_entry_hangover_period = 1,
Juuso Oikarinen50c500a2010-04-01 11:38:22 +0300262 .keep_alive_interval = 55000,
263 .max_listen_interval = 20,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300264 },
Luciano Coelho6e92b412009-12-11 15:40:50 +0200265 .itrim = {
266 .enable = false,
267 .timeout = 50000,
Juuso Oikarinen38ad2d82009-12-11 15:41:08 +0200268 },
269 .pm_config = {
270 .host_clk_settling_time = 5000,
271 .host_fast_wakeup_support = false
Juuso Oikarinen00236aed2010-04-09 11:07:30 +0300272 },
273 .roam_trigger = {
Juuso Oikarinen00236aed2010-04-09 11:07:30 +0300274 .trigger_pacing = 1,
275 .avg_weight_rssi_beacon = 20,
276 .avg_weight_rssi_data = 10,
277 .avg_weight_snr_beacon = 20,
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100278 .avg_weight_snr_data = 10,
Juuso Oikarinenbea39d62010-09-21 08:14:31 +0200279 },
280 .scan = {
281 .min_dwell_time_active = 7500,
282 .max_dwell_time_active = 30000,
Juuso Oikarinenea45b2c2011-01-24 07:01:54 +0100283 .min_dwell_time_passive = 100000,
284 .max_dwell_time_passive = 100000,
Juuso Oikarinenbea39d62010-09-21 08:14:31 +0200285 .num_probe_reqs = 2,
286 },
Juuso Oikarinen644a4862010-10-05 13:11:56 +0200287 .rf = {
288 .tx_per_channel_power_compensation_2 = {
289 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
290 },
291 .tx_per_channel_power_compensation_5 = {
292 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
293 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
294 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
295 },
296 },
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100297 .ht = {
298 .tx_ba_win_size = 64,
299 .inactivity_timeout = 10000,
300 },
Eliad Pellerc8bde242011-02-02 09:59:35 +0200301 .mem = {
Eliad Pellerfe5ef092011-02-02 09:59:36 +0200302 .num_stations = 1,
303 .ssid_profiles = 1,
304 .rx_block_num = 70,
305 .tx_min_block_num = 40,
Eliad Pellerc8bde242011-02-02 09:59:35 +0200306 .dynamic_memory = 0,
307 .min_req_tx_blocks = 104,
308 .min_req_rx_blocks = 22,
309 .tx_min = 27,
310 }
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300311};
312
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200313static void __wl1271_op_remove_interface(struct wl1271 *wl);
Arik Nemtsov7f179b42010-10-16 21:39:06 +0200314static void wl1271_free_ap_keys(struct wl1271 *wl);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200315
316
Juuso Oikarinena1dd8182010-03-18 12:26:31 +0200317static void wl1271_device_release(struct device *dev)
318{
319
320}
321
322static struct platform_device wl1271_device = {
323 .name = "wl1271",
324 .id = -1,
325
326 /* device model insists to have a release function */
327 .dev = {
328 .release = wl1271_device_release,
329 },
330};
331
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300332static LIST_HEAD(wl_list);
333
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300334static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
335 void *arg)
336{
337 struct net_device *dev = arg;
338 struct wireless_dev *wdev;
339 struct wiphy *wiphy;
340 struct ieee80211_hw *hw;
341 struct wl1271 *wl;
342 struct wl1271 *wl_temp;
343 int ret = 0;
344
345 /* Check that this notification is for us. */
346 if (what != NETDEV_CHANGE)
347 return NOTIFY_DONE;
348
349 wdev = dev->ieee80211_ptr;
350 if (wdev == NULL)
351 return NOTIFY_DONE;
352
353 wiphy = wdev->wiphy;
354 if (wiphy == NULL)
355 return NOTIFY_DONE;
356
357 hw = wiphy_priv(wiphy);
358 if (hw == NULL)
359 return NOTIFY_DONE;
360
361 wl_temp = hw->priv;
362 list_for_each_entry(wl, &wl_list, list) {
363 if (wl == wl_temp)
364 break;
365 }
366 if (wl != wl_temp)
367 return NOTIFY_DONE;
368
369 mutex_lock(&wl->mutex);
370
371 if (wl->state == WL1271_STATE_OFF)
372 goto out;
373
374 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
375 goto out;
376
377 ret = wl1271_ps_elp_wakeup(wl, false);
378 if (ret < 0)
379 goto out;
380
381 if ((dev->operstate == IF_OPER_UP) &&
382 !test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags)) {
383 wl1271_cmd_set_sta_state(wl);
384 wl1271_info("Association completed.");
385 }
386
387 wl1271_ps_elp_sleep(wl);
388
389out:
390 mutex_unlock(&wl->mutex);
391
392 return NOTIFY_OK;
393}
394
Juuso Oikarinenb7417d92010-11-10 11:27:19 +0100395static int wl1271_reg_notify(struct wiphy *wiphy,
Luciano Coelho573c67c2010-11-26 13:44:59 +0200396 struct regulatory_request *request)
397{
Juuso Oikarinenb7417d92010-11-10 11:27:19 +0100398 struct ieee80211_supported_band *band;
399 struct ieee80211_channel *ch;
400 int i;
401
402 band = wiphy->bands[IEEE80211_BAND_5GHZ];
403 for (i = 0; i < band->n_channels; i++) {
404 ch = &band->channels[i];
405 if (ch->flags & IEEE80211_CHAN_DISABLED)
406 continue;
407
408 if (ch->flags & IEEE80211_CHAN_RADAR)
409 ch->flags |= IEEE80211_CHAN_NO_IBSS |
410 IEEE80211_CHAN_PASSIVE_SCAN;
411
412 }
413
414 return 0;
415}
416
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300417static void wl1271_conf_init(struct wl1271 *wl)
418{
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300419
420 /*
421 * This function applies the default configuration to the driver. This
422 * function is invoked upon driver load (spi probe.)
423 *
424 * The configuration is stored in a run-time structure in order to
425 * facilitate for run-time adjustment of any of the parameters. Making
426 * changes to the configuration structure will apply the new values on
427 * the next interface up (wl1271_op_start.)
428 */
429
430 /* apply driver default configuration */
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300431 memcpy(&wl->conf, &default_conf, sizeof(default_conf));
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300432}
433
434
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300435static int wl1271_plt_init(struct wl1271 *wl)
436{
Luciano Coelho12419cc2010-02-18 13:25:44 +0200437 struct conf_tx_ac_category *conf_ac;
438 struct conf_tx_tid *conf_tid;
439 int ret, i;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300440
Luciano Coelho98b5dd52009-11-23 23:22:17 +0200441 ret = wl1271_cmd_general_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200442 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200443 return ret;
444
Luciano Coelho98b5dd52009-11-23 23:22:17 +0200445 ret = wl1271_cmd_radio_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200446 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200447 return ret;
448
Juuso Oikarinen644a4862010-10-05 13:11:56 +0200449 ret = wl1271_cmd_ext_radio_parms(wl);
450 if (ret < 0)
451 return ret;
452
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200453 ret = wl1271_sta_init_templates_config(wl);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200454 if (ret < 0)
455 return ret;
456
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300457 ret = wl1271_acx_init_mem_config(wl);
458 if (ret < 0)
459 return ret;
460
Luciano Coelho12419cc2010-02-18 13:25:44 +0200461 /* PHY layer config */
462 ret = wl1271_init_phy_config(wl);
463 if (ret < 0)
464 goto out_free_memmap;
465
466 ret = wl1271_acx_dco_itrim_params(wl);
467 if (ret < 0)
468 goto out_free_memmap;
469
470 /* Initialize connection monitoring thresholds */
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +0200471 ret = wl1271_acx_conn_monit_params(wl, false);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200472 if (ret < 0)
473 goto out_free_memmap;
474
475 /* Bluetooth WLAN coexistence */
476 ret = wl1271_init_pta(wl);
477 if (ret < 0)
478 goto out_free_memmap;
479
480 /* Energy detection */
481 ret = wl1271_init_energy_detection(wl);
482 if (ret < 0)
483 goto out_free_memmap;
484
Gery Kahn1ec610e2011-02-01 03:03:08 -0600485 ret = wl1271_acx_sta_mem_cfg(wl);
486 if (ret < 0)
487 goto out_free_memmap;
488
Luciano Coelho12419cc2010-02-18 13:25:44 +0200489 /* Default fragmentation threshold */
Arik Nemtsov68d069c2010-11-08 10:51:07 +0100490 ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200491 if (ret < 0)
492 goto out_free_memmap;
493
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200494 /* Default TID/AC configuration */
495 BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200496 for (i = 0; i < wl->conf.tx.tid_conf_count; i++) {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200497 conf_ac = &wl->conf.tx.ac_conf[i];
498 ret = wl1271_acx_ac_cfg(wl, conf_ac->ac, conf_ac->cw_min,
499 conf_ac->cw_max, conf_ac->aifsn,
500 conf_ac->tx_op_limit);
501 if (ret < 0)
502 goto out_free_memmap;
503
Luciano Coelho12419cc2010-02-18 13:25:44 +0200504 conf_tid = &wl->conf.tx.tid_conf[i];
505 ret = wl1271_acx_tid_cfg(wl, conf_tid->queue_id,
506 conf_tid->channel_type,
507 conf_tid->tsid,
508 conf_tid->ps_scheme,
509 conf_tid->ack_policy,
510 conf_tid->apsd_conf[0],
511 conf_tid->apsd_conf[1]);
512 if (ret < 0)
513 goto out_free_memmap;
514 }
515
Luciano Coelho12419cc2010-02-18 13:25:44 +0200516 /* Enable data path */
Luciano Coelho94210892009-12-11 15:40:55 +0200517 ret = wl1271_cmd_data_path(wl, 1);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300518 if (ret < 0)
Luciano Coelho12419cc2010-02-18 13:25:44 +0200519 goto out_free_memmap;
520
521 /* Configure for CAM power saving (ie. always active) */
522 ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
523 if (ret < 0)
524 goto out_free_memmap;
525
526 /* configure PM */
527 ret = wl1271_acx_pm_config(wl);
528 if (ret < 0)
529 goto out_free_memmap;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300530
531 return 0;
Luciano Coelho12419cc2010-02-18 13:25:44 +0200532
533 out_free_memmap:
534 kfree(wl->target_mem_map);
535 wl->target_mem_map = NULL;
536
537 return ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300538}
539
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300540static void wl1271_fw_status(struct wl1271 *wl,
Eliad Pellerc8bde242011-02-02 09:59:35 +0200541 struct wl1271_fw_full_status *full_status)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300542{
Eliad Pellerc8bde242011-02-02 09:59:35 +0200543 struct wl1271_fw_common_status *status = &full_status->common;
Juuso Oikarinenac5e1e32010-02-22 08:38:38 +0200544 struct timespec ts;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300545 u32 total = 0;
546 int i;
547
Eliad Pellerc8bde242011-02-02 09:59:35 +0200548 if (wl->bss_type == BSS_TYPE_AP_BSS)
549 wl1271_raw_read(wl, FW_STATUS_ADDR, status,
550 sizeof(struct wl1271_fw_ap_status), false);
551 else
552 wl1271_raw_read(wl, FW_STATUS_ADDR, status,
553 sizeof(struct wl1271_fw_sta_status), false);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300554
555 wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
556 "drv_rx_counter = %d, tx_results_counter = %d)",
557 status->intr,
558 status->fw_rx_counter,
559 status->drv_rx_counter,
560 status->tx_results_counter);
561
562 /* update number of available TX blocks */
563 for (i = 0; i < NUM_TX_QUEUES; i++) {
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300564 u32 cnt = le32_to_cpu(status->tx_released_blks[i]) -
565 wl->tx_blocks_freed[i];
566
567 wl->tx_blocks_freed[i] =
568 le32_to_cpu(status->tx_released_blks[i]);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300569 wl->tx_blocks_available += cnt;
570 total += cnt;
571 }
572
Ido Yariva5225502010-10-12 14:49:10 +0200573 /* if more blocks are available now, tx work can be scheduled */
574 if (total)
575 clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300576
Arik Nemtsov09039f42011-02-23 00:22:30 +0200577 if (wl->bss_type == BSS_TYPE_AP_BSS) {
578 for (i = 0; i < AP_MAX_LINKS; i++) {
579 u8 cnt = status->tx_lnk_free_blks[i] -
580 wl->links[i].prev_freed_blks;
581
582 wl->links[i].prev_freed_blks =
583 status->tx_lnk_free_blks[i];
584 wl->links[i].allocated_blks -= cnt;
585 }
586 }
587
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300588 /* update the host-chipset time offset */
Juuso Oikarinenac5e1e32010-02-22 08:38:38 +0200589 getnstimeofday(&ts);
590 wl->time_offset = (timespec_to_ns(&ts) >> 10) -
591 (s64)le32_to_cpu(status->fw_localtime);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300592}
593
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200594#define WL1271_IRQ_MAX_LOOPS 10
595
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300596static void wl1271_irq_work(struct work_struct *work)
597{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300598 int ret;
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300599 u32 intr;
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200600 int loopcount = WL1271_IRQ_MAX_LOOPS;
601 unsigned long flags;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300602 struct wl1271 *wl =
603 container_of(work, struct wl1271, irq_work);
604
605 mutex_lock(&wl->mutex);
606
607 wl1271_debug(DEBUG_IRQ, "IRQ work");
608
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200609 if (unlikely(wl->state == WL1271_STATE_OFF))
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300610 goto out;
611
612 ret = wl1271_ps_elp_wakeup(wl, true);
613 if (ret < 0)
614 goto out;
615
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200616 spin_lock_irqsave(&wl->wl_lock, flags);
617 while (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags) && loopcount) {
618 clear_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags);
619 spin_unlock_irqrestore(&wl->wl_lock, flags);
620 loopcount--;
621
622 wl1271_fw_status(wl, wl->fw_status);
Eliad Pellerc8bde242011-02-02 09:59:35 +0200623 intr = le32_to_cpu(wl->fw_status->common.intr);
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200624 if (!intr) {
625 wl1271_debug(DEBUG_IRQ, "Zero interrupt received.");
Dan Carpentercdd08642010-05-08 18:25:17 +0200626 spin_lock_irqsave(&wl->wl_lock, flags);
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200627 continue;
628 }
629
630 intr &= WL1271_INTR_MASK;
631
Eliad Pellerccc83b02010-10-27 14:09:57 +0200632 if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) {
633 wl1271_error("watchdog interrupt received! "
634 "starting recovery.");
635 ieee80211_queue_work(wl->hw, &wl->recovery_work);
636
637 /* restarting the chip. ignore any other interrupt. */
638 goto out;
639 }
640
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200641 if (intr & WL1271_ACX_INTR_DATA) {
642 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
643
644 /* check for tx results */
Eliad Pellerc8bde242011-02-02 09:59:35 +0200645 if (wl->fw_status->common.tx_results_counter !=
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200646 (wl->tx_results_count & 0xff))
647 wl1271_tx_complete(wl);
648
Ido Yariva5225502010-10-12 14:49:10 +0200649 /* Check if any tx blocks were freed */
650 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
Juuso Oikarinen6742f552010-12-13 09:52:37 +0200651 wl->tx_queue_count) {
Ido Yariva5225502010-10-12 14:49:10 +0200652 /*
653 * In order to avoid starvation of the TX path,
654 * call the work function directly.
655 */
656 wl1271_tx_work_locked(wl);
657 }
658
Eliad Pellerc8bde242011-02-02 09:59:35 +0200659 wl1271_rx(wl, &wl->fw_status->common);
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200660 }
661
662 if (intr & WL1271_ACX_INTR_EVENT_A) {
663 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
664 wl1271_event_handle(wl, 0);
665 }
666
667 if (intr & WL1271_ACX_INTR_EVENT_B) {
668 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
669 wl1271_event_handle(wl, 1);
670 }
671
672 if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
673 wl1271_debug(DEBUG_IRQ,
674 "WL1271_ACX_INTR_INIT_COMPLETE");
675
676 if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
677 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
678
679 spin_lock_irqsave(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300680 }
681
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200682 if (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags))
683 ieee80211_queue_work(wl->hw, &wl->irq_work);
684 else
685 clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
686 spin_unlock_irqrestore(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300687
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300688 wl1271_ps_elp_sleep(wl);
689
690out:
691 mutex_unlock(&wl->mutex);
692}
693
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300694static int wl1271_fetch_firmware(struct wl1271 *wl)
695{
696 const struct firmware *fw;
Arik Nemtsov166d5042010-10-16 21:44:57 +0200697 const char *fw_name;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300698 int ret;
699
Arik Nemtsov166d5042010-10-16 21:44:57 +0200700 switch (wl->bss_type) {
701 case BSS_TYPE_AP_BSS:
702 fw_name = WL1271_AP_FW_NAME;
703 break;
704 case BSS_TYPE_IBSS:
705 case BSS_TYPE_STA_BSS:
706 fw_name = WL1271_FW_NAME;
707 break;
708 default:
709 wl1271_error("no compatible firmware for bss_type %d",
710 wl->bss_type);
711 return -EINVAL;
712 }
713
714 wl1271_debug(DEBUG_BOOT, "booting firmware %s", fw_name);
715
716 ret = request_firmware(&fw, fw_name, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300717
718 if (ret < 0) {
719 wl1271_error("could not get firmware: %d", ret);
720 return ret;
721 }
722
723 if (fw->size % 4) {
724 wl1271_error("firmware size is not multiple of 32 bits: %zu",
725 fw->size);
726 ret = -EILSEQ;
727 goto out;
728 }
729
Arik Nemtsov166d5042010-10-16 21:44:57 +0200730 vfree(wl->fw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300731 wl->fw_len = fw->size;
Juuso Oikarinen1fba4972009-10-08 21:56:32 +0300732 wl->fw = vmalloc(wl->fw_len);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300733
734 if (!wl->fw) {
735 wl1271_error("could not allocate memory for the firmware");
736 ret = -ENOMEM;
737 goto out;
738 }
739
740 memcpy(wl->fw, fw->data, wl->fw_len);
Arik Nemtsov166d5042010-10-16 21:44:57 +0200741 wl->fw_bss_type = wl->bss_type;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300742 ret = 0;
743
744out:
745 release_firmware(fw);
746
747 return ret;
748}
749
750static int wl1271_fetch_nvs(struct wl1271 *wl)
751{
752 const struct firmware *fw;
753 int ret;
754
Teemu Paasikivi8197b712010-02-22 08:38:23 +0200755 ret = request_firmware(&fw, WL1271_NVS_NAME, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300756
757 if (ret < 0) {
758 wl1271_error("could not get nvs file: %d", ret);
759 return ret;
760 }
761
Julia Lawall929ebd32010-05-15 23:16:39 +0200762 wl->nvs = kmemdup(fw->data, sizeof(struct wl1271_nvs_file), GFP_KERNEL);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300763
764 if (!wl->nvs) {
765 wl1271_error("could not allocate memory for the nvs file");
766 ret = -ENOMEM;
767 goto out;
768 }
769
Juuso Oikarinen02fabb02010-08-19 04:41:15 +0200770 wl->nvs_len = fw->size;
771
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300772out:
773 release_firmware(fw);
774
775 return ret;
776}
777
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200778static void wl1271_recovery_work(struct work_struct *work)
779{
780 struct wl1271 *wl =
781 container_of(work, struct wl1271, recovery_work);
782
783 mutex_lock(&wl->mutex);
784
785 if (wl->state != WL1271_STATE_ON)
786 goto out;
787
788 wl1271_info("Hardware recovery in progress.");
789
Juuso Oikarinend25611d2010-09-30 10:43:27 +0200790 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
791 ieee80211_connection_loss(wl->vif);
792
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200793 /* reboot the chipset */
794 __wl1271_op_remove_interface(wl);
795 ieee80211_restart_hw(wl->hw);
796
797out:
798 mutex_unlock(&wl->mutex);
799}
800
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300801static void wl1271_fw_wakeup(struct wl1271 *wl)
802{
803 u32 elp_reg;
804
805 elp_reg = ELPCTRL_WAKE_UP;
Juuso Oikarinen74621412009-10-12 15:08:54 +0300806 wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300807}
808
809static int wl1271_setup(struct wl1271 *wl)
810{
811 wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL);
812 if (!wl->fw_status)
813 return -ENOMEM;
814
815 wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
816 if (!wl->tx_res_if) {
817 kfree(wl->fw_status);
818 return -ENOMEM;
819 }
820
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300821 return 0;
822}
823
824static int wl1271_chip_wakeup(struct wl1271 *wl)
825{
Juuso Oikarinen451de972009-10-12 15:08:46 +0300826 struct wl1271_partition_set partition;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300827 int ret = 0;
828
Juuso Oikarinen01ac17e2009-12-11 15:41:02 +0200829 msleep(WL1271_PRE_POWER_ON_SLEEP);
Ohad Ben-Cohen2cc78ff2010-09-16 01:22:04 +0200830 ret = wl1271_power_on(wl);
831 if (ret < 0)
832 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300833 msleep(WL1271_POWER_ON_SLEEP);
Teemu Paasikivi9b280722010-02-18 13:25:56 +0200834 wl1271_io_reset(wl);
835 wl1271_io_init(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300836
837 /* We don't need a real memory partition here, because we only want
838 * to use the registers at this point. */
Juuso Oikarinen451de972009-10-12 15:08:46 +0300839 memset(&partition, 0, sizeof(partition));
840 partition.reg.start = REGISTERS_BASE;
841 partition.reg.size = REGISTERS_DOWN_SIZE;
842 wl1271_set_partition(wl, &partition);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300843
844 /* ELP module wake up */
845 wl1271_fw_wakeup(wl);
846
847 /* whal_FwCtrl_BootSm() */
848
849 /* 0. read chip id from CHIP_ID */
Teemu Paasikivi7b048c52010-02-18 13:25:55 +0200850 wl->chip.id = wl1271_read32(wl, CHIP_ID_B);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300851
852 /* 1. check if chip id is valid */
853
854 switch (wl->chip.id) {
855 case CHIP_ID_1271_PG10:
856 wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
857 wl->chip.id);
858
859 ret = wl1271_setup(wl);
860 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200861 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300862 break;
863 case CHIP_ID_1271_PG20:
864 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
865 wl->chip.id);
866
867 ret = wl1271_setup(wl);
868 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200869 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300870 break;
871 default:
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200872 wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300873 ret = -ENODEV;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200874 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300875 }
876
Arik Nemtsov166d5042010-10-16 21:44:57 +0200877 /* Make sure the firmware type matches the BSS type */
878 if (wl->fw == NULL || wl->fw_bss_type != wl->bss_type) {
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300879 ret = wl1271_fetch_firmware(wl);
880 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200881 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300882 }
883
884 /* No NVS from netlink, try to get it from the filesystem */
885 if (wl->nvs == NULL) {
886 ret = wl1271_fetch_nvs(wl);
887 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200888 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300889 }
890
891out:
892 return ret;
893}
894
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300895int wl1271_plt_start(struct wl1271 *wl)
896{
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200897 int retries = WL1271_BOOT_RETRIES;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300898 int ret;
899
900 mutex_lock(&wl->mutex);
901
902 wl1271_notice("power up");
903
904 if (wl->state != WL1271_STATE_OFF) {
905 wl1271_error("cannot go into PLT state because not "
906 "in off state: %d", wl->state);
907 ret = -EBUSY;
908 goto out;
909 }
910
Arik Nemtsov166d5042010-10-16 21:44:57 +0200911 wl->bss_type = BSS_TYPE_STA_BSS;
912
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200913 while (retries) {
914 retries--;
915 ret = wl1271_chip_wakeup(wl);
916 if (ret < 0)
917 goto power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300918
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200919 ret = wl1271_boot(wl);
920 if (ret < 0)
921 goto power_off;
922
923 ret = wl1271_plt_init(wl);
924 if (ret < 0)
925 goto irq_disable;
926
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200927 wl->state = WL1271_STATE_PLT;
928 wl1271_notice("firmware booted in PLT mode (%s)",
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100929 wl->chip.fw_ver_str);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300930 goto out;
931
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200932irq_disable:
933 wl1271_disable_interrupts(wl);
934 mutex_unlock(&wl->mutex);
935 /* Unlocking the mutex in the middle of handling is
936 inherently unsafe. In this case we deem it safe to do,
937 because we need to let any possibly pending IRQ out of
938 the system (and while we are WL1271_STATE_OFF the IRQ
939 work function will not do anything.) Also, any other
940 possible concurrent operations will fail due to the
941 current state, hence the wl1271 struct should be safe. */
942 cancel_work_sync(&wl->irq_work);
943 mutex_lock(&wl->mutex);
944power_off:
945 wl1271_power_off(wl);
946 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300947
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200948 wl1271_error("firmware boot in PLT mode failed despite %d retries",
949 WL1271_BOOT_RETRIES);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300950out:
951 mutex_unlock(&wl->mutex);
952
953 return ret;
954}
955
Juuso Oikarinen4ae3fa82011-01-14 12:48:46 +0100956int __wl1271_plt_stop(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300957{
958 int ret = 0;
959
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300960 wl1271_notice("power down");
961
962 if (wl->state != WL1271_STATE_PLT) {
963 wl1271_error("cannot power down because not in PLT "
964 "state: %d", wl->state);
965 ret = -EBUSY;
966 goto out;
967 }
968
969 wl1271_disable_interrupts(wl);
970 wl1271_power_off(wl);
971
972 wl->state = WL1271_STATE_OFF;
Luciano Coelhobd5ea182009-10-13 12:47:58 +0300973 wl->rx_counter = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300974
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300975 mutex_unlock(&wl->mutex);
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +0200976 cancel_work_sync(&wl->irq_work);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200977 cancel_work_sync(&wl->recovery_work);
Juuso Oikarinen4ae3fa82011-01-14 12:48:46 +0100978 mutex_lock(&wl->mutex);
979out:
980 return ret;
981}
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +0200982
Juuso Oikarinen4ae3fa82011-01-14 12:48:46 +0100983int wl1271_plt_stop(struct wl1271 *wl)
984{
985 int ret;
986
987 mutex_lock(&wl->mutex);
988 ret = __wl1271_plt_stop(wl);
989 mutex_unlock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300990 return ret;
991}
992
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300993static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
994{
995 struct wl1271 *wl = hw->priv;
Juuso Oikarinen830fb672009-12-11 15:41:06 +0200996 unsigned long flags;
Juuso Oikarinen6742f552010-12-13 09:52:37 +0200997 int q;
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +0200998 u8 hlid = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300999
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001000 spin_lock_irqsave(&wl->wl_lock, flags);
Juuso Oikarinen6742f552010-12-13 09:52:37 +02001001 wl->tx_queue_count++;
Arik Nemtsovf4d08dd2011-02-23 00:22:24 +02001002
1003 /*
1004 * The workqueue is slow to process the tx_queue and we need stop
1005 * the queue here, otherwise the queue will get too long.
1006 */
1007 if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
1008 wl1271_debug(DEBUG_TX, "op_tx: stopping queues");
1009 ieee80211_stop_queues(wl->hw);
1010 set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
1011 }
1012
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001013 spin_unlock_irqrestore(&wl->wl_lock, flags);
1014
1015 /* queue the packet */
Juuso Oikarinen6742f552010-12-13 09:52:37 +02001016 q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02001017 if (wl->bss_type == BSS_TYPE_AP_BSS) {
1018 hlid = wl1271_tx_get_hlid(skb);
1019 wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q);
1020 skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
1021 } else {
1022 skb_queue_tail(&wl->tx_queue[q], skb);
1023 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001024
1025 /*
1026 * The chip specific setup must run before the first TX packet -
1027 * before that, the tx_work will not be initialized!
1028 */
1029
Ido Yariva5225502010-10-12 14:49:10 +02001030 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
1031 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001032
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001033 return NETDEV_TX_OK;
1034}
1035
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03001036static struct notifier_block wl1271_dev_notifier = {
1037 .notifier_call = wl1271_dev_notify,
1038};
1039
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001040static int wl1271_op_start(struct ieee80211_hw *hw)
1041{
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001042 wl1271_debug(DEBUG_MAC80211, "mac80211 start");
1043
1044 /*
1045 * We have to delay the booting of the hardware because
1046 * we need to know the local MAC address before downloading and
1047 * initializing the firmware. The MAC address cannot be changed
1048 * after boot, and without the proper MAC address, the firmware
1049 * will not function properly.
1050 *
1051 * The MAC address is first known when the corresponding interface
1052 * is added. That is where we will initialize the hardware.
Arik Nemtsov166d5042010-10-16 21:44:57 +02001053 *
1054 * In addition, we currently have different firmwares for AP and managed
1055 * operation. We will know which to boot according to interface type.
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001056 */
1057
1058 return 0;
1059}
1060
1061static void wl1271_op_stop(struct ieee80211_hw *hw)
1062{
1063 wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
1064}
1065
1066static int wl1271_op_add_interface(struct ieee80211_hw *hw,
1067 struct ieee80211_vif *vif)
1068{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001069 struct wl1271 *wl = hw->priv;
John W. Linvilleac01e942010-07-28 17:09:41 -04001070 struct wiphy *wiphy = hw->wiphy;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001071 int retries = WL1271_BOOT_RETRIES;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001072 int ret = 0;
Eliad Peller71125ab2010-10-28 21:46:43 +02001073 bool booted = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001074
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001075 wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
1076 vif->type, vif->addr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001077
1078 mutex_lock(&wl->mutex);
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001079 if (wl->vif) {
Eliad Peller71125ab2010-10-28 21:46:43 +02001080 wl1271_debug(DEBUG_MAC80211,
1081 "multiple vifs are not supported yet");
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001082 ret = -EBUSY;
1083 goto out;
1084 }
1085
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001086 switch (vif->type) {
1087 case NL80211_IFTYPE_STATION:
1088 wl->bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001089 wl->set_bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001090 break;
1091 case NL80211_IFTYPE_ADHOC:
1092 wl->bss_type = BSS_TYPE_IBSS;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001093 wl->set_bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001094 break;
Arik Nemtsov038d9252010-10-16 21:53:24 +02001095 case NL80211_IFTYPE_AP:
1096 wl->bss_type = BSS_TYPE_AP_BSS;
1097 break;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001098 default:
1099 ret = -EOPNOTSUPP;
1100 goto out;
1101 }
1102
1103 memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001104
1105 if (wl->state != WL1271_STATE_OFF) {
1106 wl1271_error("cannot start because not in off state: %d",
1107 wl->state);
1108 ret = -EBUSY;
1109 goto out;
1110 }
1111
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001112 while (retries) {
1113 retries--;
1114 ret = wl1271_chip_wakeup(wl);
1115 if (ret < 0)
1116 goto power_off;
1117
1118 ret = wl1271_boot(wl);
1119 if (ret < 0)
1120 goto power_off;
1121
1122 ret = wl1271_hw_init(wl);
1123 if (ret < 0)
1124 goto irq_disable;
1125
Eliad Peller71125ab2010-10-28 21:46:43 +02001126 booted = true;
1127 break;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001128
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001129irq_disable:
1130 wl1271_disable_interrupts(wl);
1131 mutex_unlock(&wl->mutex);
1132 /* Unlocking the mutex in the middle of handling is
1133 inherently unsafe. In this case we deem it safe to do,
1134 because we need to let any possibly pending IRQ out of
1135 the system (and while we are WL1271_STATE_OFF the IRQ
1136 work function will not do anything.) Also, any other
1137 possible concurrent operations will fail due to the
1138 current state, hence the wl1271 struct should be safe. */
1139 cancel_work_sync(&wl->irq_work);
1140 mutex_lock(&wl->mutex);
1141power_off:
1142 wl1271_power_off(wl);
1143 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001144
Eliad Peller71125ab2010-10-28 21:46:43 +02001145 if (!booted) {
1146 wl1271_error("firmware boot failed despite %d retries",
1147 WL1271_BOOT_RETRIES);
1148 goto out;
1149 }
1150
1151 wl->vif = vif;
1152 wl->state = WL1271_STATE_ON;
Levi, Shahar4b7fac72011-01-23 07:27:22 +01001153 wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str);
Eliad Peller71125ab2010-10-28 21:46:43 +02001154
1155 /* update hw/fw version info in wiphy struct */
1156 wiphy->hw_version = wl->chip.id;
Levi, Shahar4b7fac72011-01-23 07:27:22 +01001157 strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
Eliad Peller71125ab2010-10-28 21:46:43 +02001158 sizeof(wiphy->fw_version));
1159
Luciano Coelhofb6a6812010-12-03 17:05:40 +02001160 /*
1161 * Now we know if 11a is supported (info from the NVS), so disable
1162 * 11a channels if not supported
1163 */
1164 if (!wl->enable_11a)
1165 wiphy->bands[IEEE80211_BAND_5GHZ]->n_channels = 0;
1166
1167 wl1271_debug(DEBUG_MAC80211, "11a is %ssupported",
1168 wl->enable_11a ? "" : "not ");
1169
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +03001170out:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001171 mutex_unlock(&wl->mutex);
1172
Juuso Oikarineneb887df2010-07-08 17:49:58 +03001173 if (!ret)
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001174 list_add(&wl->list, &wl_list);
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001175
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001176 return ret;
1177}
1178
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001179static void __wl1271_op_remove_interface(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001180{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001181 int i;
1182
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001183 wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001184
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001185 wl1271_info("down");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001186
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001187 list_del(&wl->list);
1188
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001189 WARN_ON(wl->state != WL1271_STATE_ON);
1190
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001191 /* enable dyn ps just in case (if left on due to fw crash etc) */
Juuso Oikarinen9a547bf2010-07-08 17:50:04 +03001192 if (wl->bss_type == BSS_TYPE_STA_BSS)
Juuso Oikarinenf532be62010-07-08 17:50:05 +03001193 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001194
Luciano Coelho08688d62010-07-08 17:50:07 +03001195 if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
Luciano Coelho08688d62010-07-08 17:50:07 +03001196 wl->scan.state = WL1271_SCAN_STATE_IDLE;
1197 kfree(wl->scan.scanned_ch);
1198 wl->scan.scanned_ch = NULL;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02001199 wl->scan.req = NULL;
Juuso Oikarinen76a029f2010-07-29 04:54:45 +03001200 ieee80211_scan_completed(wl->hw, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001201 }
1202
1203 wl->state = WL1271_STATE_OFF;
1204
1205 wl1271_disable_interrupts(wl);
1206
1207 mutex_unlock(&wl->mutex);
1208
Juuso Oikarinen78abd322010-09-21 06:23:32 +02001209 cancel_delayed_work_sync(&wl->scan_complete_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001210 cancel_work_sync(&wl->irq_work);
1211 cancel_work_sync(&wl->tx_work);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001212 cancel_delayed_work_sync(&wl->pspoll_work);
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +02001213 cancel_delayed_work_sync(&wl->elp_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001214
1215 mutex_lock(&wl->mutex);
1216
1217 /* let's notify MAC80211 about the remaining pending TX frames */
Juuso Oikarinen781608c2010-05-24 11:18:17 +03001218 wl1271_tx_reset(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001219 wl1271_power_off(wl);
1220
1221 memset(wl->bssid, 0, ETH_ALEN);
1222 memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1);
1223 wl->ssid_len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001224 wl->bss_type = MAX_BSS_TYPE;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001225 wl->set_bss_type = MAX_BSS_TYPE;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001226 wl->band = IEEE80211_BAND_2GHZ;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001227
1228 wl->rx_counter = 0;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02001229 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001230 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
1231 wl->tx_blocks_available = 0;
1232 wl->tx_results_count = 0;
1233 wl->tx_packets_count = 0;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001234 wl->tx_security_last_seq = 0;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001235 wl->tx_security_seq = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001236 wl->time_offset = 0;
1237 wl->session_counter = 0;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001238 wl->rate_set = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001239 wl->flags = 0;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001240 wl->vif = NULL;
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001241 wl->filters = 0;
Arik Nemtsov7f179b42010-10-16 21:39:06 +02001242 wl1271_free_ap_keys(wl);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02001243 memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));
Luciano Coelhod6e19d12009-10-12 15:08:43 +03001244
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001245 for (i = 0; i < NUM_TX_QUEUES; i++)
1246 wl->tx_blocks_freed[i] = 0;
1247
1248 wl1271_debugfs_reset(wl);
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03001249
1250 kfree(wl->fw_status);
1251 wl->fw_status = NULL;
1252 kfree(wl->tx_res_if);
1253 wl->tx_res_if = NULL;
1254 kfree(wl->target_mem_map);
1255 wl->target_mem_map = NULL;
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001256}
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03001257
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001258static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
1259 struct ieee80211_vif *vif)
1260{
1261 struct wl1271 *wl = hw->priv;
1262
1263 mutex_lock(&wl->mutex);
Juuso Oikarinen67353292010-11-18 15:19:02 +02001264 /*
1265 * wl->vif can be null here if someone shuts down the interface
1266 * just when hardware recovery has been started.
1267 */
1268 if (wl->vif) {
1269 WARN_ON(wl->vif != vif);
1270 __wl1271_op_remove_interface(wl);
1271 }
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001272
Juuso Oikarinen67353292010-11-18 15:19:02 +02001273 mutex_unlock(&wl->mutex);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001274 cancel_work_sync(&wl->recovery_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001275}
1276
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001277static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
1278{
Arik Nemtsovae113b52010-10-16 18:45:07 +02001279 wl1271_set_default_filters(wl);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001280
1281 /* combine requested filters with current filter config */
1282 filters = wl->filters | filters;
1283
1284 wl1271_debug(DEBUG_FILTERS, "RX filters set: ");
1285
1286 if (filters & FIF_PROMISC_IN_BSS) {
1287 wl1271_debug(DEBUG_FILTERS, " - FIF_PROMISC_IN_BSS");
1288 wl->rx_config &= ~CFG_UNI_FILTER_EN;
1289 wl->rx_config |= CFG_BSSID_FILTER_EN;
1290 }
1291 if (filters & FIF_BCN_PRBRESP_PROMISC) {
1292 wl1271_debug(DEBUG_FILTERS, " - FIF_BCN_PRBRESP_PROMISC");
1293 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1294 wl->rx_config &= ~CFG_SSID_FILTER_EN;
1295 }
1296 if (filters & FIF_OTHER_BSS) {
1297 wl1271_debug(DEBUG_FILTERS, " - FIF_OTHER_BSS");
1298 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1299 }
1300 if (filters & FIF_CONTROL) {
1301 wl1271_debug(DEBUG_FILTERS, " - FIF_CONTROL");
1302 wl->rx_filter |= CFG_RX_CTL_EN;
1303 }
1304 if (filters & FIF_FCSFAIL) {
1305 wl1271_debug(DEBUG_FILTERS, " - FIF_FCSFAIL");
1306 wl->rx_filter |= CFG_RX_FCS_ERROR;
1307 }
1308}
1309
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001310static int wl1271_dummy_join(struct wl1271 *wl)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001311{
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001312 int ret = 0;
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001313 /* we need to use a dummy BSSID for now */
1314 static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
1315 0xad, 0xbe, 0xef };
1316
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001317 memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
1318
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001319 /* pass through frames from all BSS */
1320 wl1271_configure_filters(wl, FIF_OTHER_BSS);
1321
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001322 ret = wl1271_cmd_join(wl, wl->set_bss_type);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001323 if (ret < 0)
1324 goto out;
1325
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001326 set_bit(WL1271_FLAG_JOINED, &wl->flags);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001327
1328out:
1329 return ret;
1330}
1331
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001332static int wl1271_join(struct wl1271 *wl, bool set_assoc)
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001333{
1334 int ret;
1335
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001336 /*
1337 * One of the side effects of the JOIN command is that is clears
1338 * WPA/WPA2 keys from the chipset. Performing a JOIN while associated
1339 * to a WPA/WPA2 access point will therefore kill the data-path.
1340 * Currently there is no supported scenario for JOIN during
1341 * association - if it becomes a supported scenario, the WPA/WPA2 keys
1342 * must be handled somehow.
1343 *
1344 */
1345 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1346 wl1271_info("JOIN while associated.");
1347
1348 if (set_assoc)
1349 set_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
1350
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001351 ret = wl1271_cmd_join(wl, wl->set_bss_type);
1352 if (ret < 0)
1353 goto out;
1354
1355 set_bit(WL1271_FLAG_JOINED, &wl->flags);
1356
1357 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1358 goto out;
1359
1360 /*
1361 * The join command disable the keep-alive mode, shut down its process,
1362 * and also clear the template config, so we need to reset it all after
1363 * the join. The acx_aid starts the keep-alive process, and the order
1364 * of the commands below is relevant.
1365 */
1366 ret = wl1271_acx_keep_alive_mode(wl, true);
1367 if (ret < 0)
1368 goto out;
1369
1370 ret = wl1271_acx_aid(wl, wl->aid);
1371 if (ret < 0)
1372 goto out;
1373
1374 ret = wl1271_cmd_build_klv_null_data(wl);
1375 if (ret < 0)
1376 goto out;
1377
1378 ret = wl1271_acx_keep_alive_config(wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
1379 ACX_KEEP_ALIVE_TPL_VALID);
1380 if (ret < 0)
1381 goto out;
1382
1383out:
1384 return ret;
1385}
1386
1387static int wl1271_unjoin(struct wl1271 *wl)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001388{
1389 int ret;
1390
1391 /* to stop listening to a channel, we disconnect */
1392 ret = wl1271_cmd_disconnect(wl);
1393 if (ret < 0)
1394 goto out;
1395
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001396 clear_bit(WL1271_FLAG_JOINED, &wl->flags);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001397 memset(wl->bssid, 0, ETH_ALEN);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001398
1399 /* stop filterting packets based on bssid */
1400 wl1271_configure_filters(wl, FIF_OTHER_BSS);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001401
1402out:
1403 return ret;
1404}
1405
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001406static void wl1271_set_band_rate(struct wl1271 *wl)
1407{
1408 if (wl->band == IEEE80211_BAND_2GHZ)
1409 wl->basic_rate_set = wl->conf.tx.basic_rate;
1410 else
1411 wl->basic_rate_set = wl->conf.tx.basic_rate_5;
1412}
1413
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001414static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle)
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03001415{
1416 int ret;
1417
1418 if (idle) {
1419 if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
1420 ret = wl1271_unjoin(wl);
1421 if (ret < 0)
1422 goto out;
1423 }
Arik Nemtsove0fe3712010-10-16 18:19:53 +02001424 wl->rate_set = wl1271_tx_min_rate_get(wl);
Arik Nemtsov79b223f2010-10-16 17:52:59 +02001425 ret = wl1271_acx_sta_rate_policies(wl);
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03001426 if (ret < 0)
1427 goto out;
1428 ret = wl1271_acx_keep_alive_config(
1429 wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
1430 ACX_KEEP_ALIVE_TPL_INVALID);
1431 if (ret < 0)
1432 goto out;
1433 set_bit(WL1271_FLAG_IDLE, &wl->flags);
1434 } else {
1435 /* increment the session counter */
1436 wl->session_counter++;
1437 if (wl->session_counter >= SESSION_COUNTER_MAX)
1438 wl->session_counter = 0;
1439 ret = wl1271_dummy_join(wl);
1440 if (ret < 0)
1441 goto out;
1442 clear_bit(WL1271_FLAG_IDLE, &wl->flags);
1443 }
1444
1445out:
1446 return ret;
1447}
1448
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001449static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
1450{
1451 struct wl1271 *wl = hw->priv;
1452 struct ieee80211_conf *conf = &hw->conf;
1453 int channel, ret = 0;
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001454 bool is_ap;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001455
1456 channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
1457
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001458 wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s"
1459 " changed 0x%x",
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001460 channel,
1461 conf->flags & IEEE80211_CONF_PS ? "on" : "off",
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001462 conf->power_level,
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001463 conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
1464 changed);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001465
Juuso Oikarinen781608c2010-05-24 11:18:17 +03001466 /*
1467 * mac80211 will go to idle nearly immediately after transmitting some
1468 * frames, such as the deauth. To make sure those frames reach the air,
1469 * wait here until the TX queue is fully flushed.
1470 */
1471 if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
1472 (conf->flags & IEEE80211_CONF_IDLE))
1473 wl1271_tx_flush(wl);
1474
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001475 mutex_lock(&wl->mutex);
1476
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02001477 if (unlikely(wl->state == WL1271_STATE_OFF)) {
1478 ret = -EAGAIN;
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001479 goto out;
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02001480 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001481
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001482 is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
1483
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001484 ret = wl1271_ps_elp_wakeup(wl, false);
1485 if (ret < 0)
1486 goto out;
1487
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001488 /* if the channel changes while joined, join again */
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001489 if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
1490 ((wl->band != conf->channel->band) ||
1491 (wl->channel != channel))) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001492 wl->band = conf->channel->band;
1493 wl->channel = channel;
1494
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001495 if (!is_ap) {
1496 /*
1497 * FIXME: the mac80211 should really provide a fixed
1498 * rate to use here. for now, just use the smallest
1499 * possible rate for the band as a fixed rate for
1500 * association frames and other control messages.
1501 */
1502 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1503 wl1271_set_band_rate(wl);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001504
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001505 wl->basic_rate = wl1271_tx_min_rate_get(wl);
1506 ret = wl1271_acx_sta_rate_policies(wl);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001507 if (ret < 0)
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001508 wl1271_warning("rate policy for channel "
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001509 "failed %d", ret);
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001510
1511 if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
1512 ret = wl1271_join(wl, false);
1513 if (ret < 0)
1514 wl1271_warning("cmd join on channel "
1515 "failed %d", ret);
1516 }
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001517 }
1518 }
1519
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001520 if (changed & IEEE80211_CONF_CHANGE_IDLE && !is_ap) {
1521 ret = wl1271_sta_handle_idle(wl,
1522 conf->flags & IEEE80211_CONF_IDLE);
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03001523 if (ret < 0)
1524 wl1271_warning("idle mode change failed %d", ret);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001525 }
1526
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001527 /*
1528 * if mac80211 changes the PSM mode, make sure the mode is not
1529 * incorrectly changed after the pspoll failure active window.
1530 */
1531 if (changed & IEEE80211_CONF_CHANGE_PS)
1532 clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
1533
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001534 if (conf->flags & IEEE80211_CONF_PS &&
1535 !test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
1536 set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001537
1538 /*
1539 * We enter PSM only if we're already associated.
1540 * If we're not, we'll enter it when joining an SSID,
1541 * through the bss_info_changed() hook.
1542 */
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001543 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02001544 wl1271_debug(DEBUG_PSM, "psm enabled");
Juuso Oikarinend8c42c02010-02-18 13:25:36 +02001545 ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02001546 wl->basic_rate, true);
Juuso Oikarinenaf5e0842009-12-11 15:41:00 +02001547 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001548 } else if (!(conf->flags & IEEE80211_CONF_PS) &&
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001549 test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02001550 wl1271_debug(DEBUG_PSM, "psm disabled");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001551
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001552 clear_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001553
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001554 if (test_bit(WL1271_FLAG_PSM, &wl->flags))
Juuso Oikarinend8c42c02010-02-18 13:25:36 +02001555 ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02001556 wl->basic_rate, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001557 }
1558
1559 if (conf->power_level != wl->power_level) {
1560 ret = wl1271_acx_tx_power(wl, conf->power_level);
1561 if (ret < 0)
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02001562 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001563
1564 wl->power_level = conf->power_level;
1565 }
1566
1567out_sleep:
1568 wl1271_ps_elp_sleep(wl);
1569
1570out:
1571 mutex_unlock(&wl->mutex);
1572
1573 return ret;
1574}
1575
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001576struct wl1271_filter_params {
1577 bool enabled;
1578 int mc_list_length;
1579 u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
1580};
1581
Jiri Pirko22bedad2010-04-01 21:22:57 +00001582static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw,
1583 struct netdev_hw_addr_list *mc_list)
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001584{
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001585 struct wl1271_filter_params *fp;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001586 struct netdev_hw_addr *ha;
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001587 struct wl1271 *wl = hw->priv;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001588
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001589 if (unlikely(wl->state == WL1271_STATE_OFF))
1590 return 0;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001591
Juuso Oikarinen74441132009-10-13 12:47:53 +03001592 fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001593 if (!fp) {
1594 wl1271_error("Out of memory setting filters.");
1595 return 0;
1596 }
1597
1598 /* update multicast filtering parameters */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001599 fp->mc_list_length = 0;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001600 if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) {
1601 fp->enabled = false;
1602 } else {
1603 fp->enabled = true;
1604 netdev_hw_addr_list_for_each(ha, mc_list) {
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001605 memcpy(fp->mc_list[fp->mc_list_length],
Jiri Pirko22bedad2010-04-01 21:22:57 +00001606 ha->addr, ETH_ALEN);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001607 fp->mc_list_length++;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001608 }
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001609 }
1610
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001611 return (u64)(unsigned long)fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001612}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001613
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001614#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
1615 FIF_ALLMULTI | \
1616 FIF_FCSFAIL | \
1617 FIF_BCN_PRBRESP_PROMISC | \
1618 FIF_CONTROL | \
1619 FIF_OTHER_BSS)
1620
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001621static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
1622 unsigned int changed,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001623 unsigned int *total, u64 multicast)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001624{
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001625 struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001626 struct wl1271 *wl = hw->priv;
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001627 int ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001628
Arik Nemtsov7d057862010-10-16 19:25:35 +02001629 wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter changed %x"
1630 " total %x", changed, *total);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001631
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001632 mutex_lock(&wl->mutex);
1633
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001634 *total &= WL1271_SUPPORTED_FILTERS;
1635 changed &= WL1271_SUPPORTED_FILTERS;
1636
1637 if (unlikely(wl->state == WL1271_STATE_OFF))
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001638 goto out;
1639
1640 ret = wl1271_ps_elp_wakeup(wl, false);
1641 if (ret < 0)
1642 goto out;
1643
Arik Nemtsov7d057862010-10-16 19:25:35 +02001644 if (wl->bss_type != BSS_TYPE_AP_BSS) {
1645 if (*total & FIF_ALLMULTI)
1646 ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
1647 else if (fp)
1648 ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
1649 fp->mc_list,
1650 fp->mc_list_length);
1651 if (ret < 0)
1652 goto out_sleep;
1653 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001654
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001655 /* determine, whether supported filter values have changed */
1656 if (changed == 0)
1657 goto out_sleep;
1658
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001659 /* configure filters */
1660 wl->filters = *total;
1661 wl1271_configure_filters(wl, 0);
1662
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001663 /* apply configured filters */
1664 ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
1665 if (ret < 0)
1666 goto out_sleep;
1667
1668out_sleep:
1669 wl1271_ps_elp_sleep(wl);
1670
1671out:
1672 mutex_unlock(&wl->mutex);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001673 kfree(fp);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001674}
1675
Arik Nemtsov7f179b42010-10-16 21:39:06 +02001676static int wl1271_record_ap_key(struct wl1271 *wl, u8 id, u8 key_type,
1677 u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
1678 u16 tx_seq_16)
1679{
1680 struct wl1271_ap_key *ap_key;
1681 int i;
1682
1683 wl1271_debug(DEBUG_CRYPT, "record ap key id %d", (int)id);
1684
1685 if (key_size > MAX_KEY_SIZE)
1686 return -EINVAL;
1687
1688 /*
1689 * Find next free entry in ap_keys. Also check we are not replacing
1690 * an existing key.
1691 */
1692 for (i = 0; i < MAX_NUM_KEYS; i++) {
1693 if (wl->recorded_ap_keys[i] == NULL)
1694 break;
1695
1696 if (wl->recorded_ap_keys[i]->id == id) {
1697 wl1271_warning("trying to record key replacement");
1698 return -EINVAL;
1699 }
1700 }
1701
1702 if (i == MAX_NUM_KEYS)
1703 return -EBUSY;
1704
1705 ap_key = kzalloc(sizeof(*ap_key), GFP_KERNEL);
1706 if (!ap_key)
1707 return -ENOMEM;
1708
1709 ap_key->id = id;
1710 ap_key->key_type = key_type;
1711 ap_key->key_size = key_size;
1712 memcpy(ap_key->key, key, key_size);
1713 ap_key->hlid = hlid;
1714 ap_key->tx_seq_32 = tx_seq_32;
1715 ap_key->tx_seq_16 = tx_seq_16;
1716
1717 wl->recorded_ap_keys[i] = ap_key;
1718 return 0;
1719}
1720
1721static void wl1271_free_ap_keys(struct wl1271 *wl)
1722{
1723 int i;
1724
1725 for (i = 0; i < MAX_NUM_KEYS; i++) {
1726 kfree(wl->recorded_ap_keys[i]);
1727 wl->recorded_ap_keys[i] = NULL;
1728 }
1729}
1730
1731static int wl1271_ap_init_hwenc(struct wl1271 *wl)
1732{
1733 int i, ret = 0;
1734 struct wl1271_ap_key *key;
1735 bool wep_key_added = false;
1736
1737 for (i = 0; i < MAX_NUM_KEYS; i++) {
1738 if (wl->recorded_ap_keys[i] == NULL)
1739 break;
1740
1741 key = wl->recorded_ap_keys[i];
1742 ret = wl1271_cmd_set_ap_key(wl, KEY_ADD_OR_REPLACE,
1743 key->id, key->key_type,
1744 key->key_size, key->key,
1745 key->hlid, key->tx_seq_32,
1746 key->tx_seq_16);
1747 if (ret < 0)
1748 goto out;
1749
1750 if (key->key_type == KEY_WEP)
1751 wep_key_added = true;
1752 }
1753
1754 if (wep_key_added) {
1755 ret = wl1271_cmd_set_ap_default_wep_key(wl, wl->default_key);
1756 if (ret < 0)
1757 goto out;
1758 }
1759
1760out:
1761 wl1271_free_ap_keys(wl);
1762 return ret;
1763}
1764
1765static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
1766 u8 key_size, const u8 *key, u32 tx_seq_32,
1767 u16 tx_seq_16, struct ieee80211_sta *sta)
1768{
1769 int ret;
1770 bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
1771
1772 if (is_ap) {
1773 struct wl1271_station *wl_sta;
1774 u8 hlid;
1775
1776 if (sta) {
1777 wl_sta = (struct wl1271_station *)sta->drv_priv;
1778 hlid = wl_sta->hlid;
1779 } else {
1780 hlid = WL1271_AP_BROADCAST_HLID;
1781 }
1782
1783 if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
1784 /*
1785 * We do not support removing keys after AP shutdown.
1786 * Pretend we do to make mac80211 happy.
1787 */
1788 if (action != KEY_ADD_OR_REPLACE)
1789 return 0;
1790
1791 ret = wl1271_record_ap_key(wl, id,
1792 key_type, key_size,
1793 key, hlid, tx_seq_32,
1794 tx_seq_16);
1795 } else {
1796 ret = wl1271_cmd_set_ap_key(wl, action,
1797 id, key_type, key_size,
1798 key, hlid, tx_seq_32,
1799 tx_seq_16);
1800 }
1801
1802 if (ret < 0)
1803 return ret;
1804 } else {
1805 const u8 *addr;
1806 static const u8 bcast_addr[ETH_ALEN] = {
1807 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
1808 };
1809
1810 addr = sta ? sta->addr : bcast_addr;
1811
1812 if (is_zero_ether_addr(addr)) {
1813 /* We dont support TX only encryption */
1814 return -EOPNOTSUPP;
1815 }
1816
1817 /* The wl1271 does not allow to remove unicast keys - they
1818 will be cleared automatically on next CMD_JOIN. Ignore the
1819 request silently, as we dont want the mac80211 to emit
1820 an error message. */
1821 if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr))
1822 return 0;
1823
1824 ret = wl1271_cmd_set_sta_key(wl, action,
1825 id, key_type, key_size,
1826 key, addr, tx_seq_32,
1827 tx_seq_16);
1828 if (ret < 0)
1829 return ret;
1830
1831 /* the default WEP key needs to be configured at least once */
1832 if (key_type == KEY_WEP) {
1833 ret = wl1271_cmd_set_sta_default_wep_key(wl,
1834 wl->default_key);
1835 if (ret < 0)
1836 return ret;
1837 }
1838 }
1839
1840 return 0;
1841}
1842
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001843static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
1844 struct ieee80211_vif *vif,
1845 struct ieee80211_sta *sta,
1846 struct ieee80211_key_conf *key_conf)
1847{
1848 struct wl1271 *wl = hw->priv;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001849 int ret;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001850 u32 tx_seq_32 = 0;
1851 u16 tx_seq_16 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001852 u8 key_type;
1853
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001854 wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
1855
Arik Nemtsov7f179b42010-10-16 21:39:06 +02001856 wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001857 wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
Johannes Berg97359d12010-08-10 09:46:38 +02001858 key_conf->cipher, key_conf->keyidx,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001859 key_conf->keylen, key_conf->flags);
1860 wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
1861
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001862 mutex_lock(&wl->mutex);
1863
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02001864 if (unlikely(wl->state == WL1271_STATE_OFF)) {
1865 ret = -EAGAIN;
1866 goto out_unlock;
1867 }
1868
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001869 ret = wl1271_ps_elp_wakeup(wl, false);
1870 if (ret < 0)
1871 goto out_unlock;
1872
Johannes Berg97359d12010-08-10 09:46:38 +02001873 switch (key_conf->cipher) {
1874 case WLAN_CIPHER_SUITE_WEP40:
1875 case WLAN_CIPHER_SUITE_WEP104:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001876 key_type = KEY_WEP;
1877
1878 key_conf->hw_key_idx = key_conf->keyidx;
1879 break;
Johannes Berg97359d12010-08-10 09:46:38 +02001880 case WLAN_CIPHER_SUITE_TKIP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001881 key_type = KEY_TKIP;
1882
1883 key_conf->hw_key_idx = key_conf->keyidx;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001884 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1885 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001886 break;
Johannes Berg97359d12010-08-10 09:46:38 +02001887 case WLAN_CIPHER_SUITE_CCMP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001888 key_type = KEY_AES;
1889
1890 key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001891 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1892 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001893 break;
Juuso Oikarinen7a557242010-09-27 12:42:07 +02001894 case WL1271_CIPHER_SUITE_GEM:
1895 key_type = KEY_GEM;
1896 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1897 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
1898 break;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001899 default:
Johannes Berg97359d12010-08-10 09:46:38 +02001900 wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001901
1902 ret = -EOPNOTSUPP;
1903 goto out_sleep;
1904 }
1905
1906 switch (cmd) {
1907 case SET_KEY:
Arik Nemtsov7f179b42010-10-16 21:39:06 +02001908 ret = wl1271_set_key(wl, KEY_ADD_OR_REPLACE,
1909 key_conf->keyidx, key_type,
1910 key_conf->keylen, key_conf->key,
1911 tx_seq_32, tx_seq_16, sta);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001912 if (ret < 0) {
1913 wl1271_error("Could not add or replace key");
1914 goto out_sleep;
1915 }
1916 break;
1917
1918 case DISABLE_KEY:
Arik Nemtsov7f179b42010-10-16 21:39:06 +02001919 ret = wl1271_set_key(wl, KEY_REMOVE,
1920 key_conf->keyidx, key_type,
1921 key_conf->keylen, key_conf->key,
1922 0, 0, sta);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001923 if (ret < 0) {
1924 wl1271_error("Could not remove key");
1925 goto out_sleep;
1926 }
1927 break;
1928
1929 default:
1930 wl1271_error("Unsupported key cmd 0x%x", cmd);
1931 ret = -EOPNOTSUPP;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001932 break;
1933 }
1934
1935out_sleep:
1936 wl1271_ps_elp_sleep(wl);
1937
1938out_unlock:
1939 mutex_unlock(&wl->mutex);
1940
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001941 return ret;
1942}
1943
1944static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
Johannes Berga060bbf2010-04-27 11:59:34 +02001945 struct ieee80211_vif *vif,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001946 struct cfg80211_scan_request *req)
1947{
1948 struct wl1271 *wl = hw->priv;
1949 int ret;
1950 u8 *ssid = NULL;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001951 size_t len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001952
1953 wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
1954
1955 if (req->n_ssids) {
1956 ssid = req->ssids[0].ssid;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001957 len = req->ssids[0].ssid_len;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001958 }
1959
1960 mutex_lock(&wl->mutex);
1961
Juuso Oikarinenb739a422010-10-26 13:24:38 +02001962 if (wl->state == WL1271_STATE_OFF) {
1963 /*
1964 * We cannot return -EBUSY here because cfg80211 will expect
1965 * a call to ieee80211_scan_completed if we do - in this case
1966 * there won't be any call.
1967 */
1968 ret = -EAGAIN;
1969 goto out;
1970 }
1971
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001972 ret = wl1271_ps_elp_wakeup(wl, false);
1973 if (ret < 0)
1974 goto out;
1975
Luciano Coelho5924f892010-08-04 03:46:22 +03001976 ret = wl1271_scan(hw->priv, ssid, len, req);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001977
1978 wl1271_ps_elp_sleep(wl);
1979
1980out:
1981 mutex_unlock(&wl->mutex);
1982
1983 return ret;
1984}
1985
Arik Nemtsov68d069c2010-11-08 10:51:07 +01001986static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
1987{
1988 struct wl1271 *wl = hw->priv;
1989 int ret = 0;
1990
1991 mutex_lock(&wl->mutex);
1992
1993 if (unlikely(wl->state == WL1271_STATE_OFF)) {
1994 ret = -EAGAIN;
1995 goto out;
1996 }
1997
1998 ret = wl1271_ps_elp_wakeup(wl, false);
1999 if (ret < 0)
2000 goto out;
2001
2002 ret = wl1271_acx_frag_threshold(wl, (u16)value);
2003 if (ret < 0)
2004 wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret);
2005
2006 wl1271_ps_elp_sleep(wl);
2007
2008out:
2009 mutex_unlock(&wl->mutex);
2010
2011 return ret;
2012}
2013
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002014static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
2015{
2016 struct wl1271 *wl = hw->priv;
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03002017 int ret = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002018
2019 mutex_lock(&wl->mutex);
2020
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002021 if (unlikely(wl->state == WL1271_STATE_OFF)) {
2022 ret = -EAGAIN;
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03002023 goto out;
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002024 }
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03002025
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002026 ret = wl1271_ps_elp_wakeup(wl, false);
2027 if (ret < 0)
2028 goto out;
2029
2030 ret = wl1271_acx_rts_threshold(wl, (u16) value);
2031 if (ret < 0)
2032 wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret);
2033
2034 wl1271_ps_elp_sleep(wl);
2035
2036out:
2037 mutex_unlock(&wl->mutex);
2038
2039 return ret;
2040}
2041
Arik Nemtsove78a2872010-10-16 19:07:21 +02002042static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb,
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002043 int offset)
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002044{
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002045 u8 *ptr = skb->data + offset;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002046
2047 /* find the location of the ssid in the beacon */
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002048 while (ptr < skb->data + skb->len) {
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002049 if (ptr[0] == WLAN_EID_SSID) {
2050 wl->ssid_len = ptr[1];
2051 memcpy(wl->ssid, ptr+2, wl->ssid_len);
Arik Nemtsove78a2872010-10-16 19:07:21 +02002052 return 0;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002053 }
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002054 ptr += (ptr[1] + 2);
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002055 }
Arik Nemtsove78a2872010-10-16 19:07:21 +02002056
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002057 wl1271_error("No SSID in IEs!\n");
Arik Nemtsove78a2872010-10-16 19:07:21 +02002058 return -ENOENT;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002059}
2060
Arik Nemtsove78a2872010-10-16 19:07:21 +02002061static int wl1271_bss_erp_info_changed(struct wl1271 *wl,
2062 struct ieee80211_bss_conf *bss_conf,
2063 u32 changed)
2064{
2065 int ret = 0;
2066
2067 if (changed & BSS_CHANGED_ERP_SLOT) {
2068 if (bss_conf->use_short_slot)
2069 ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
2070 else
2071 ret = wl1271_acx_slot(wl, SLOT_TIME_LONG);
2072 if (ret < 0) {
2073 wl1271_warning("Set slot time failed %d", ret);
2074 goto out;
2075 }
2076 }
2077
2078 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
2079 if (bss_conf->use_short_preamble)
2080 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT);
2081 else
2082 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG);
2083 }
2084
2085 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
2086 if (bss_conf->use_cts_prot)
2087 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE);
2088 else
2089 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE);
2090 if (ret < 0) {
2091 wl1271_warning("Set ctsprotect failed %d", ret);
2092 goto out;
2093 }
2094 }
2095
2096out:
2097 return ret;
2098}
2099
2100static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
2101 struct ieee80211_vif *vif,
2102 struct ieee80211_bss_conf *bss_conf,
2103 u32 changed)
2104{
2105 bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
2106 int ret = 0;
2107
2108 if ((changed & BSS_CHANGED_BEACON_INT)) {
2109 wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d",
2110 bss_conf->beacon_int);
2111
2112 wl->beacon_int = bss_conf->beacon_int;
2113 }
2114
2115 if ((changed & BSS_CHANGED_BEACON)) {
2116 struct ieee80211_hdr *hdr;
2117 int ieoffset = offsetof(struct ieee80211_mgmt,
2118 u.beacon.variable);
2119 struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
2120 u16 tmpl_id;
2121
2122 if (!beacon)
2123 goto out;
2124
2125 wl1271_debug(DEBUG_MASTER, "beacon updated");
2126
2127 ret = wl1271_ssid_set(wl, beacon, ieoffset);
2128 if (ret < 0) {
2129 dev_kfree_skb(beacon);
2130 goto out;
2131 }
2132 tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
2133 CMD_TEMPL_BEACON;
2134 ret = wl1271_cmd_template_set(wl, tmpl_id,
2135 beacon->data,
2136 beacon->len, 0,
2137 wl1271_tx_min_rate_get(wl));
2138 if (ret < 0) {
2139 dev_kfree_skb(beacon);
2140 goto out;
2141 }
2142
2143 hdr = (struct ieee80211_hdr *) beacon->data;
2144 hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
2145 IEEE80211_STYPE_PROBE_RESP);
2146
2147 tmpl_id = is_ap ? CMD_TEMPL_AP_PROBE_RESPONSE :
2148 CMD_TEMPL_PROBE_RESPONSE;
2149 ret = wl1271_cmd_template_set(wl,
2150 tmpl_id,
2151 beacon->data,
2152 beacon->len, 0,
2153 wl1271_tx_min_rate_get(wl));
2154 dev_kfree_skb(beacon);
2155 if (ret < 0)
2156 goto out;
2157 }
2158
2159out:
2160 return ret;
2161}
2162
2163/* AP mode changes */
2164static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002165 struct ieee80211_vif *vif,
2166 struct ieee80211_bss_conf *bss_conf,
2167 u32 changed)
2168{
Arik Nemtsove78a2872010-10-16 19:07:21 +02002169 int ret = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002170
Arik Nemtsove78a2872010-10-16 19:07:21 +02002171 if ((changed & BSS_CHANGED_BASIC_RATES)) {
2172 u32 rates = bss_conf->basic_rates;
2173 struct conf_tx_rate_class mgmt_rc;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002174
Arik Nemtsove78a2872010-10-16 19:07:21 +02002175 wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates);
2176 wl->basic_rate = wl1271_tx_min_rate_get(wl);
2177 wl1271_debug(DEBUG_AP, "basic rates: 0x%x",
2178 wl->basic_rate_set);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002179
Arik Nemtsove78a2872010-10-16 19:07:21 +02002180 /* update the AP management rate policy with the new rates */
2181 mgmt_rc.enabled_rates = wl->basic_rate_set;
2182 mgmt_rc.long_retry_limit = 10;
2183 mgmt_rc.short_retry_limit = 10;
2184 mgmt_rc.aflags = 0;
2185 ret = wl1271_acx_ap_rate_policy(wl, &mgmt_rc,
2186 ACX_TX_AP_MODE_MGMT_RATE);
2187 if (ret < 0) {
2188 wl1271_error("AP mgmt policy change failed %d", ret);
2189 goto out;
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02002190 }
2191 }
2192
Arik Nemtsove78a2872010-10-16 19:07:21 +02002193 ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed);
2194 if (ret < 0)
2195 goto out;
2196
2197 if ((changed & BSS_CHANGED_BEACON_ENABLED)) {
2198 if (bss_conf->enable_beacon) {
2199 if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
2200 ret = wl1271_cmd_start_bss(wl);
2201 if (ret < 0)
2202 goto out;
2203
2204 set_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
2205 wl1271_debug(DEBUG_AP, "started AP");
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002206
2207 ret = wl1271_ap_init_hwenc(wl);
2208 if (ret < 0)
2209 goto out;
Arik Nemtsove78a2872010-10-16 19:07:21 +02002210 }
2211 } else {
2212 if (test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
2213 ret = wl1271_cmd_stop_bss(wl);
2214 if (ret < 0)
2215 goto out;
2216
2217 clear_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
2218 wl1271_debug(DEBUG_AP, "stopped AP");
2219 }
2220 }
2221 }
2222
2223 ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
2224 if (ret < 0)
2225 goto out;
2226out:
2227 return;
2228}
2229
2230/* STA/IBSS mode changes */
2231static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
2232 struct ieee80211_vif *vif,
2233 struct ieee80211_bss_conf *bss_conf,
2234 u32 changed)
2235{
2236 bool do_join = false, set_assoc = false;
2237 bool is_ibss = (wl->bss_type == BSS_TYPE_IBSS);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002238 u32 sta_rate_set = 0;
Arik Nemtsove78a2872010-10-16 19:07:21 +02002239 int ret;
Luciano Coelho2d6e4e762011-01-11 19:07:21 +01002240 struct ieee80211_sta *sta;
Arik Nemtsova1008852011-02-12 23:24:20 +02002241 bool sta_exists = false;
2242 struct ieee80211_sta_ht_cap sta_ht_cap;
Arik Nemtsove78a2872010-10-16 19:07:21 +02002243
2244 if (is_ibss) {
2245 ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf,
2246 changed);
2247 if (ret < 0)
2248 goto out;
2249 }
2250
2251 if ((changed & BSS_CHANGED_BEACON_INT) && is_ibss)
2252 do_join = true;
2253
2254 /* Need to update the SSID (for filtering etc) */
2255 if ((changed & BSS_CHANGED_BEACON) && is_ibss)
2256 do_join = true;
2257
2258 if ((changed & BSS_CHANGED_BEACON_ENABLED) && is_ibss) {
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02002259 wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
2260 bss_conf->enable_beacon ? "enabled" : "disabled");
2261
2262 if (bss_conf->enable_beacon)
2263 wl->set_bss_type = BSS_TYPE_IBSS;
2264 else
2265 wl->set_bss_type = BSS_TYPE_STA_BSS;
2266 do_join = true;
2267 }
2268
Arik Nemtsove78a2872010-10-16 19:07:21 +02002269 if ((changed & BSS_CHANGED_CQM)) {
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03002270 bool enable = false;
2271 if (bss_conf->cqm_rssi_thold)
2272 enable = true;
2273 ret = wl1271_acx_rssi_snr_trigger(wl, enable,
2274 bss_conf->cqm_rssi_thold,
2275 bss_conf->cqm_rssi_hyst);
2276 if (ret < 0)
2277 goto out;
2278 wl->rssi_thold = bss_conf->cqm_rssi_thold;
2279 }
2280
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002281 if ((changed & BSS_CHANGED_BSSID) &&
2282 /*
2283 * Now we know the correct bssid, so we send a new join command
2284 * and enable the BSSID filter
2285 */
2286 memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
Arik Nemtsove78a2872010-10-16 19:07:21 +02002287 memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
Juuso Oikarinena0cb7be2010-03-18 12:26:44 +02002288
Eliad Pellerfa287b82010-12-26 09:27:50 +01002289 if (!is_zero_ether_addr(wl->bssid)) {
2290 ret = wl1271_cmd_build_null_data(wl);
2291 if (ret < 0)
2292 goto out;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002293
Eliad Pellerfa287b82010-12-26 09:27:50 +01002294 ret = wl1271_build_qos_null_data(wl);
2295 if (ret < 0)
2296 goto out;
Saravanan Dhanabal141418c2010-04-28 09:50:00 +03002297
Eliad Pellerfa287b82010-12-26 09:27:50 +01002298 /* filter out all packets not from this BSSID */
2299 wl1271_configure_filters(wl, 0);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02002300
Eliad Pellerfa287b82010-12-26 09:27:50 +01002301 /* Need to update the BSSID (for filtering etc) */
2302 do_join = true;
2303 }
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002304 }
2305
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002306 rcu_read_lock();
2307 sta = ieee80211_find_sta(vif, bss_conf->bssid);
2308 if (sta) {
2309 /* save the supp_rates of the ap */
2310 sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band];
2311 if (sta->ht_cap.ht_supported)
2312 sta_rate_set |=
2313 (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET);
Arik Nemtsova1008852011-02-12 23:24:20 +02002314 sta_ht_cap = sta->ht_cap;
2315 sta_exists = true;
2316 }
2317 rcu_read_unlock();
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002318
Arik Nemtsova1008852011-02-12 23:24:20 +02002319 if (sta_exists) {
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002320 /* handle new association with HT and HT information change */
2321 if ((changed & BSS_CHANGED_HT) &&
2322 (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
Arik Nemtsova1008852011-02-12 23:24:20 +02002323 ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap,
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002324 true);
2325 if (ret < 0) {
2326 wl1271_warning("Set ht cap true failed %d",
2327 ret);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002328 goto out;
2329 }
2330 ret = wl1271_acx_set_ht_information(wl,
2331 bss_conf->ht_operation_mode);
2332 if (ret < 0) {
2333 wl1271_warning("Set ht information failed %d",
2334 ret);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002335 goto out;
2336 }
2337 }
2338 /* handle new association without HT and disassociation */
2339 else if (changed & BSS_CHANGED_ASSOC) {
Arik Nemtsova1008852011-02-12 23:24:20 +02002340 ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap,
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002341 false);
2342 if (ret < 0) {
2343 wl1271_warning("Set ht cap false failed %d",
2344 ret);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002345 goto out;
2346 }
2347 }
2348 }
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002349
Arik Nemtsove78a2872010-10-16 19:07:21 +02002350 if ((changed & BSS_CHANGED_ASSOC)) {
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002351 if (bss_conf->assoc) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002352 u32 rates;
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002353 int ieoffset;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002354 wl->aid = bss_conf->aid;
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002355 set_assoc = true;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002356
Juuso Oikarinen90494a92010-07-08 17:50:00 +03002357 wl->ps_poll_failures = 0;
2358
Luciano Coelhoae751ba2009-10-12 15:08:57 +03002359 /*
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002360 * use basic rates from AP, and determine lowest rate
2361 * to use with control frames.
2362 */
2363 rates = bss_conf->basic_rates;
2364 wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
2365 rates);
Arik Nemtsove0fe3712010-10-16 18:19:53 +02002366 wl->basic_rate = wl1271_tx_min_rate_get(wl);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002367 if (sta_rate_set)
2368 wl->rate_set = wl1271_tx_enabled_rates_get(wl,
2369 sta_rate_set);
Arik Nemtsov79b223f2010-10-16 17:52:59 +02002370 ret = wl1271_acx_sta_rate_policies(wl);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002371 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02002372 goto out;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002373
2374 /*
Luciano Coelhoae751ba2009-10-12 15:08:57 +03002375 * with wl1271, we don't need to update the
2376 * beacon_int and dtim_period, because the firmware
2377 * updates it by itself when the first beacon is
2378 * received after a join.
2379 */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002380 ret = wl1271_cmd_build_ps_poll(wl, wl->aid);
2381 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02002382 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002383
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02002384 /*
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002385 * Get a template for hardware connection maintenance
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02002386 */
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002387 dev_kfree_skb(wl->probereq);
2388 wl->probereq = wl1271_cmd_build_ap_probe_req(wl, NULL);
2389 ieoffset = offsetof(struct ieee80211_mgmt,
2390 u.probe_req.variable);
2391 wl1271_ssid_set(wl, wl->probereq, ieoffset);
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02002392
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02002393 /* enable the connection monitoring feature */
2394 ret = wl1271_acx_conn_monit_params(wl, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002395 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02002396 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002397
2398 /* If we want to go in PSM but we're not there yet */
Juuso Oikarinen71449f82009-12-11 15:41:07 +02002399 if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) &&
2400 !test_bit(WL1271_FLAG_PSM, &wl->flags)) {
Arik Nemtsove78a2872010-10-16 19:07:21 +02002401 enum wl1271_cmd_ps_mode mode;
2402
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002403 mode = STATION_POWER_SAVE_MODE;
Juuso Oikarinen65cddbf2010-08-24 06:28:03 +03002404 ret = wl1271_ps_set_mode(wl, mode,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02002405 wl->basic_rate,
Juuso Oikarinen65cddbf2010-08-24 06:28:03 +03002406 true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002407 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02002408 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002409 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03002410 } else {
2411 /* use defaults when not associated */
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03002412 clear_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags);
Juuso Oikarinen830fb672009-12-11 15:41:06 +02002413 clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
Juuso Oikarinend94cd292009-10-08 21:56:25 +03002414 wl->aid = 0;
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02002415
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002416 /* free probe-request template */
2417 dev_kfree_skb(wl->probereq);
2418 wl->probereq = NULL;
2419
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03002420 /* re-enable dynamic ps - just in case */
Juuso Oikarinenf532be62010-07-08 17:50:05 +03002421 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03002422
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002423 /* revert back to minimum rates for the current band */
2424 wl1271_set_band_rate(wl);
Arik Nemtsove0fe3712010-10-16 18:19:53 +02002425 wl->basic_rate = wl1271_tx_min_rate_get(wl);
Arik Nemtsov79b223f2010-10-16 17:52:59 +02002426 ret = wl1271_acx_sta_rate_policies(wl);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002427 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02002428 goto out;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002429
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02002430 /* disable connection monitor features */
2431 ret = wl1271_acx_conn_monit_params(wl, false);
Juuso Oikarinenc1899552010-03-26 12:53:32 +02002432
2433 /* Disable the keep-alive feature */
2434 ret = wl1271_acx_keep_alive_mode(wl, false);
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02002435 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02002436 goto out;
Juuso Oikarinenb84a7d32010-11-22 12:59:08 +02002437
2438 /* restore the bssid filter and go to dummy bssid */
2439 wl1271_unjoin(wl);
2440 wl1271_dummy_join(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002441 }
2442 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03002443
Arik Nemtsove78a2872010-10-16 19:07:21 +02002444 ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
2445 if (ret < 0)
2446 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002447
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03002448 if (changed & BSS_CHANGED_ARP_FILTER) {
2449 __be32 addr = bss_conf->arp_addr_list[0];
2450 WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
2451
Eliad Pellerc5312772010-12-09 11:31:27 +02002452 if (bss_conf->arp_addr_cnt == 1 &&
2453 bss_conf->arp_filter_enabled) {
2454 /*
2455 * The template should have been configured only upon
2456 * association. however, it seems that the correct ip
2457 * isn't being set (when sending), so we have to
2458 * reconfigure the template upon every ip change.
2459 */
2460 ret = wl1271_cmd_build_arp_rsp(wl, addr);
2461 if (ret < 0) {
2462 wl1271_warning("build arp rsp failed: %d", ret);
Arik Nemtsove78a2872010-10-16 19:07:21 +02002463 goto out;
Eliad Pellerc5312772010-12-09 11:31:27 +02002464 }
2465
2466 ret = wl1271_acx_arp_ip_filter(wl,
Eliad Pellere5e2f242011-01-24 19:19:03 +01002467 ACX_ARP_FILTER_ARP_FILTERING,
Eliad Pellerc5312772010-12-09 11:31:27 +02002468 addr);
2469 } else
2470 ret = wl1271_acx_arp_ip_filter(wl, 0, addr);
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03002471
2472 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02002473 goto out;
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03002474 }
2475
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02002476 if (do_join) {
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002477 ret = wl1271_join(wl, set_assoc);
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02002478 if (ret < 0) {
2479 wl1271_warning("cmd join failed %d", ret);
Arik Nemtsove78a2872010-10-16 19:07:21 +02002480 goto out;
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02002481 }
Juuso Oikarinenc1899552010-03-26 12:53:32 +02002482 }
2483
Arik Nemtsove78a2872010-10-16 19:07:21 +02002484out:
2485 return;
2486}
2487
2488static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
2489 struct ieee80211_vif *vif,
2490 struct ieee80211_bss_conf *bss_conf,
2491 u32 changed)
2492{
2493 struct wl1271 *wl = hw->priv;
2494 bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
2495 int ret;
2496
2497 wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
2498 (int)changed);
2499
2500 mutex_lock(&wl->mutex);
2501
2502 if (unlikely(wl->state == WL1271_STATE_OFF))
2503 goto out;
2504
2505 ret = wl1271_ps_elp_wakeup(wl, false);
2506 if (ret < 0)
2507 goto out;
2508
2509 if (is_ap)
2510 wl1271_bss_info_changed_ap(wl, vif, bss_conf, changed);
2511 else
2512 wl1271_bss_info_changed_sta(wl, vif, bss_conf, changed);
2513
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002514 wl1271_ps_elp_sleep(wl);
2515
2516out:
2517 mutex_unlock(&wl->mutex);
2518}
2519
Kalle Valoc6999d82010-02-18 13:25:41 +02002520static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
2521 const struct ieee80211_tx_queue_params *params)
2522{
2523 struct wl1271 *wl = hw->priv;
Kalle Valo4695dc92010-03-18 12:26:38 +02002524 u8 ps_scheme;
Arik Nemtsov488fc542010-10-16 20:33:45 +02002525 int ret = 0;
Kalle Valoc6999d82010-02-18 13:25:41 +02002526
2527 mutex_lock(&wl->mutex);
2528
2529 wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
2530
Kalle Valo4695dc92010-03-18 12:26:38 +02002531 if (params->uapsd)
2532 ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
2533 else
2534 ps_scheme = CONF_PS_SCHEME_LEGACY;
2535
Arik Nemtsov488fc542010-10-16 20:33:45 +02002536 if (wl->state == WL1271_STATE_OFF) {
2537 /*
2538 * If the state is off, the parameters will be recorded and
2539 * configured on init. This happens in AP-mode.
2540 */
2541 struct conf_tx_ac_category *conf_ac =
2542 &wl->conf.tx.ac_conf[wl1271_tx_get_queue(queue)];
2543 struct conf_tx_tid *conf_tid =
2544 &wl->conf.tx.tid_conf[wl1271_tx_get_queue(queue)];
2545
2546 conf_ac->ac = wl1271_tx_get_queue(queue);
2547 conf_ac->cw_min = (u8)params->cw_min;
2548 conf_ac->cw_max = params->cw_max;
2549 conf_ac->aifsn = params->aifs;
2550 conf_ac->tx_op_limit = params->txop << 5;
2551
2552 conf_tid->queue_id = wl1271_tx_get_queue(queue);
2553 conf_tid->channel_type = CONF_CHANNEL_TYPE_EDCF;
2554 conf_tid->tsid = wl1271_tx_get_queue(queue);
2555 conf_tid->ps_scheme = ps_scheme;
2556 conf_tid->ack_policy = CONF_ACK_POLICY_LEGACY;
2557 conf_tid->apsd_conf[0] = 0;
2558 conf_tid->apsd_conf[1] = 0;
2559 } else {
2560 ret = wl1271_ps_elp_wakeup(wl, false);
2561 if (ret < 0)
2562 goto out;
2563
2564 /*
2565 * the txop is confed in units of 32us by the mac80211,
2566 * we need us
2567 */
2568 ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
2569 params->cw_min, params->cw_max,
2570 params->aifs, params->txop << 5);
2571 if (ret < 0)
2572 goto out_sleep;
2573
2574 ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
2575 CONF_CHANNEL_TYPE_EDCF,
2576 wl1271_tx_get_queue(queue),
2577 ps_scheme, CONF_ACK_POLICY_LEGACY,
2578 0, 0);
2579 if (ret < 0)
2580 goto out_sleep;
Kalle Valoc82c1dd2010-02-18 13:25:47 +02002581
2582out_sleep:
Arik Nemtsov488fc542010-10-16 20:33:45 +02002583 wl1271_ps_elp_sleep(wl);
2584 }
Kalle Valoc6999d82010-02-18 13:25:41 +02002585
2586out:
2587 mutex_unlock(&wl->mutex);
2588
2589 return ret;
2590}
2591
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03002592static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw)
2593{
2594
2595 struct wl1271 *wl = hw->priv;
2596 u64 mactime = ULLONG_MAX;
2597 int ret;
2598
2599 wl1271_debug(DEBUG_MAC80211, "mac80211 get tsf");
2600
2601 mutex_lock(&wl->mutex);
2602
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002603 if (unlikely(wl->state == WL1271_STATE_OFF))
2604 goto out;
2605
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03002606 ret = wl1271_ps_elp_wakeup(wl, false);
2607 if (ret < 0)
2608 goto out;
2609
2610 ret = wl1271_acx_tsf_info(wl, &mactime);
2611 if (ret < 0)
2612 goto out_sleep;
2613
2614out_sleep:
2615 wl1271_ps_elp_sleep(wl);
2616
2617out:
2618 mutex_unlock(&wl->mutex);
2619 return mactime;
2620}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002621
John W. Linvilleece550d2010-07-28 16:41:06 -04002622static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
2623 struct survey_info *survey)
2624{
2625 struct wl1271 *wl = hw->priv;
2626 struct ieee80211_conf *conf = &hw->conf;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02002627
John W. Linvilleece550d2010-07-28 16:41:06 -04002628 if (idx != 0)
2629 return -ENOENT;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02002630
John W. Linvilleece550d2010-07-28 16:41:06 -04002631 survey->channel = conf->channel;
2632 survey->filled = SURVEY_INFO_NOISE_DBM;
2633 survey->noise = wl->noise;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02002634
John W. Linvilleece550d2010-07-28 16:41:06 -04002635 return 0;
2636}
2637
Arik Nemtsov409622e2011-02-23 00:22:29 +02002638static int wl1271_allocate_sta(struct wl1271 *wl,
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02002639 struct ieee80211_sta *sta,
2640 u8 *hlid)
2641{
2642 struct wl1271_station *wl_sta;
2643 int id;
2644
2645 id = find_first_zero_bit(wl->ap_hlid_map, AP_MAX_STATIONS);
2646 if (id >= AP_MAX_STATIONS) {
2647 wl1271_warning("could not allocate HLID - too much stations");
2648 return -EBUSY;
2649 }
2650
2651 wl_sta = (struct wl1271_station *)sta->drv_priv;
2652
2653 __set_bit(id, wl->ap_hlid_map);
2654 wl_sta->hlid = WL1271_AP_STA_HLID_START + id;
2655 *hlid = wl_sta->hlid;
2656 return 0;
2657}
2658
Arik Nemtsov409622e2011-02-23 00:22:29 +02002659static void wl1271_free_sta(struct wl1271 *wl, u8 hlid)
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02002660{
2661 int id = hlid - WL1271_AP_STA_HLID_START;
2662
Arik Nemtsov409622e2011-02-23 00:22:29 +02002663 if (WARN_ON(!test_bit(id, wl->ap_hlid_map)))
2664 return;
2665
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02002666 __clear_bit(id, wl->ap_hlid_map);
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02002667 wl1271_tx_reset_link_queues(wl, hlid);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02002668}
2669
2670static int wl1271_op_sta_add(struct ieee80211_hw *hw,
2671 struct ieee80211_vif *vif,
2672 struct ieee80211_sta *sta)
2673{
2674 struct wl1271 *wl = hw->priv;
2675 int ret = 0;
2676 u8 hlid;
2677
2678 mutex_lock(&wl->mutex);
2679
2680 if (unlikely(wl->state == WL1271_STATE_OFF))
2681 goto out;
2682
2683 if (wl->bss_type != BSS_TYPE_AP_BSS)
2684 goto out;
2685
2686 wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);
2687
Arik Nemtsov409622e2011-02-23 00:22:29 +02002688 ret = wl1271_allocate_sta(wl, sta, &hlid);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02002689 if (ret < 0)
2690 goto out;
2691
2692 ret = wl1271_ps_elp_wakeup(wl, false);
2693 if (ret < 0)
Arik Nemtsov409622e2011-02-23 00:22:29 +02002694 goto out_free_sta;
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02002695
2696 ret = wl1271_cmd_add_sta(wl, sta, hlid);
2697 if (ret < 0)
2698 goto out_sleep;
2699
2700out_sleep:
2701 wl1271_ps_elp_sleep(wl);
2702
Arik Nemtsov409622e2011-02-23 00:22:29 +02002703out_free_sta:
2704 if (ret < 0)
2705 wl1271_free_sta(wl, hlid);
2706
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02002707out:
2708 mutex_unlock(&wl->mutex);
2709 return ret;
2710}
2711
2712static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
2713 struct ieee80211_vif *vif,
2714 struct ieee80211_sta *sta)
2715{
2716 struct wl1271 *wl = hw->priv;
2717 struct wl1271_station *wl_sta;
2718 int ret = 0, id;
2719
2720 mutex_lock(&wl->mutex);
2721
2722 if (unlikely(wl->state == WL1271_STATE_OFF))
2723 goto out;
2724
2725 if (wl->bss_type != BSS_TYPE_AP_BSS)
2726 goto out;
2727
2728 wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
2729
2730 wl_sta = (struct wl1271_station *)sta->drv_priv;
2731 id = wl_sta->hlid - WL1271_AP_STA_HLID_START;
2732 if (WARN_ON(!test_bit(id, wl->ap_hlid_map)))
2733 goto out;
2734
2735 ret = wl1271_ps_elp_wakeup(wl, false);
2736 if (ret < 0)
2737 goto out;
2738
2739 ret = wl1271_cmd_remove_sta(wl, wl_sta->hlid);
2740 if (ret < 0)
2741 goto out_sleep;
2742
Arik Nemtsov409622e2011-02-23 00:22:29 +02002743 wl1271_free_sta(wl, wl_sta->hlid);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02002744
2745out_sleep:
2746 wl1271_ps_elp_sleep(wl);
2747
2748out:
2749 mutex_unlock(&wl->mutex);
2750 return ret;
2751}
2752
Levi, Shaharbbba3e62011-01-23 07:27:23 +01002753int wl1271_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
Luciano Coelho7c3ee9e2011-01-31 09:41:52 +02002754 enum ieee80211_ampdu_mlme_action action,
2755 struct ieee80211_sta *sta, u16 tid, u16 *ssn,
2756 u8 buf_size)
Levi, Shaharbbba3e62011-01-23 07:27:23 +01002757{
2758 struct wl1271 *wl = hw->priv;
2759 int ret;
2760
2761 mutex_lock(&wl->mutex);
2762
2763 if (unlikely(wl->state == WL1271_STATE_OFF)) {
2764 ret = -EAGAIN;
2765 goto out;
2766 }
2767
2768 ret = wl1271_ps_elp_wakeup(wl, false);
2769 if (ret < 0)
2770 goto out;
2771
2772 switch (action) {
2773 case IEEE80211_AMPDU_RX_START:
2774 if (wl->ba_support) {
2775 ret = wl1271_acx_set_ba_receiver_session(wl, tid, *ssn,
2776 true);
2777 if (!ret)
2778 wl->ba_rx_bitmap |= BIT(tid);
2779 } else {
2780 ret = -ENOTSUPP;
2781 }
2782 break;
2783
2784 case IEEE80211_AMPDU_RX_STOP:
2785 ret = wl1271_acx_set_ba_receiver_session(wl, tid, 0, false);
2786 if (!ret)
2787 wl->ba_rx_bitmap &= ~BIT(tid);
2788 break;
2789
2790 /*
2791 * The BA initiator session management in FW independently.
2792 * Falling break here on purpose for all TX APDU commands.
2793 */
2794 case IEEE80211_AMPDU_TX_START:
2795 case IEEE80211_AMPDU_TX_STOP:
2796 case IEEE80211_AMPDU_TX_OPERATIONAL:
2797 ret = -EINVAL;
2798 break;
2799
2800 default:
2801 wl1271_error("Incorrect ampdu action id=%x\n", action);
2802 ret = -EINVAL;
2803 }
2804
2805 wl1271_ps_elp_sleep(wl);
2806
2807out:
2808 mutex_unlock(&wl->mutex);
2809
2810 return ret;
2811}
2812
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002813/* can't be const, mac80211 writes to this */
2814static struct ieee80211_rate wl1271_rates[] = {
2815 { .bitrate = 10,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002816 .hw_value = CONF_HW_BIT_RATE_1MBPS,
2817 .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002818 { .bitrate = 20,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002819 .hw_value = CONF_HW_BIT_RATE_2MBPS,
2820 .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002821 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2822 { .bitrate = 55,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002823 .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
2824 .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002825 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2826 { .bitrate = 110,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002827 .hw_value = CONF_HW_BIT_RATE_11MBPS,
2828 .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002829 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2830 { .bitrate = 60,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002831 .hw_value = CONF_HW_BIT_RATE_6MBPS,
2832 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002833 { .bitrate = 90,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002834 .hw_value = CONF_HW_BIT_RATE_9MBPS,
2835 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002836 { .bitrate = 120,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002837 .hw_value = CONF_HW_BIT_RATE_12MBPS,
2838 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002839 { .bitrate = 180,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002840 .hw_value = CONF_HW_BIT_RATE_18MBPS,
2841 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002842 { .bitrate = 240,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002843 .hw_value = CONF_HW_BIT_RATE_24MBPS,
2844 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002845 { .bitrate = 360,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002846 .hw_value = CONF_HW_BIT_RATE_36MBPS,
2847 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002848 { .bitrate = 480,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002849 .hw_value = CONF_HW_BIT_RATE_48MBPS,
2850 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002851 { .bitrate = 540,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002852 .hw_value = CONF_HW_BIT_RATE_54MBPS,
2853 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002854};
2855
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002856/* can't be const, mac80211 writes to this */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002857static struct ieee80211_channel wl1271_channels[] = {
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02002858 { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002859 { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002860 { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
2861 { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
2862 { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002863 { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002864 { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
2865 { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
2866 { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002867 { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002868 { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
2869 { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
2870 { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
Arik Nemtsov6c89b7b2011-01-18 20:39:52 +01002871 { .hw_value = 14, .center_freq = 2484, .max_power = 25 },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002872};
2873
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002874/* mapping to indexes for wl1271_rates */
Tobias Klausera0ea9492010-05-20 10:38:11 +02002875static const u8 wl1271_rate_to_idx_2ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002876 /* MCS rates are used only with 11n */
Shahar Levi18357852010-10-13 16:09:41 +02002877 7, /* CONF_HW_RXTX_RATE_MCS7 */
2878 6, /* CONF_HW_RXTX_RATE_MCS6 */
2879 5, /* CONF_HW_RXTX_RATE_MCS5 */
2880 4, /* CONF_HW_RXTX_RATE_MCS4 */
2881 3, /* CONF_HW_RXTX_RATE_MCS3 */
2882 2, /* CONF_HW_RXTX_RATE_MCS2 */
2883 1, /* CONF_HW_RXTX_RATE_MCS1 */
2884 0, /* CONF_HW_RXTX_RATE_MCS0 */
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002885
2886 11, /* CONF_HW_RXTX_RATE_54 */
2887 10, /* CONF_HW_RXTX_RATE_48 */
2888 9, /* CONF_HW_RXTX_RATE_36 */
2889 8, /* CONF_HW_RXTX_RATE_24 */
2890
2891 /* TI-specific rate */
2892 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
2893
2894 7, /* CONF_HW_RXTX_RATE_18 */
2895 6, /* CONF_HW_RXTX_RATE_12 */
2896 3, /* CONF_HW_RXTX_RATE_11 */
2897 5, /* CONF_HW_RXTX_RATE_9 */
2898 4, /* CONF_HW_RXTX_RATE_6 */
2899 2, /* CONF_HW_RXTX_RATE_5_5 */
2900 1, /* CONF_HW_RXTX_RATE_2 */
2901 0 /* CONF_HW_RXTX_RATE_1 */
2902};
2903
Shahar Levie8b03a22010-10-13 16:09:39 +02002904/* 11n STA capabilities */
2905#define HW_RX_HIGHEST_RATE 72
2906
Shahar Levi00d20102010-11-08 11:20:10 +00002907#ifdef CONFIG_WL12XX_HT
2908#define WL12XX_HT_CAP { \
Shahar Levie8b03a22010-10-13 16:09:39 +02002909 .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20, \
2910 .ht_supported = true, \
2911 .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, \
2912 .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \
2913 .mcs = { \
2914 .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \
2915 .rx_highest = cpu_to_le16(HW_RX_HIGHEST_RATE), \
2916 .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \
2917 }, \
2918}
Shahar Levi18357852010-10-13 16:09:41 +02002919#else
Shahar Levi00d20102010-11-08 11:20:10 +00002920#define WL12XX_HT_CAP { \
Shahar Levi18357852010-10-13 16:09:41 +02002921 .ht_supported = false, \
2922}
2923#endif
Shahar Levie8b03a22010-10-13 16:09:39 +02002924
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002925/* can't be const, mac80211 writes to this */
2926static struct ieee80211_supported_band wl1271_band_2ghz = {
2927 .channels = wl1271_channels,
2928 .n_channels = ARRAY_SIZE(wl1271_channels),
2929 .bitrates = wl1271_rates,
2930 .n_bitrates = ARRAY_SIZE(wl1271_rates),
Shahar Levi00d20102010-11-08 11:20:10 +00002931 .ht_cap = WL12XX_HT_CAP,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002932};
2933
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002934/* 5 GHz data rates for WL1273 */
2935static struct ieee80211_rate wl1271_rates_5ghz[] = {
2936 { .bitrate = 60,
2937 .hw_value = CONF_HW_BIT_RATE_6MBPS,
2938 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
2939 { .bitrate = 90,
2940 .hw_value = CONF_HW_BIT_RATE_9MBPS,
2941 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
2942 { .bitrate = 120,
2943 .hw_value = CONF_HW_BIT_RATE_12MBPS,
2944 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
2945 { .bitrate = 180,
2946 .hw_value = CONF_HW_BIT_RATE_18MBPS,
2947 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
2948 { .bitrate = 240,
2949 .hw_value = CONF_HW_BIT_RATE_24MBPS,
2950 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
2951 { .bitrate = 360,
2952 .hw_value = CONF_HW_BIT_RATE_36MBPS,
2953 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
2954 { .bitrate = 480,
2955 .hw_value = CONF_HW_BIT_RATE_48MBPS,
2956 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
2957 { .bitrate = 540,
2958 .hw_value = CONF_HW_BIT_RATE_54MBPS,
2959 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
2960};
2961
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002962/* 5 GHz band channels for WL1273 */
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002963static struct ieee80211_channel wl1271_channels_5ghz[] = {
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002964 { .hw_value = 7, .center_freq = 5035},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002965 { .hw_value = 8, .center_freq = 5040},
2966 { .hw_value = 9, .center_freq = 5045},
2967 { .hw_value = 11, .center_freq = 5055},
2968 { .hw_value = 12, .center_freq = 5060},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002969 { .hw_value = 16, .center_freq = 5080},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002970 { .hw_value = 34, .center_freq = 5170},
2971 { .hw_value = 36, .center_freq = 5180},
2972 { .hw_value = 38, .center_freq = 5190},
2973 { .hw_value = 40, .center_freq = 5200},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002974 { .hw_value = 42, .center_freq = 5210},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002975 { .hw_value = 44, .center_freq = 5220},
2976 { .hw_value = 46, .center_freq = 5230},
2977 { .hw_value = 48, .center_freq = 5240},
2978 { .hw_value = 52, .center_freq = 5260},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002979 { .hw_value = 56, .center_freq = 5280},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002980 { .hw_value = 60, .center_freq = 5300},
2981 { .hw_value = 64, .center_freq = 5320},
2982 { .hw_value = 100, .center_freq = 5500},
2983 { .hw_value = 104, .center_freq = 5520},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002984 { .hw_value = 108, .center_freq = 5540},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002985 { .hw_value = 112, .center_freq = 5560},
2986 { .hw_value = 116, .center_freq = 5580},
2987 { .hw_value = 120, .center_freq = 5600},
2988 { .hw_value = 124, .center_freq = 5620},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002989 { .hw_value = 128, .center_freq = 5640},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002990 { .hw_value = 132, .center_freq = 5660},
2991 { .hw_value = 136, .center_freq = 5680},
2992 { .hw_value = 140, .center_freq = 5700},
2993 { .hw_value = 149, .center_freq = 5745},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002994 { .hw_value = 153, .center_freq = 5765},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002995 { .hw_value = 157, .center_freq = 5785},
2996 { .hw_value = 161, .center_freq = 5805},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002997 { .hw_value = 165, .center_freq = 5825},
2998};
2999
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003000/* mapping to indexes for wl1271_rates_5ghz */
Tobias Klausera0ea9492010-05-20 10:38:11 +02003001static const u8 wl1271_rate_to_idx_5ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003002 /* MCS rates are used only with 11n */
Shahar Levi18357852010-10-13 16:09:41 +02003003 7, /* CONF_HW_RXTX_RATE_MCS7 */
3004 6, /* CONF_HW_RXTX_RATE_MCS6 */
3005 5, /* CONF_HW_RXTX_RATE_MCS5 */
3006 4, /* CONF_HW_RXTX_RATE_MCS4 */
3007 3, /* CONF_HW_RXTX_RATE_MCS3 */
3008 2, /* CONF_HW_RXTX_RATE_MCS2 */
3009 1, /* CONF_HW_RXTX_RATE_MCS1 */
3010 0, /* CONF_HW_RXTX_RATE_MCS0 */
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003011
3012 7, /* CONF_HW_RXTX_RATE_54 */
3013 6, /* CONF_HW_RXTX_RATE_48 */
3014 5, /* CONF_HW_RXTX_RATE_36 */
3015 4, /* CONF_HW_RXTX_RATE_24 */
3016
3017 /* TI-specific rate */
3018 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
3019
3020 3, /* CONF_HW_RXTX_RATE_18 */
3021 2, /* CONF_HW_RXTX_RATE_12 */
3022 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_11 */
3023 1, /* CONF_HW_RXTX_RATE_9 */
3024 0, /* CONF_HW_RXTX_RATE_6 */
3025 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_5_5 */
3026 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_2 */
3027 CONF_HW_RXTX_RATE_UNSUPPORTED /* CONF_HW_RXTX_RATE_1 */
3028};
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03003029
3030static struct ieee80211_supported_band wl1271_band_5ghz = {
3031 .channels = wl1271_channels_5ghz,
3032 .n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
3033 .bitrates = wl1271_rates_5ghz,
3034 .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
Shahar Levi00d20102010-11-08 11:20:10 +00003035 .ht_cap = WL12XX_HT_CAP,
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03003036};
3037
Tobias Klausera0ea9492010-05-20 10:38:11 +02003038static const u8 *wl1271_band_rate_to_idx[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003039 [IEEE80211_BAND_2GHZ] = wl1271_rate_to_idx_2ghz,
3040 [IEEE80211_BAND_5GHZ] = wl1271_rate_to_idx_5ghz
3041};
3042
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003043static const struct ieee80211_ops wl1271_ops = {
3044 .start = wl1271_op_start,
3045 .stop = wl1271_op_stop,
3046 .add_interface = wl1271_op_add_interface,
3047 .remove_interface = wl1271_op_remove_interface,
3048 .config = wl1271_op_config,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03003049 .prepare_multicast = wl1271_op_prepare_multicast,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003050 .configure_filter = wl1271_op_configure_filter,
3051 .tx = wl1271_op_tx,
3052 .set_key = wl1271_op_set_key,
3053 .hw_scan = wl1271_op_hw_scan,
3054 .bss_info_changed = wl1271_op_bss_info_changed,
Arik Nemtsov68d069c2010-11-08 10:51:07 +01003055 .set_frag_threshold = wl1271_op_set_frag_threshold,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003056 .set_rts_threshold = wl1271_op_set_rts_threshold,
Kalle Valoc6999d82010-02-18 13:25:41 +02003057 .conf_tx = wl1271_op_conf_tx,
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03003058 .get_tsf = wl1271_op_get_tsf,
John W. Linvilleece550d2010-07-28 16:41:06 -04003059 .get_survey = wl1271_op_get_survey,
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003060 .sta_add = wl1271_op_sta_add,
3061 .sta_remove = wl1271_op_sta_remove,
Levi, Shaharbbba3e62011-01-23 07:27:23 +01003062 .ampdu_action = wl1271_op_ampdu_action,
Kalle Valoc8c90872010-02-18 13:25:53 +02003063 CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003064};
3065
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003066
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02003067u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band)
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003068{
3069 u8 idx;
3070
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02003071 BUG_ON(band >= sizeof(wl1271_band_rate_to_idx)/sizeof(u8 *));
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003072
3073 if (unlikely(rate >= CONF_HW_RXTX_RATE_MAX)) {
3074 wl1271_error("Illegal RX rate from HW: %d", rate);
3075 return 0;
3076 }
3077
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02003078 idx = wl1271_band_rate_to_idx[band][rate];
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003079 if (unlikely(idx == CONF_HW_RXTX_RATE_UNSUPPORTED)) {
3080 wl1271_error("Unsupported RX rate from HW: %d", rate);
3081 return 0;
3082 }
3083
3084 return idx;
3085}
3086
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003087static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
3088 struct device_attribute *attr,
3089 char *buf)
3090{
3091 struct wl1271 *wl = dev_get_drvdata(dev);
3092 ssize_t len;
3093
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02003094 len = PAGE_SIZE;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003095
3096 mutex_lock(&wl->mutex);
3097 len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
3098 wl->sg_enabled);
3099 mutex_unlock(&wl->mutex);
3100
3101 return len;
3102
3103}
3104
3105static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
3106 struct device_attribute *attr,
3107 const char *buf, size_t count)
3108{
3109 struct wl1271 *wl = dev_get_drvdata(dev);
3110 unsigned long res;
3111 int ret;
3112
3113 ret = strict_strtoul(buf, 10, &res);
3114
3115 if (ret < 0) {
3116 wl1271_warning("incorrect value written to bt_coex_mode");
3117 return count;
3118 }
3119
3120 mutex_lock(&wl->mutex);
3121
3122 res = !!res;
3123
3124 if (res == wl->sg_enabled)
3125 goto out;
3126
3127 wl->sg_enabled = res;
3128
3129 if (wl->state == WL1271_STATE_OFF)
3130 goto out;
3131
3132 ret = wl1271_ps_elp_wakeup(wl, false);
3133 if (ret < 0)
3134 goto out;
3135
3136 wl1271_acx_sg_enable(wl, wl->sg_enabled);
3137 wl1271_ps_elp_sleep(wl);
3138
3139 out:
3140 mutex_unlock(&wl->mutex);
3141 return count;
3142}
3143
3144static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
3145 wl1271_sysfs_show_bt_coex_state,
3146 wl1271_sysfs_store_bt_coex_state);
3147
Juuso Oikarinend717fd62010-05-07 11:38:58 +03003148static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
3149 struct device_attribute *attr,
3150 char *buf)
3151{
3152 struct wl1271 *wl = dev_get_drvdata(dev);
3153 ssize_t len;
3154
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02003155 len = PAGE_SIZE;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03003156
3157 mutex_lock(&wl->mutex);
3158 if (wl->hw_pg_ver >= 0)
3159 len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
3160 else
3161 len = snprintf(buf, len, "n/a\n");
3162 mutex_unlock(&wl->mutex);
3163
3164 return len;
3165}
3166
3167static DEVICE_ATTR(hw_pg_ver, S_IRUGO | S_IWUSR,
3168 wl1271_sysfs_show_hw_pg_ver, NULL);
3169
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02003170int wl1271_register_hw(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003171{
3172 int ret;
3173
3174 if (wl->mac80211_registered)
3175 return 0;
3176
Arik Nemtsov31d26ec2010-10-16 21:49:52 +02003177 ret = wl1271_fetch_nvs(wl);
3178 if (ret == 0) {
3179 u8 *nvs_ptr = (u8 *)wl->nvs->nvs;
3180
3181 wl->mac_addr[0] = nvs_ptr[11];
3182 wl->mac_addr[1] = nvs_ptr[10];
3183 wl->mac_addr[2] = nvs_ptr[6];
3184 wl->mac_addr[3] = nvs_ptr[5];
3185 wl->mac_addr[4] = nvs_ptr[4];
3186 wl->mac_addr[5] = nvs_ptr[3];
3187 }
3188
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003189 SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
3190
3191 ret = ieee80211_register_hw(wl->hw);
3192 if (ret < 0) {
3193 wl1271_error("unable to register mac80211 hw: %d", ret);
3194 return ret;
3195 }
3196
3197 wl->mac80211_registered = true;
3198
Eliad Pellerd60080a2010-11-24 12:53:16 +02003199 wl1271_debugfs_init(wl);
3200
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03003201 register_netdevice_notifier(&wl1271_dev_notifier);
3202
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003203 wl1271_notice("loaded");
3204
3205 return 0;
3206}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02003207EXPORT_SYMBOL_GPL(wl1271_register_hw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003208
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003209void wl1271_unregister_hw(struct wl1271 *wl)
3210{
Juuso Oikarinen4ae3fa82011-01-14 12:48:46 +01003211 if (wl->state == WL1271_STATE_PLT)
3212 __wl1271_plt_stop(wl);
3213
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03003214 unregister_netdevice_notifier(&wl1271_dev_notifier);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003215 ieee80211_unregister_hw(wl->hw);
3216 wl->mac80211_registered = false;
3217
3218}
3219EXPORT_SYMBOL_GPL(wl1271_unregister_hw);
3220
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02003221int wl1271_init_ieee80211(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003222{
Juuso Oikarinen7a557242010-09-27 12:42:07 +02003223 static const u32 cipher_suites[] = {
3224 WLAN_CIPHER_SUITE_WEP40,
3225 WLAN_CIPHER_SUITE_WEP104,
3226 WLAN_CIPHER_SUITE_TKIP,
3227 WLAN_CIPHER_SUITE_CCMP,
3228 WL1271_CIPHER_SUITE_GEM,
3229 };
3230
Juuso Oikarinen1e2b7972009-10-08 21:56:20 +03003231 /* The tx descriptor buffer and the TKIP space. */
3232 wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
3233 sizeof(struct wl1271_tx_hw_descr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003234
3235 /* unit us */
3236 /* FIXME: find a proper value */
3237 wl->hw->channel_change_time = 10000;
Juuso Oikarinen50c500a2010-04-01 11:38:22 +03003238 wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003239
3240 wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
Juuso Oikarinen03442a32009-11-23 23:22:14 +02003241 IEEE80211_HW_BEACON_FILTER |
Juuso Oikarinen0a343322010-02-22 08:38:41 +02003242 IEEE80211_HW_SUPPORTS_PS |
Kalle Valo4695dc92010-03-18 12:26:38 +02003243 IEEE80211_HW_SUPPORTS_UAPSD |
Juuso Oikarinena9af0922010-03-26 12:53:30 +02003244 IEEE80211_HW_HAS_RATE_CONTROL |
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03003245 IEEE80211_HW_CONNECTION_MONITOR |
Eliad Peller62c07402011-02-02 11:20:05 +02003246 IEEE80211_HW_SUPPORTS_CQM_RSSI |
Arik Nemtsovba7c0822011-02-23 00:22:28 +02003247 IEEE80211_HW_REPORTS_TX_ACK_STATUS |
3248 IEEE80211_HW_AP_LINK_PS;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003249
Juuso Oikarinen7a557242010-09-27 12:42:07 +02003250 wl->hw->wiphy->cipher_suites = cipher_suites;
3251 wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
3252
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02003253 wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
Arik Nemtsov038d9252010-10-16 21:53:24 +02003254 BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003255 wl->hw->wiphy->max_scan_ssids = 1;
Guy Eilamea559b42010-12-09 16:54:59 +02003256 /*
3257 * Maximum length of elements in scanning probe request templates
3258 * should be the maximum length possible for a template, without
3259 * the IEEE80211 header of the template
3260 */
3261 wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
3262 sizeof(struct ieee80211_header);
Luciano Coelhoa8aaaf52011-01-11 18:25:18 +01003263
3264 /*
3265 * We keep local copies of the band structs because we need to
3266 * modify them on a per-device basis.
3267 */
3268 memcpy(&wl->bands[IEEE80211_BAND_2GHZ], &wl1271_band_2ghz,
3269 sizeof(wl1271_band_2ghz));
3270 memcpy(&wl->bands[IEEE80211_BAND_5GHZ], &wl1271_band_5ghz,
3271 sizeof(wl1271_band_5ghz));
3272
3273 wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
3274 &wl->bands[IEEE80211_BAND_2GHZ];
3275 wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
3276 &wl->bands[IEEE80211_BAND_5GHZ];
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03003277
Kalle Valo12bd8942010-03-18 12:26:33 +02003278 wl->hw->queues = 4;
Juuso Oikarinen31627dc2010-03-26 12:53:12 +02003279 wl->hw->max_rates = 1;
Kalle Valo12bd8942010-03-18 12:26:33 +02003280
Juuso Oikarinenb7417d92010-11-10 11:27:19 +01003281 wl->hw->wiphy->reg_notifier = wl1271_reg_notify;
3282
Teemu Paasikivi8197b712010-02-22 08:38:23 +02003283 SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003284
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003285 wl->hw->sta_data_size = sizeof(struct wl1271_station);
3286
Luciano Coelho4c9cfa72011-01-12 14:27:03 +01003287 wl->hw->max_rx_aggregation_subframes = 8;
3288
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003289 return 0;
3290}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02003291EXPORT_SYMBOL_GPL(wl1271_init_ieee80211);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003292
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003293#define WL1271_DEFAULT_CHANNEL 0
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02003294
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02003295struct ieee80211_hw *wl1271_alloc_hw(void)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003296{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003297 struct ieee80211_hw *hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003298 struct platform_device *plat_dev = NULL;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003299 struct wl1271 *wl;
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02003300 int i, j, ret;
Ido Yariv1f37cbc2010-09-30 13:28:27 +02003301 unsigned int order;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003302
3303 hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
3304 if (!hw) {
3305 wl1271_error("could not alloc ieee80211_hw");
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003306 ret = -ENOMEM;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003307 goto err_hw_alloc;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003308 }
3309
Julia Lawall929ebd32010-05-15 23:16:39 +02003310 plat_dev = kmemdup(&wl1271_device, sizeof(wl1271_device), GFP_KERNEL);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003311 if (!plat_dev) {
3312 wl1271_error("could not allocate platform_device");
3313 ret = -ENOMEM;
3314 goto err_plat_alloc;
3315 }
3316
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003317 wl = hw->priv;
3318 memset(wl, 0, sizeof(*wl));
3319
Juuso Oikarinen01c09162009-10-13 12:47:55 +03003320 INIT_LIST_HEAD(&wl->list);
3321
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003322 wl->hw = hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003323 wl->plat_dev = plat_dev;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003324
Juuso Oikarinen6742f552010-12-13 09:52:37 +02003325 for (i = 0; i < NUM_TX_QUEUES; i++)
3326 skb_queue_head_init(&wl->tx_queue[i]);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003327
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02003328 for (i = 0; i < NUM_TX_QUEUES; i++)
3329 for (j = 0; j < AP_MAX_LINKS; j++)
3330 skb_queue_head_init(&wl->links[j].tx_queue[i]);
3331
Juuso Oikarinen37b70a82009-10-08 21:56:21 +03003332 INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03003333 INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work);
Juuso Oikarinen117b38d2010-09-30 10:43:28 +02003334 INIT_WORK(&wl->irq_work, wl1271_irq_work);
3335 INIT_WORK(&wl->tx_work, wl1271_tx_work);
3336 INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
3337 INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003338 wl->channel = WL1271_DEFAULT_CHANNEL;
Juuso Oikarinen60e84c22010-03-26 12:53:25 +02003339 wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003340 wl->default_key = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003341 wl->rx_counter = 0;
Arik Nemtsovae113b52010-10-16 18:45:07 +02003342 wl->rx_config = WL1271_DEFAULT_STA_RX_CONFIG;
3343 wl->rx_filter = WL1271_DEFAULT_STA_RX_FILTER;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02003344 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003345 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
Juuso Oikarinena6fe2312009-12-11 15:40:58 +02003346 wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003347 wl->basic_rate = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02003348 wl->rate_set = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03003349 wl->band = IEEE80211_BAND_2GHZ;
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03003350 wl->vif = NULL;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02003351 wl->flags = 0;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003352 wl->sg_enabled = true;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03003353 wl->hw_pg_ver = -1;
Arik Nemtsov166d5042010-10-16 21:44:57 +02003354 wl->bss_type = MAX_BSS_TYPE;
3355 wl->set_bss_type = MAX_BSS_TYPE;
3356 wl->fw_bss_type = MAX_BSS_TYPE;
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02003357 wl->last_tx_hlid = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003358
Ido Yariv25eeb9e2010-10-12 16:20:06 +02003359 memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
Juuso Oikarinenbe7078c2009-10-08 21:56:26 +03003360 for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003361 wl->tx_frames[i] = NULL;
3362
3363 spin_lock_init(&wl->wl_lock);
3364
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003365 wl->state = WL1271_STATE_OFF;
3366 mutex_init(&wl->mutex);
3367
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02003368 /* Apply default driver configuration. */
3369 wl1271_conf_init(wl);
3370
Ido Yariv1f37cbc2010-09-30 13:28:27 +02003371 order = get_order(WL1271_AGGR_BUFFER_SIZE);
3372 wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
3373 if (!wl->aggr_buf) {
3374 ret = -ENOMEM;
3375 goto err_hw;
3376 }
3377
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003378 /* Register platform device */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003379 ret = platform_device_register(wl->plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003380 if (ret) {
3381 wl1271_error("couldn't register platform device");
Ido Yariv1f37cbc2010-09-30 13:28:27 +02003382 goto err_aggr;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003383 }
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003384 dev_set_drvdata(&wl->plat_dev->dev, wl);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003385
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003386 /* Create sysfs file to control bt coex state */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003387 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003388 if (ret < 0) {
3389 wl1271_error("failed to create sysfs file bt_coex_state");
3390 goto err_platform;
3391 }
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003392
Juuso Oikarinend717fd62010-05-07 11:38:58 +03003393 /* Create sysfs file to get HW PG version */
3394 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
3395 if (ret < 0) {
3396 wl1271_error("failed to create sysfs file hw_pg_ver");
3397 goto err_bt_coex_state;
3398 }
3399
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02003400 return hw;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003401
Juuso Oikarinend717fd62010-05-07 11:38:58 +03003402err_bt_coex_state:
3403 device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
3404
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003405err_platform:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003406 platform_device_unregister(wl->plat_dev);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003407
Ido Yariv1f37cbc2010-09-30 13:28:27 +02003408err_aggr:
3409 free_pages((unsigned long)wl->aggr_buf, order);
3410
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003411err_hw:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003412 wl1271_debugfs_exit(wl);
3413 kfree(plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003414
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003415err_plat_alloc:
3416 ieee80211_free_hw(hw);
3417
3418err_hw_alloc:
3419
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003420 return ERR_PTR(ret);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02003421}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02003422EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02003423
3424int wl1271_free_hw(struct wl1271 *wl)
3425{
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003426 platform_device_unregister(wl->plat_dev);
Ido Yariv1f37cbc2010-09-30 13:28:27 +02003427 free_pages((unsigned long)wl->aggr_buf,
3428 get_order(WL1271_AGGR_BUFFER_SIZE));
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003429 kfree(wl->plat_dev);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02003430
3431 wl1271_debugfs_exit(wl);
3432
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02003433 vfree(wl->fw);
3434 wl->fw = NULL;
3435 kfree(wl->nvs);
3436 wl->nvs = NULL;
3437
3438 kfree(wl->fw_status);
3439 kfree(wl->tx_res_if);
3440
3441 ieee80211_free_hw(wl->hw);
3442
3443 return 0;
3444}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02003445EXPORT_SYMBOL_GPL(wl1271_free_hw);
3446
Guy Eilam491bbd62011-01-12 10:33:29 +01003447u32 wl12xx_debug_level = DEBUG_NONE;
Eliad Peller17c17552010-12-12 12:15:35 +02003448EXPORT_SYMBOL_GPL(wl12xx_debug_level);
Guy Eilam491bbd62011-01-12 10:33:29 +01003449module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
Eliad Peller17c17552010-12-12 12:15:35 +02003450MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
3451
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02003452MODULE_LICENSE("GPL");
Luciano Coelhob1a48ca2011-02-22 14:19:28 +02003453MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02003454MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");