blob: fecb0c313a1d6e0003095b13a8779f30856ec000 [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
34#include "wl1271.h"
35#include "wl12xx_80211.h"
36#include "wl1271_reg.h"
Teemu Paasikivi7b048c52010-02-18 13:25:55 +020037#include "wl1271_io.h"
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030038#include "wl1271_event.h"
39#include "wl1271_tx.h"
40#include "wl1271_rx.h"
41#include "wl1271_ps.h"
42#include "wl1271_init.h"
43#include "wl1271_debugfs.h"
44#include "wl1271_cmd.h"
45#include "wl1271_boot.h"
Kalle Valoc8c90872010-02-18 13:25:53 +020046#include "wl1271_testmode.h"
Luciano Coelho34dd2aa2010-07-08 17:50:06 +030047#include "wl1271_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,
119 .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,
123 .aflags = 0
124 },
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 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200156 .tid_conf_count = 4,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300157 .tid_conf = {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200158 [CONF_TX_AC_BE] = {
159 .queue_id = CONF_TX_AC_BE,
160 .channel_type = CONF_CHANNEL_TYPE_EDCF,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300161 .tsid = CONF_TX_AC_BE,
162 .ps_scheme = CONF_PS_SCHEME_LEGACY,
163 .ack_policy = CONF_ACK_POLICY_LEGACY,
164 .apsd_conf = {0, 0},
165 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200166 [CONF_TX_AC_BK] = {
167 .queue_id = CONF_TX_AC_BK,
168 .channel_type = CONF_CHANNEL_TYPE_EDCF,
169 .tsid = CONF_TX_AC_BK,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300170 .ps_scheme = CONF_PS_SCHEME_LEGACY,
171 .ack_policy = CONF_ACK_POLICY_LEGACY,
172 .apsd_conf = {0, 0},
173 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200174 [CONF_TX_AC_VI] = {
175 .queue_id = CONF_TX_AC_VI,
176 .channel_type = CONF_CHANNEL_TYPE_EDCF,
177 .tsid = CONF_TX_AC_VI,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300178 .ps_scheme = CONF_PS_SCHEME_LEGACY,
179 .ack_policy = CONF_ACK_POLICY_LEGACY,
180 .apsd_conf = {0, 0},
181 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200182 [CONF_TX_AC_VO] = {
183 .queue_id = CONF_TX_AC_VO,
184 .channel_type = CONF_CHANNEL_TYPE_EDCF,
185 .tsid = CONF_TX_AC_VO,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300186 .ps_scheme = CONF_PS_SCHEME_LEGACY,
187 .ack_policy = CONF_ACK_POLICY_LEGACY,
188 .apsd_conf = {0, 0},
189 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300190 },
191 .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD,
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200192 .tx_compl_timeout = 700,
Juuso Oikarinenebba60c2010-04-01 11:38:20 +0300193 .tx_compl_threshold = 4,
194 .basic_rate = CONF_HW_BIT_RATE_1MBPS,
195 .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300196 },
197 .conn = {
198 .wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
Juuso Oikarinen50c500a2010-04-01 11:38:22 +0300199 .listen_interval = 1,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300200 .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED,
201 .bcn_filt_ie_count = 1,
202 .bcn_filt_ie = {
203 [0] = {
204 .ie = WLAN_EID_CHANNEL_SWITCH,
205 .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE,
206 }
207 },
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200208 .synch_fail_thold = 10,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300209 .bss_lose_timeout = 100,
210 .beacon_rx_timeout = 10000,
211 .broadcast_timeout = 20000,
212 .rx_broadcast_in_ps = 1,
Juuso Oikarinen90494a92010-07-08 17:50:00 +0300213 .ps_poll_threshold = 10,
214 .ps_poll_recovery_period = 700,
Juuso Oikarinen11f70f92009-10-13 12:47:46 +0300215 .bet_enable = CONF_BET_MODE_ENABLE,
Juuso Oikarinen84502562009-11-23 23:22:12 +0200216 .bet_max_consecutive = 10,
Juuso Oikarinenc1899552010-03-26 12:53:32 +0200217 .psm_entry_retries = 3,
Juuso Oikarinen50c500a2010-04-01 11:38:22 +0300218 .keep_alive_interval = 55000,
219 .max_listen_interval = 20,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300220 },
Luciano Coelho6e92b412009-12-11 15:40:50 +0200221 .itrim = {
222 .enable = false,
223 .timeout = 50000,
Juuso Oikarinen38ad2d82009-12-11 15:41:08 +0200224 },
225 .pm_config = {
226 .host_clk_settling_time = 5000,
227 .host_fast_wakeup_support = false
Juuso Oikarinen00236aed2010-04-09 11:07:30 +0300228 },
229 .roam_trigger = {
Juuso Oikarinen00236aed2010-04-09 11:07:30 +0300230 .trigger_pacing = 1,
231 .avg_weight_rssi_beacon = 20,
232 .avg_weight_rssi_data = 10,
233 .avg_weight_snr_beacon = 20,
234 .avg_weight_snr_data = 10
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300235 }
236};
237
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200238static void __wl1271_op_remove_interface(struct wl1271 *wl);
239
240
Juuso Oikarinena1dd8182010-03-18 12:26:31 +0200241static void wl1271_device_release(struct device *dev)
242{
243
244}
245
246static struct platform_device wl1271_device = {
247 .name = "wl1271",
248 .id = -1,
249
250 /* device model insists to have a release function */
251 .dev = {
252 .release = wl1271_device_release,
253 },
254};
255
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300256static LIST_HEAD(wl_list);
257
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300258static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
259 void *arg)
260{
261 struct net_device *dev = arg;
262 struct wireless_dev *wdev;
263 struct wiphy *wiphy;
264 struct ieee80211_hw *hw;
265 struct wl1271 *wl;
266 struct wl1271 *wl_temp;
267 int ret = 0;
268
269 /* Check that this notification is for us. */
270 if (what != NETDEV_CHANGE)
271 return NOTIFY_DONE;
272
273 wdev = dev->ieee80211_ptr;
274 if (wdev == NULL)
275 return NOTIFY_DONE;
276
277 wiphy = wdev->wiphy;
278 if (wiphy == NULL)
279 return NOTIFY_DONE;
280
281 hw = wiphy_priv(wiphy);
282 if (hw == NULL)
283 return NOTIFY_DONE;
284
285 wl_temp = hw->priv;
286 list_for_each_entry(wl, &wl_list, list) {
287 if (wl == wl_temp)
288 break;
289 }
290 if (wl != wl_temp)
291 return NOTIFY_DONE;
292
293 mutex_lock(&wl->mutex);
294
295 if (wl->state == WL1271_STATE_OFF)
296 goto out;
297
298 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
299 goto out;
300
301 ret = wl1271_ps_elp_wakeup(wl, false);
302 if (ret < 0)
303 goto out;
304
305 if ((dev->operstate == IF_OPER_UP) &&
306 !test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags)) {
307 wl1271_cmd_set_sta_state(wl);
308 wl1271_info("Association completed.");
309 }
310
311 wl1271_ps_elp_sleep(wl);
312
313out:
314 mutex_unlock(&wl->mutex);
315
316 return NOTIFY_OK;
317}
318
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300319static void wl1271_conf_init(struct wl1271 *wl)
320{
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300321
322 /*
323 * This function applies the default configuration to the driver. This
324 * function is invoked upon driver load (spi probe.)
325 *
326 * The configuration is stored in a run-time structure in order to
327 * facilitate for run-time adjustment of any of the parameters. Making
328 * changes to the configuration structure will apply the new values on
329 * the next interface up (wl1271_op_start.)
330 */
331
332 /* apply driver default configuration */
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300333 memcpy(&wl->conf, &default_conf, sizeof(default_conf));
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300334}
335
336
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300337static int wl1271_plt_init(struct wl1271 *wl)
338{
Luciano Coelho12419cc2010-02-18 13:25:44 +0200339 struct conf_tx_ac_category *conf_ac;
340 struct conf_tx_tid *conf_tid;
341 int ret, i;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300342
Luciano Coelho98b5dd52009-11-23 23:22:17 +0200343 ret = wl1271_cmd_general_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200344 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200345 return ret;
346
Luciano Coelho98b5dd52009-11-23 23:22:17 +0200347 ret = wl1271_cmd_radio_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200348 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200349 return ret;
350
Luciano Coelho12419cc2010-02-18 13:25:44 +0200351 ret = wl1271_init_templates_config(wl);
352 if (ret < 0)
353 return ret;
354
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300355 ret = wl1271_acx_init_mem_config(wl);
356 if (ret < 0)
357 return ret;
358
Luciano Coelho12419cc2010-02-18 13:25:44 +0200359 /* PHY layer config */
360 ret = wl1271_init_phy_config(wl);
361 if (ret < 0)
362 goto out_free_memmap;
363
364 ret = wl1271_acx_dco_itrim_params(wl);
365 if (ret < 0)
366 goto out_free_memmap;
367
368 /* Initialize connection monitoring thresholds */
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +0200369 ret = wl1271_acx_conn_monit_params(wl, false);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200370 if (ret < 0)
371 goto out_free_memmap;
372
373 /* Bluetooth WLAN coexistence */
374 ret = wl1271_init_pta(wl);
375 if (ret < 0)
376 goto out_free_memmap;
377
378 /* Energy detection */
379 ret = wl1271_init_energy_detection(wl);
380 if (ret < 0)
381 goto out_free_memmap;
382
383 /* Default fragmentation threshold */
384 ret = wl1271_acx_frag_threshold(wl);
385 if (ret < 0)
386 goto out_free_memmap;
387
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200388 /* Default TID/AC configuration */
389 BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200390 for (i = 0; i < wl->conf.tx.tid_conf_count; i++) {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200391 conf_ac = &wl->conf.tx.ac_conf[i];
392 ret = wl1271_acx_ac_cfg(wl, conf_ac->ac, conf_ac->cw_min,
393 conf_ac->cw_max, conf_ac->aifsn,
394 conf_ac->tx_op_limit);
395 if (ret < 0)
396 goto out_free_memmap;
397
Luciano Coelho12419cc2010-02-18 13:25:44 +0200398 conf_tid = &wl->conf.tx.tid_conf[i];
399 ret = wl1271_acx_tid_cfg(wl, conf_tid->queue_id,
400 conf_tid->channel_type,
401 conf_tid->tsid,
402 conf_tid->ps_scheme,
403 conf_tid->ack_policy,
404 conf_tid->apsd_conf[0],
405 conf_tid->apsd_conf[1]);
406 if (ret < 0)
407 goto out_free_memmap;
408 }
409
Luciano Coelho12419cc2010-02-18 13:25:44 +0200410 /* Enable data path */
Luciano Coelho94210892009-12-11 15:40:55 +0200411 ret = wl1271_cmd_data_path(wl, 1);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300412 if (ret < 0)
Luciano Coelho12419cc2010-02-18 13:25:44 +0200413 goto out_free_memmap;
414
415 /* Configure for CAM power saving (ie. always active) */
416 ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
417 if (ret < 0)
418 goto out_free_memmap;
419
420 /* configure PM */
421 ret = wl1271_acx_pm_config(wl);
422 if (ret < 0)
423 goto out_free_memmap;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300424
425 return 0;
Luciano Coelho12419cc2010-02-18 13:25:44 +0200426
427 out_free_memmap:
428 kfree(wl->target_mem_map);
429 wl->target_mem_map = NULL;
430
431 return ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300432}
433
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300434static void wl1271_fw_status(struct wl1271 *wl,
435 struct wl1271_fw_status *status)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300436{
Juuso Oikarinenac5e1e32010-02-22 08:38:38 +0200437 struct timespec ts;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300438 u32 total = 0;
439 int i;
440
Teemu Paasikivi09a9c2b2010-02-22 08:38:28 +0200441 wl1271_raw_read(wl, FW_STATUS_ADDR, status, sizeof(*status), false);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300442
443 wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
444 "drv_rx_counter = %d, tx_results_counter = %d)",
445 status->intr,
446 status->fw_rx_counter,
447 status->drv_rx_counter,
448 status->tx_results_counter);
449
450 /* update number of available TX blocks */
451 for (i = 0; i < NUM_TX_QUEUES; i++) {
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300452 u32 cnt = le32_to_cpu(status->tx_released_blks[i]) -
453 wl->tx_blocks_freed[i];
454
455 wl->tx_blocks_freed[i] =
456 le32_to_cpu(status->tx_released_blks[i]);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300457 wl->tx_blocks_available += cnt;
458 total += cnt;
459 }
460
461 /* if more blocks are available now, schedule some tx work */
462 if (total && !skb_queue_empty(&wl->tx_queue))
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300463 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300464
465 /* update the host-chipset time offset */
Juuso Oikarinenac5e1e32010-02-22 08:38:38 +0200466 getnstimeofday(&ts);
467 wl->time_offset = (timespec_to_ns(&ts) >> 10) -
468 (s64)le32_to_cpu(status->fw_localtime);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300469}
470
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200471#define WL1271_IRQ_MAX_LOOPS 10
472
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300473static void wl1271_irq_work(struct work_struct *work)
474{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300475 int ret;
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300476 u32 intr;
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200477 int loopcount = WL1271_IRQ_MAX_LOOPS;
478 unsigned long flags;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300479 struct wl1271 *wl =
480 container_of(work, struct wl1271, irq_work);
481
482 mutex_lock(&wl->mutex);
483
484 wl1271_debug(DEBUG_IRQ, "IRQ work");
485
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200486 if (unlikely(wl->state == WL1271_STATE_OFF))
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300487 goto out;
488
489 ret = wl1271_ps_elp_wakeup(wl, true);
490 if (ret < 0)
491 goto out;
492
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200493 spin_lock_irqsave(&wl->wl_lock, flags);
494 while (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags) && loopcount) {
495 clear_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags);
496 spin_unlock_irqrestore(&wl->wl_lock, flags);
497 loopcount--;
498
499 wl1271_fw_status(wl, wl->fw_status);
500 intr = le32_to_cpu(wl->fw_status->intr);
501 if (!intr) {
502 wl1271_debug(DEBUG_IRQ, "Zero interrupt received.");
Dan Carpentercdd08642010-05-08 18:25:17 +0200503 spin_lock_irqsave(&wl->wl_lock, flags);
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200504 continue;
505 }
506
507 intr &= WL1271_INTR_MASK;
508
509 if (intr & WL1271_ACX_INTR_DATA) {
510 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
511
512 /* check for tx results */
513 if (wl->fw_status->tx_results_counter !=
514 (wl->tx_results_count & 0xff))
515 wl1271_tx_complete(wl);
516
517 wl1271_rx(wl, wl->fw_status);
518 }
519
520 if (intr & WL1271_ACX_INTR_EVENT_A) {
521 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
522 wl1271_event_handle(wl, 0);
523 }
524
525 if (intr & WL1271_ACX_INTR_EVENT_B) {
526 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
527 wl1271_event_handle(wl, 1);
528 }
529
530 if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
531 wl1271_debug(DEBUG_IRQ,
532 "WL1271_ACX_INTR_INIT_COMPLETE");
533
534 if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
535 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
536
537 spin_lock_irqsave(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300538 }
539
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200540 if (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags))
541 ieee80211_queue_work(wl->hw, &wl->irq_work);
542 else
543 clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
544 spin_unlock_irqrestore(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300545
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300546 wl1271_ps_elp_sleep(wl);
547
548out:
549 mutex_unlock(&wl->mutex);
550}
551
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300552static int wl1271_fetch_firmware(struct wl1271 *wl)
553{
554 const struct firmware *fw;
555 int ret;
556
Teemu Paasikivi8197b712010-02-22 08:38:23 +0200557 ret = request_firmware(&fw, WL1271_FW_NAME, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300558
559 if (ret < 0) {
560 wl1271_error("could not get firmware: %d", ret);
561 return ret;
562 }
563
564 if (fw->size % 4) {
565 wl1271_error("firmware size is not multiple of 32 bits: %zu",
566 fw->size);
567 ret = -EILSEQ;
568 goto out;
569 }
570
571 wl->fw_len = fw->size;
Juuso Oikarinen1fba4972009-10-08 21:56:32 +0300572 wl->fw = vmalloc(wl->fw_len);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300573
574 if (!wl->fw) {
575 wl1271_error("could not allocate memory for the firmware");
576 ret = -ENOMEM;
577 goto out;
578 }
579
580 memcpy(wl->fw, fw->data, wl->fw_len);
581
582 ret = 0;
583
584out:
585 release_firmware(fw);
586
587 return ret;
588}
589
590static int wl1271_fetch_nvs(struct wl1271 *wl)
591{
592 const struct firmware *fw;
593 int ret;
594
Teemu Paasikivi8197b712010-02-22 08:38:23 +0200595 ret = request_firmware(&fw, WL1271_NVS_NAME, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300596
597 if (ret < 0) {
598 wl1271_error("could not get nvs file: %d", ret);
599 return ret;
600 }
601
Julia Lawall929ebd32010-05-15 23:16:39 +0200602 wl->nvs = kmemdup(fw->data, sizeof(struct wl1271_nvs_file), GFP_KERNEL);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300603
604 if (!wl->nvs) {
605 wl1271_error("could not allocate memory for the nvs file");
606 ret = -ENOMEM;
607 goto out;
608 }
609
Juuso Oikarinen02fabb02010-08-19 04:41:15 +0200610 wl->nvs_len = fw->size;
611
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300612out:
613 release_firmware(fw);
614
615 return ret;
616}
617
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200618static void wl1271_recovery_work(struct work_struct *work)
619{
620 struct wl1271 *wl =
621 container_of(work, struct wl1271, recovery_work);
622
623 mutex_lock(&wl->mutex);
624
625 if (wl->state != WL1271_STATE_ON)
626 goto out;
627
628 wl1271_info("Hardware recovery in progress.");
629
630 /* reboot the chipset */
631 __wl1271_op_remove_interface(wl);
632 ieee80211_restart_hw(wl->hw);
633
634out:
635 mutex_unlock(&wl->mutex);
636}
637
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300638static void wl1271_fw_wakeup(struct wl1271 *wl)
639{
640 u32 elp_reg;
641
642 elp_reg = ELPCTRL_WAKE_UP;
Juuso Oikarinen74621412009-10-12 15:08:54 +0300643 wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300644}
645
646static int wl1271_setup(struct wl1271 *wl)
647{
648 wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL);
649 if (!wl->fw_status)
650 return -ENOMEM;
651
652 wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
653 if (!wl->tx_res_if) {
654 kfree(wl->fw_status);
655 return -ENOMEM;
656 }
657
658 INIT_WORK(&wl->irq_work, wl1271_irq_work);
659 INIT_WORK(&wl->tx_work, wl1271_tx_work);
Juuso Oikarinenc454f1d2010-08-24 06:28:03 +0300660 INIT_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200661 INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
Juuso Oikarinenc454f1d2010-08-24 06:28:03 +0300662
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300663 return 0;
664}
665
666static int wl1271_chip_wakeup(struct wl1271 *wl)
667{
Juuso Oikarinen451de972009-10-12 15:08:46 +0300668 struct wl1271_partition_set partition;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300669 int ret = 0;
670
Juuso Oikarinen01ac17e2009-12-11 15:41:02 +0200671 msleep(WL1271_PRE_POWER_ON_SLEEP);
Ohad Ben-Cohen2cc78ff2010-09-16 01:22:04 +0200672 ret = wl1271_power_on(wl);
673 if (ret < 0)
674 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300675 msleep(WL1271_POWER_ON_SLEEP);
Teemu Paasikivi9b280722010-02-18 13:25:56 +0200676 wl1271_io_reset(wl);
677 wl1271_io_init(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300678
679 /* We don't need a real memory partition here, because we only want
680 * to use the registers at this point. */
Juuso Oikarinen451de972009-10-12 15:08:46 +0300681 memset(&partition, 0, sizeof(partition));
682 partition.reg.start = REGISTERS_BASE;
683 partition.reg.size = REGISTERS_DOWN_SIZE;
684 wl1271_set_partition(wl, &partition);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300685
686 /* ELP module wake up */
687 wl1271_fw_wakeup(wl);
688
689 /* whal_FwCtrl_BootSm() */
690
691 /* 0. read chip id from CHIP_ID */
Teemu Paasikivi7b048c52010-02-18 13:25:55 +0200692 wl->chip.id = wl1271_read32(wl, CHIP_ID_B);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300693
694 /* 1. check if chip id is valid */
695
696 switch (wl->chip.id) {
697 case CHIP_ID_1271_PG10:
698 wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
699 wl->chip.id);
700
701 ret = wl1271_setup(wl);
702 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200703 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300704 break;
705 case CHIP_ID_1271_PG20:
706 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
707 wl->chip.id);
708
709 ret = wl1271_setup(wl);
710 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200711 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300712 break;
713 default:
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200714 wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300715 ret = -ENODEV;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200716 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300717 }
718
719 if (wl->fw == NULL) {
720 ret = wl1271_fetch_firmware(wl);
721 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200722 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300723 }
724
725 /* No NVS from netlink, try to get it from the filesystem */
726 if (wl->nvs == NULL) {
727 ret = wl1271_fetch_nvs(wl);
728 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200729 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300730 }
731
732out:
733 return ret;
734}
735
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300736int wl1271_plt_start(struct wl1271 *wl)
737{
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200738 int retries = WL1271_BOOT_RETRIES;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300739 int ret;
740
741 mutex_lock(&wl->mutex);
742
743 wl1271_notice("power up");
744
745 if (wl->state != WL1271_STATE_OFF) {
746 wl1271_error("cannot go into PLT state because not "
747 "in off state: %d", wl->state);
748 ret = -EBUSY;
749 goto out;
750 }
751
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200752 while (retries) {
753 retries--;
754 ret = wl1271_chip_wakeup(wl);
755 if (ret < 0)
756 goto power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300757
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200758 ret = wl1271_boot(wl);
759 if (ret < 0)
760 goto power_off;
761
762 ret = wl1271_plt_init(wl);
763 if (ret < 0)
764 goto irq_disable;
765
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200766 wl->state = WL1271_STATE_PLT;
767 wl1271_notice("firmware booted in PLT mode (%s)",
768 wl->chip.fw_ver);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300769 goto out;
770
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200771irq_disable:
772 wl1271_disable_interrupts(wl);
773 mutex_unlock(&wl->mutex);
774 /* Unlocking the mutex in the middle of handling is
775 inherently unsafe. In this case we deem it safe to do,
776 because we need to let any possibly pending IRQ out of
777 the system (and while we are WL1271_STATE_OFF the IRQ
778 work function will not do anything.) Also, any other
779 possible concurrent operations will fail due to the
780 current state, hence the wl1271 struct should be safe. */
781 cancel_work_sync(&wl->irq_work);
782 mutex_lock(&wl->mutex);
783power_off:
784 wl1271_power_off(wl);
785 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300786
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200787 wl1271_error("firmware boot in PLT mode failed despite %d retries",
788 WL1271_BOOT_RETRIES);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300789out:
790 mutex_unlock(&wl->mutex);
791
792 return ret;
793}
794
795int wl1271_plt_stop(struct wl1271 *wl)
796{
797 int ret = 0;
798
799 mutex_lock(&wl->mutex);
800
801 wl1271_notice("power down");
802
803 if (wl->state != WL1271_STATE_PLT) {
804 wl1271_error("cannot power down because not in PLT "
805 "state: %d", wl->state);
806 ret = -EBUSY;
807 goto out;
808 }
809
810 wl1271_disable_interrupts(wl);
811 wl1271_power_off(wl);
812
813 wl->state = WL1271_STATE_OFF;
Luciano Coelhobd5ea182009-10-13 12:47:58 +0300814 wl->rx_counter = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300815
816out:
817 mutex_unlock(&wl->mutex);
818
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +0200819 cancel_work_sync(&wl->irq_work);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200820 cancel_work_sync(&wl->recovery_work);
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +0200821
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300822 return ret;
823}
824
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300825static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
826{
827 struct wl1271 *wl = hw->priv;
Juuso Oikarinen830fb672009-12-11 15:41:06 +0200828 struct ieee80211_conf *conf = &hw->conf;
829 struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
830 struct ieee80211_sta *sta = txinfo->control.sta;
831 unsigned long flags;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300832
Juuso Oikarinen830fb672009-12-11 15:41:06 +0200833 /* peek into the rates configured in the STA entry */
834 spin_lock_irqsave(&wl->wl_lock, flags);
835 if (sta && sta->supp_rates[conf->channel->band] != wl->sta_rate_set) {
836 wl->sta_rate_set = sta->supp_rates[conf->channel->band];
837 set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags);
838 }
839 spin_unlock_irqrestore(&wl->wl_lock, flags);
840
841 /* queue the packet */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300842 skb_queue_tail(&wl->tx_queue, skb);
843
844 /*
845 * The chip specific setup must run before the first TX packet -
846 * before that, the tx_work will not be initialized!
847 */
848
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300849 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300850
851 /*
852 * The workqueue is slow to process the tx_queue and we need stop
853 * the queue here, otherwise the queue will get too long.
854 */
Juuso Oikarinen06f7bc72010-02-22 08:38:33 +0200855 if (skb_queue_len(&wl->tx_queue) >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
856 wl1271_debug(DEBUG_TX, "op_tx: stopping queues");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300857
Juuso Oikarinen06f7bc72010-02-22 08:38:33 +0200858 spin_lock_irqsave(&wl->wl_lock, flags);
859 ieee80211_stop_queues(wl->hw);
Juuso Oikarinen71449f82009-12-11 15:41:07 +0200860 set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
Juuso Oikarinen06f7bc72010-02-22 08:38:33 +0200861 spin_unlock_irqrestore(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300862 }
863
864 return NETDEV_TX_OK;
865}
866
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300867static struct notifier_block wl1271_dev_notifier = {
868 .notifier_call = wl1271_dev_notify,
869};
870
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300871static int wl1271_op_start(struct ieee80211_hw *hw)
872{
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200873 wl1271_debug(DEBUG_MAC80211, "mac80211 start");
874
875 /*
876 * We have to delay the booting of the hardware because
877 * we need to know the local MAC address before downloading and
878 * initializing the firmware. The MAC address cannot be changed
879 * after boot, and without the proper MAC address, the firmware
880 * will not function properly.
881 *
882 * The MAC address is first known when the corresponding interface
883 * is added. That is where we will initialize the hardware.
884 */
885
886 return 0;
887}
888
889static void wl1271_op_stop(struct ieee80211_hw *hw)
890{
891 wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
892}
893
894static int wl1271_op_add_interface(struct ieee80211_hw *hw,
895 struct ieee80211_vif *vif)
896{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300897 struct wl1271 *wl = hw->priv;
John W. Linvilleac01e942010-07-28 17:09:41 -0400898 struct wiphy *wiphy = hw->wiphy;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200899 int retries = WL1271_BOOT_RETRIES;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300900 int ret = 0;
901
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200902 wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
903 vif->type, vif->addr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300904
905 mutex_lock(&wl->mutex);
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200906 if (wl->vif) {
907 ret = -EBUSY;
908 goto out;
909 }
910
911 wl->vif = vif;
912
913 switch (vif->type) {
914 case NL80211_IFTYPE_STATION:
915 wl->bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +0200916 wl->set_bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200917 break;
918 case NL80211_IFTYPE_ADHOC:
919 wl->bss_type = BSS_TYPE_IBSS;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +0200920 wl->set_bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200921 break;
922 default:
923 ret = -EOPNOTSUPP;
924 goto out;
925 }
926
927 memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300928
929 if (wl->state != WL1271_STATE_OFF) {
930 wl1271_error("cannot start because not in off state: %d",
931 wl->state);
932 ret = -EBUSY;
933 goto out;
934 }
935
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200936 while (retries) {
937 retries--;
938 ret = wl1271_chip_wakeup(wl);
939 if (ret < 0)
940 goto power_off;
941
942 ret = wl1271_boot(wl);
943 if (ret < 0)
944 goto power_off;
945
946 ret = wl1271_hw_init(wl);
947 if (ret < 0)
948 goto irq_disable;
949
950 wl->state = WL1271_STATE_ON;
951 wl1271_info("firmware booted (%s)", wl->chip.fw_ver);
John W. Linvilleac01e942010-07-28 17:09:41 -0400952
953 /* update hw/fw version info in wiphy struct */
954 wiphy->hw_version = wl->chip.id;
955 strncpy(wiphy->fw_version, wl->chip.fw_ver,
956 sizeof(wiphy->fw_version));
957
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300958 goto out;
959
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200960irq_disable:
961 wl1271_disable_interrupts(wl);
962 mutex_unlock(&wl->mutex);
963 /* Unlocking the mutex in the middle of handling is
964 inherently unsafe. In this case we deem it safe to do,
965 because we need to let any possibly pending IRQ out of
966 the system (and while we are WL1271_STATE_OFF the IRQ
967 work function will not do anything.) Also, any other
968 possible concurrent operations will fail due to the
969 current state, hence the wl1271 struct should be safe. */
970 cancel_work_sync(&wl->irq_work);
971 mutex_lock(&wl->mutex);
972power_off:
973 wl1271_power_off(wl);
974 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300975
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200976 wl1271_error("firmware boot failed despite %d retries",
977 WL1271_BOOT_RETRIES);
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300978out:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300979 mutex_unlock(&wl->mutex);
980
Juuso Oikarineneb887df2010-07-08 17:49:58 +0300981 if (!ret)
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300982 list_add(&wl->list, &wl_list);
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300983
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300984 return ret;
985}
986
Juuso Oikarinen52a2a372010-09-21 06:23:30 +0200987static void __wl1271_op_remove_interface(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300988{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300989 int i;
990
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200991 wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300992
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200993 wl1271_info("down");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300994
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300995 list_del(&wl->list);
996
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300997 WARN_ON(wl->state != WL1271_STATE_ON);
998
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +0300999 /* enable dyn ps just in case (if left on due to fw crash etc) */
Juuso Oikarinen9a547bf2010-07-08 17:50:04 +03001000 if (wl->bss_type == BSS_TYPE_STA_BSS)
Juuso Oikarinenf532be62010-07-08 17:50:05 +03001001 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001002
Luciano Coelho08688d62010-07-08 17:50:07 +03001003 if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
Luciano Coelho08688d62010-07-08 17:50:07 +03001004 wl->scan.state = WL1271_SCAN_STATE_IDLE;
1005 kfree(wl->scan.scanned_ch);
1006 wl->scan.scanned_ch = NULL;
Juuso Oikarinen76a029f2010-07-29 04:54:45 +03001007 ieee80211_scan_completed(wl->hw, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001008 }
1009
1010 wl->state = WL1271_STATE_OFF;
1011
1012 wl1271_disable_interrupts(wl);
1013
1014 mutex_unlock(&wl->mutex);
1015
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001016 cancel_work_sync(&wl->scan_complete_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001017 cancel_work_sync(&wl->irq_work);
1018 cancel_work_sync(&wl->tx_work);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001019 cancel_delayed_work_sync(&wl->pspoll_work);
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +02001020 cancel_delayed_work_sync(&wl->elp_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001021
1022 mutex_lock(&wl->mutex);
1023
1024 /* let's notify MAC80211 about the remaining pending TX frames */
Juuso Oikarinen781608c2010-05-24 11:18:17 +03001025 wl1271_tx_reset(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001026 wl1271_power_off(wl);
1027
1028 memset(wl->bssid, 0, ETH_ALEN);
1029 memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1);
1030 wl->ssid_len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001031 wl->bss_type = MAX_BSS_TYPE;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001032 wl->set_bss_type = MAX_BSS_TYPE;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001033 wl->band = IEEE80211_BAND_2GHZ;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001034
1035 wl->rx_counter = 0;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02001036 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001037 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
1038 wl->tx_blocks_available = 0;
1039 wl->tx_results_count = 0;
1040 wl->tx_packets_count = 0;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001041 wl->tx_security_last_seq = 0;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001042 wl->tx_security_seq = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001043 wl->time_offset = 0;
1044 wl->session_counter = 0;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001045 wl->rate_set = CONF_TX_RATE_MASK_BASIC;
1046 wl->sta_rate_set = 0;
1047 wl->flags = 0;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001048 wl->vif = NULL;
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001049 wl->filters = 0;
Luciano Coelhod6e19d12009-10-12 15:08:43 +03001050
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001051 for (i = 0; i < NUM_TX_QUEUES; i++)
1052 wl->tx_blocks_freed[i] = 0;
1053
1054 wl1271_debugfs_reset(wl);
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03001055
1056 kfree(wl->fw_status);
1057 wl->fw_status = NULL;
1058 kfree(wl->tx_res_if);
1059 wl->tx_res_if = NULL;
1060 kfree(wl->target_mem_map);
1061 wl->target_mem_map = NULL;
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001062}
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03001063
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001064static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
1065 struct ieee80211_vif *vif)
1066{
1067 struct wl1271 *wl = hw->priv;
1068
1069 mutex_lock(&wl->mutex);
1070 WARN_ON(wl->vif != vif);
1071 __wl1271_op_remove_interface(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001072 mutex_unlock(&wl->mutex);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001073
1074 cancel_work_sync(&wl->recovery_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001075}
1076
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001077static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
1078{
1079 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
1080 wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
1081
1082 /* combine requested filters with current filter config */
1083 filters = wl->filters | filters;
1084
1085 wl1271_debug(DEBUG_FILTERS, "RX filters set: ");
1086
1087 if (filters & FIF_PROMISC_IN_BSS) {
1088 wl1271_debug(DEBUG_FILTERS, " - FIF_PROMISC_IN_BSS");
1089 wl->rx_config &= ~CFG_UNI_FILTER_EN;
1090 wl->rx_config |= CFG_BSSID_FILTER_EN;
1091 }
1092 if (filters & FIF_BCN_PRBRESP_PROMISC) {
1093 wl1271_debug(DEBUG_FILTERS, " - FIF_BCN_PRBRESP_PROMISC");
1094 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1095 wl->rx_config &= ~CFG_SSID_FILTER_EN;
1096 }
1097 if (filters & FIF_OTHER_BSS) {
1098 wl1271_debug(DEBUG_FILTERS, " - FIF_OTHER_BSS");
1099 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1100 }
1101 if (filters & FIF_CONTROL) {
1102 wl1271_debug(DEBUG_FILTERS, " - FIF_CONTROL");
1103 wl->rx_filter |= CFG_RX_CTL_EN;
1104 }
1105 if (filters & FIF_FCSFAIL) {
1106 wl1271_debug(DEBUG_FILTERS, " - FIF_FCSFAIL");
1107 wl->rx_filter |= CFG_RX_FCS_ERROR;
1108 }
1109}
1110
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001111static int wl1271_dummy_join(struct wl1271 *wl)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001112{
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001113 int ret = 0;
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001114 /* we need to use a dummy BSSID for now */
1115 static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
1116 0xad, 0xbe, 0xef };
1117
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001118 memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
1119
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001120 /* pass through frames from all BSS */
1121 wl1271_configure_filters(wl, FIF_OTHER_BSS);
1122
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001123 ret = wl1271_cmd_join(wl, wl->set_bss_type);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001124 if (ret < 0)
1125 goto out;
1126
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001127 set_bit(WL1271_FLAG_JOINED, &wl->flags);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001128
1129out:
1130 return ret;
1131}
1132
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001133static int wl1271_join(struct wl1271 *wl, bool set_assoc)
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001134{
1135 int ret;
1136
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001137 /*
1138 * One of the side effects of the JOIN command is that is clears
1139 * WPA/WPA2 keys from the chipset. Performing a JOIN while associated
1140 * to a WPA/WPA2 access point will therefore kill the data-path.
1141 * Currently there is no supported scenario for JOIN during
1142 * association - if it becomes a supported scenario, the WPA/WPA2 keys
1143 * must be handled somehow.
1144 *
1145 */
1146 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1147 wl1271_info("JOIN while associated.");
1148
1149 if (set_assoc)
1150 set_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
1151
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001152 ret = wl1271_cmd_join(wl, wl->set_bss_type);
1153 if (ret < 0)
1154 goto out;
1155
1156 set_bit(WL1271_FLAG_JOINED, &wl->flags);
1157
1158 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1159 goto out;
1160
1161 /*
1162 * The join command disable the keep-alive mode, shut down its process,
1163 * and also clear the template config, so we need to reset it all after
1164 * the join. The acx_aid starts the keep-alive process, and the order
1165 * of the commands below is relevant.
1166 */
1167 ret = wl1271_acx_keep_alive_mode(wl, true);
1168 if (ret < 0)
1169 goto out;
1170
1171 ret = wl1271_acx_aid(wl, wl->aid);
1172 if (ret < 0)
1173 goto out;
1174
1175 ret = wl1271_cmd_build_klv_null_data(wl);
1176 if (ret < 0)
1177 goto out;
1178
1179 ret = wl1271_acx_keep_alive_config(wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
1180 ACX_KEEP_ALIVE_TPL_VALID);
1181 if (ret < 0)
1182 goto out;
1183
1184out:
1185 return ret;
1186}
1187
1188static int wl1271_unjoin(struct wl1271 *wl)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001189{
1190 int ret;
1191
1192 /* to stop listening to a channel, we disconnect */
1193 ret = wl1271_cmd_disconnect(wl);
1194 if (ret < 0)
1195 goto out;
1196
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001197 clear_bit(WL1271_FLAG_JOINED, &wl->flags);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001198 memset(wl->bssid, 0, ETH_ALEN);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001199
1200 /* stop filterting packets based on bssid */
1201 wl1271_configure_filters(wl, FIF_OTHER_BSS);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001202
1203out:
1204 return ret;
1205}
1206
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001207static void wl1271_set_band_rate(struct wl1271 *wl)
1208{
1209 if (wl->band == IEEE80211_BAND_2GHZ)
1210 wl->basic_rate_set = wl->conf.tx.basic_rate;
1211 else
1212 wl->basic_rate_set = wl->conf.tx.basic_rate_5;
1213}
1214
1215static u32 wl1271_min_rate_get(struct wl1271 *wl)
1216{
1217 int i;
1218 u32 rate = 0;
1219
1220 if (!wl->basic_rate_set) {
1221 WARN_ON(1);
1222 wl->basic_rate_set = wl->conf.tx.basic_rate;
1223 }
1224
1225 for (i = 0; !rate; i++) {
1226 if ((wl->basic_rate_set >> i) & 0x1)
1227 rate = 1 << i;
1228 }
1229
1230 return rate;
1231}
1232
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03001233static int wl1271_handle_idle(struct wl1271 *wl, bool idle)
1234{
1235 int ret;
1236
1237 if (idle) {
1238 if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
1239 ret = wl1271_unjoin(wl);
1240 if (ret < 0)
1241 goto out;
1242 }
1243 wl->rate_set = wl1271_min_rate_get(wl);
1244 wl->sta_rate_set = 0;
1245 ret = wl1271_acx_rate_policies(wl);
1246 if (ret < 0)
1247 goto out;
1248 ret = wl1271_acx_keep_alive_config(
1249 wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
1250 ACX_KEEP_ALIVE_TPL_INVALID);
1251 if (ret < 0)
1252 goto out;
1253 set_bit(WL1271_FLAG_IDLE, &wl->flags);
1254 } else {
1255 /* increment the session counter */
1256 wl->session_counter++;
1257 if (wl->session_counter >= SESSION_COUNTER_MAX)
1258 wl->session_counter = 0;
1259 ret = wl1271_dummy_join(wl);
1260 if (ret < 0)
1261 goto out;
1262 clear_bit(WL1271_FLAG_IDLE, &wl->flags);
1263 }
1264
1265out:
1266 return ret;
1267}
1268
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001269static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
1270{
1271 struct wl1271 *wl = hw->priv;
1272 struct ieee80211_conf *conf = &hw->conf;
1273 int channel, ret = 0;
1274
1275 channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
1276
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001277 wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s",
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001278 channel,
1279 conf->flags & IEEE80211_CONF_PS ? "on" : "off",
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001280 conf->power_level,
1281 conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001282
Juuso Oikarinen781608c2010-05-24 11:18:17 +03001283 /*
1284 * mac80211 will go to idle nearly immediately after transmitting some
1285 * frames, such as the deauth. To make sure those frames reach the air,
1286 * wait here until the TX queue is fully flushed.
1287 */
1288 if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
1289 (conf->flags & IEEE80211_CONF_IDLE))
1290 wl1271_tx_flush(wl);
1291
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001292 mutex_lock(&wl->mutex);
1293
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001294 if (unlikely(wl->state == WL1271_STATE_OFF))
1295 goto out;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001296
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001297 ret = wl1271_ps_elp_wakeup(wl, false);
1298 if (ret < 0)
1299 goto out;
1300
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001301 /* if the channel changes while joined, join again */
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001302 if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
1303 ((wl->band != conf->channel->band) ||
1304 (wl->channel != channel))) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001305 wl->band = conf->channel->band;
1306 wl->channel = channel;
1307
1308 /*
1309 * FIXME: the mac80211 should really provide a fixed rate
1310 * to use here. for now, just use the smallest possible rate
1311 * for the band as a fixed rate for association frames and
1312 * other control messages.
1313 */
1314 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1315 wl1271_set_band_rate(wl);
1316
1317 wl->basic_rate = wl1271_min_rate_get(wl);
1318 ret = wl1271_acx_rate_policies(wl);
1319 if (ret < 0)
1320 wl1271_warning("rate policy for update channel "
1321 "failed %d", ret);
1322
1323 if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001324 ret = wl1271_join(wl, false);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001325 if (ret < 0)
1326 wl1271_warning("cmd join to update channel "
1327 "failed %d", ret);
1328 }
1329 }
1330
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001331 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03001332 ret = wl1271_handle_idle(wl, conf->flags & IEEE80211_CONF_IDLE);
1333 if (ret < 0)
1334 wl1271_warning("idle mode change failed %d", ret);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001335 }
1336
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001337 /*
1338 * if mac80211 changes the PSM mode, make sure the mode is not
1339 * incorrectly changed after the pspoll failure active window.
1340 */
1341 if (changed & IEEE80211_CONF_CHANGE_PS)
1342 clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
1343
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001344 if (conf->flags & IEEE80211_CONF_PS &&
1345 !test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
1346 set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001347
1348 /*
1349 * We enter PSM only if we're already associated.
1350 * If we're not, we'll enter it when joining an SSID,
1351 * through the bss_info_changed() hook.
1352 */
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001353 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02001354 wl1271_debug(DEBUG_PSM, "psm enabled");
Juuso Oikarinend8c42c02010-02-18 13:25:36 +02001355 ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
Juuso Oikarinen65cddbf12010-08-24 06:28:03 +03001356 wl->basic_rate_set, true);
Juuso Oikarinenaf5e0842009-12-11 15:41:00 +02001357 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001358 } else if (!(conf->flags & IEEE80211_CONF_PS) &&
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001359 test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02001360 wl1271_debug(DEBUG_PSM, "psm disabled");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001361
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001362 clear_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001363
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001364 if (test_bit(WL1271_FLAG_PSM, &wl->flags))
Juuso Oikarinend8c42c02010-02-18 13:25:36 +02001365 ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
Juuso Oikarinen65cddbf12010-08-24 06:28:03 +03001366 wl->basic_rate_set, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001367 }
1368
1369 if (conf->power_level != wl->power_level) {
1370 ret = wl1271_acx_tx_power(wl, conf->power_level);
1371 if (ret < 0)
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02001372 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001373
1374 wl->power_level = conf->power_level;
1375 }
1376
1377out_sleep:
1378 wl1271_ps_elp_sleep(wl);
1379
1380out:
1381 mutex_unlock(&wl->mutex);
1382
1383 return ret;
1384}
1385
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001386struct wl1271_filter_params {
1387 bool enabled;
1388 int mc_list_length;
1389 u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
1390};
1391
Jiri Pirko22bedad2010-04-01 21:22:57 +00001392static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw,
1393 struct netdev_hw_addr_list *mc_list)
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001394{
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001395 struct wl1271_filter_params *fp;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001396 struct netdev_hw_addr *ha;
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001397 struct wl1271 *wl = hw->priv;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001398
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001399 if (unlikely(wl->state == WL1271_STATE_OFF))
1400 return 0;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001401
Juuso Oikarinen74441132009-10-13 12:47:53 +03001402 fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001403 if (!fp) {
1404 wl1271_error("Out of memory setting filters.");
1405 return 0;
1406 }
1407
1408 /* update multicast filtering parameters */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001409 fp->mc_list_length = 0;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001410 if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) {
1411 fp->enabled = false;
1412 } else {
1413 fp->enabled = true;
1414 netdev_hw_addr_list_for_each(ha, mc_list) {
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001415 memcpy(fp->mc_list[fp->mc_list_length],
Jiri Pirko22bedad2010-04-01 21:22:57 +00001416 ha->addr, ETH_ALEN);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001417 fp->mc_list_length++;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001418 }
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001419 }
1420
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001421 return (u64)(unsigned long)fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001422}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001423
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001424#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
1425 FIF_ALLMULTI | \
1426 FIF_FCSFAIL | \
1427 FIF_BCN_PRBRESP_PROMISC | \
1428 FIF_CONTROL | \
1429 FIF_OTHER_BSS)
1430
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001431static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
1432 unsigned int changed,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001433 unsigned int *total, u64 multicast)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001434{
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001435 struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001436 struct wl1271 *wl = hw->priv;
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001437 int ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001438
1439 wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter");
1440
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001441 mutex_lock(&wl->mutex);
1442
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001443 *total &= WL1271_SUPPORTED_FILTERS;
1444 changed &= WL1271_SUPPORTED_FILTERS;
1445
1446 if (unlikely(wl->state == WL1271_STATE_OFF))
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001447 goto out;
1448
1449 ret = wl1271_ps_elp_wakeup(wl, false);
1450 if (ret < 0)
1451 goto out;
1452
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001453
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001454 if (*total & FIF_ALLMULTI)
1455 ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
1456 else if (fp)
1457 ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
1458 fp->mc_list,
1459 fp->mc_list_length);
1460 if (ret < 0)
1461 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001462
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001463 /* determine, whether supported filter values have changed */
1464 if (changed == 0)
1465 goto out_sleep;
1466
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001467 /* configure filters */
1468 wl->filters = *total;
1469 wl1271_configure_filters(wl, 0);
1470
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001471 /* apply configured filters */
1472 ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
1473 if (ret < 0)
1474 goto out_sleep;
1475
1476out_sleep:
1477 wl1271_ps_elp_sleep(wl);
1478
1479out:
1480 mutex_unlock(&wl->mutex);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001481 kfree(fp);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001482}
1483
1484static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
1485 struct ieee80211_vif *vif,
1486 struct ieee80211_sta *sta,
1487 struct ieee80211_key_conf *key_conf)
1488{
1489 struct wl1271 *wl = hw->priv;
1490 const u8 *addr;
1491 int ret;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001492 u32 tx_seq_32 = 0;
1493 u16 tx_seq_16 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001494 u8 key_type;
1495
1496 static const u8 bcast_addr[ETH_ALEN] =
1497 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1498
1499 wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
1500
1501 addr = sta ? sta->addr : bcast_addr;
1502
1503 wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd);
1504 wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN);
1505 wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
Johannes Berg97359d12010-08-10 09:46:38 +02001506 key_conf->cipher, key_conf->keyidx,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001507 key_conf->keylen, key_conf->flags);
1508 wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
1509
1510 if (is_zero_ether_addr(addr)) {
1511 /* We dont support TX only encryption */
1512 ret = -EOPNOTSUPP;
1513 goto out;
1514 }
1515
1516 mutex_lock(&wl->mutex);
1517
1518 ret = wl1271_ps_elp_wakeup(wl, false);
1519 if (ret < 0)
1520 goto out_unlock;
1521
Johannes Berg97359d12010-08-10 09:46:38 +02001522 switch (key_conf->cipher) {
1523 case WLAN_CIPHER_SUITE_WEP40:
1524 case WLAN_CIPHER_SUITE_WEP104:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001525 key_type = KEY_WEP;
1526
1527 key_conf->hw_key_idx = key_conf->keyidx;
1528 break;
Johannes Berg97359d12010-08-10 09:46:38 +02001529 case WLAN_CIPHER_SUITE_TKIP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001530 key_type = KEY_TKIP;
1531
1532 key_conf->hw_key_idx = key_conf->keyidx;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001533 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1534 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001535 break;
Johannes Berg97359d12010-08-10 09:46:38 +02001536 case WLAN_CIPHER_SUITE_CCMP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001537 key_type = KEY_AES;
1538
1539 key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001540 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1541 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001542 break;
1543 default:
Johannes Berg97359d12010-08-10 09:46:38 +02001544 wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001545
1546 ret = -EOPNOTSUPP;
1547 goto out_sleep;
1548 }
1549
1550 switch (cmd) {
1551 case SET_KEY:
1552 ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE,
1553 key_conf->keyidx, key_type,
1554 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001555 addr, tx_seq_32, tx_seq_16);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001556 if (ret < 0) {
1557 wl1271_error("Could not add or replace key");
1558 goto out_sleep;
1559 }
Juuso Oikarinenee444cf2010-02-18 13:25:50 +02001560
1561 /* the default WEP key needs to be configured at least once */
1562 if (key_type == KEY_WEP) {
1563 ret = wl1271_cmd_set_default_wep_key(wl,
1564 wl->default_key);
1565 if (ret < 0)
1566 goto out_sleep;
1567 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001568 break;
1569
1570 case DISABLE_KEY:
Juuso Oikarinenfddc7dd2010-02-18 13:25:49 +02001571 /* The wl1271 does not allow to remove unicast keys - they
1572 will be cleared automatically on next CMD_JOIN. Ignore the
1573 request silently, as we dont want the mac80211 to emit
1574 an error message. */
1575 if (!is_broadcast_ether_addr(addr))
1576 break;
1577
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001578 ret = wl1271_cmd_set_key(wl, KEY_REMOVE,
1579 key_conf->keyidx, key_type,
1580 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001581 addr, 0, 0);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001582 if (ret < 0) {
1583 wl1271_error("Could not remove key");
1584 goto out_sleep;
1585 }
1586 break;
1587
1588 default:
1589 wl1271_error("Unsupported key cmd 0x%x", cmd);
1590 ret = -EOPNOTSUPP;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001591 break;
1592 }
1593
1594out_sleep:
1595 wl1271_ps_elp_sleep(wl);
1596
1597out_unlock:
1598 mutex_unlock(&wl->mutex);
1599
1600out:
1601 return ret;
1602}
1603
1604static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
Johannes Berga060bbf2010-04-27 11:59:34 +02001605 struct ieee80211_vif *vif,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001606 struct cfg80211_scan_request *req)
1607{
1608 struct wl1271 *wl = hw->priv;
1609 int ret;
1610 u8 *ssid = NULL;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001611 size_t len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001612
1613 wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
1614
1615 if (req->n_ssids) {
1616 ssid = req->ssids[0].ssid;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001617 len = req->ssids[0].ssid_len;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001618 }
1619
1620 mutex_lock(&wl->mutex);
1621
1622 ret = wl1271_ps_elp_wakeup(wl, false);
1623 if (ret < 0)
1624 goto out;
1625
Luciano Coelho5924f892010-08-04 03:46:22 +03001626 ret = wl1271_scan(hw->priv, ssid, len, req);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001627
1628 wl1271_ps_elp_sleep(wl);
1629
1630out:
1631 mutex_unlock(&wl->mutex);
1632
1633 return ret;
1634}
1635
1636static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1637{
1638 struct wl1271 *wl = hw->priv;
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03001639 int ret = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001640
1641 mutex_lock(&wl->mutex);
1642
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03001643 if (unlikely(wl->state == WL1271_STATE_OFF))
1644 goto out;
1645
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001646 ret = wl1271_ps_elp_wakeup(wl, false);
1647 if (ret < 0)
1648 goto out;
1649
1650 ret = wl1271_acx_rts_threshold(wl, (u16) value);
1651 if (ret < 0)
1652 wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret);
1653
1654 wl1271_ps_elp_sleep(wl);
1655
1656out:
1657 mutex_unlock(&wl->mutex);
1658
1659 return ret;
1660}
1661
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001662static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *beacon)
1663{
1664 u8 *ptr = beacon->data +
1665 offsetof(struct ieee80211_mgmt, u.beacon.variable);
1666
1667 /* find the location of the ssid in the beacon */
1668 while (ptr < beacon->data + beacon->len) {
1669 if (ptr[0] == WLAN_EID_SSID) {
1670 wl->ssid_len = ptr[1];
1671 memcpy(wl->ssid, ptr+2, wl->ssid_len);
1672 return;
1673 }
1674 ptr += ptr[1];
1675 }
1676 wl1271_error("ad-hoc beacon template has no SSID!\n");
1677}
1678
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001679static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
1680 struct ieee80211_vif *vif,
1681 struct ieee80211_bss_conf *bss_conf,
1682 u32 changed)
1683{
1684 enum wl1271_cmd_ps_mode mode;
1685 struct wl1271 *wl = hw->priv;
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001686 bool do_join = false;
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001687 bool set_assoc = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001688 int ret;
1689
1690 wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed");
1691
1692 mutex_lock(&wl->mutex);
1693
1694 ret = wl1271_ps_elp_wakeup(wl, false);
1695 if (ret < 0)
1696 goto out;
1697
Eliad Peller9ee82d52010-09-19 18:55:09 +02001698 if ((changed & BSS_CHANGED_BEACON_INT) &&
Juuso Oikarinen60e84c22010-03-26 12:53:25 +02001699 (wl->bss_type == BSS_TYPE_IBSS)) {
1700 wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon interval updated: %d",
1701 bss_conf->beacon_int);
1702
1703 wl->beacon_int = bss_conf->beacon_int;
1704 do_join = true;
1705 }
1706
Eliad Peller9ee82d52010-09-19 18:55:09 +02001707 if ((changed & BSS_CHANGED_BEACON) &&
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001708 (wl->bss_type == BSS_TYPE_IBSS)) {
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001709 struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
1710
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001711 wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon updated");
1712
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001713 if (beacon) {
1714 struct ieee80211_hdr *hdr;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001715
1716 wl1271_ssid_set(wl, beacon);
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001717 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON,
1718 beacon->data,
Juuso Oikarinen606c1482010-04-01 11:38:21 +03001719 beacon->len, 0,
1720 wl1271_min_rate_get(wl));
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001721
1722 if (ret < 0) {
1723 dev_kfree_skb(beacon);
1724 goto out_sleep;
1725 }
1726
1727 hdr = (struct ieee80211_hdr *) beacon->data;
1728 hdr->frame_control = cpu_to_le16(
1729 IEEE80211_FTYPE_MGMT |
1730 IEEE80211_STYPE_PROBE_RESP);
1731
1732 ret = wl1271_cmd_template_set(wl,
1733 CMD_TEMPL_PROBE_RESPONSE,
1734 beacon->data,
Juuso Oikarinen606c1482010-04-01 11:38:21 +03001735 beacon->len, 0,
1736 wl1271_min_rate_get(wl));
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001737 dev_kfree_skb(beacon);
1738 if (ret < 0)
1739 goto out_sleep;
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001740
1741 /* Need to update the SSID (for filtering etc) */
1742 do_join = true;
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001743 }
1744 }
1745
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001746 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1747 (wl->bss_type == BSS_TYPE_IBSS)) {
1748 wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
1749 bss_conf->enable_beacon ? "enabled" : "disabled");
1750
1751 if (bss_conf->enable_beacon)
1752 wl->set_bss_type = BSS_TYPE_IBSS;
1753 else
1754 wl->set_bss_type = BSS_TYPE_STA_BSS;
1755 do_join = true;
1756 }
1757
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03001758 if (changed & BSS_CHANGED_CQM) {
1759 bool enable = false;
1760 if (bss_conf->cqm_rssi_thold)
1761 enable = true;
1762 ret = wl1271_acx_rssi_snr_trigger(wl, enable,
1763 bss_conf->cqm_rssi_thold,
1764 bss_conf->cqm_rssi_hyst);
1765 if (ret < 0)
1766 goto out;
1767 wl->rssi_thold = bss_conf->cqm_rssi_thold;
1768 }
1769
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001770 if ((changed & BSS_CHANGED_BSSID) &&
1771 /*
1772 * Now we know the correct bssid, so we send a new join command
1773 * and enable the BSSID filter
1774 */
1775 memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001776 memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
Juuso Oikarinena0cb7be2010-03-18 12:26:44 +02001777
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001778 ret = wl1271_cmd_build_null_data(wl);
Juuso Oikarinena0cb7be2010-03-18 12:26:44 +02001779 if (ret < 0)
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001780 goto out_sleep;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001781
Saravanan Dhanabal141418c2010-04-28 09:50:00 +03001782 ret = wl1271_build_qos_null_data(wl);
1783 if (ret < 0)
1784 goto out_sleep;
1785
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001786 /* filter out all packets not from this BSSID */
1787 wl1271_configure_filters(wl, 0);
1788
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001789 /* Need to update the BSSID (for filtering etc) */
1790 do_join = true;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001791 }
1792
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001793 if (changed & BSS_CHANGED_ASSOC) {
1794 if (bss_conf->assoc) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001795 u32 rates;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001796 wl->aid = bss_conf->aid;
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001797 set_assoc = true;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001798
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001799 wl->ps_poll_failures = 0;
1800
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001801 /*
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001802 * use basic rates from AP, and determine lowest rate
1803 * to use with control frames.
1804 */
1805 rates = bss_conf->basic_rates;
1806 wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
1807 rates);
1808 wl->basic_rate = wl1271_min_rate_get(wl);
1809 ret = wl1271_acx_rate_policies(wl);
1810 if (ret < 0)
1811 goto out_sleep;
1812
1813 /*
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001814 * with wl1271, we don't need to update the
1815 * beacon_int and dtim_period, because the firmware
1816 * updates it by itself when the first beacon is
1817 * received after a join.
1818 */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001819 ret = wl1271_cmd_build_ps_poll(wl, wl->aid);
1820 if (ret < 0)
1821 goto out_sleep;
1822
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02001823 /*
1824 * The SSID is intentionally set to NULL here - the
1825 * firmware will set the probe request with a
1826 * broadcast SSID regardless of what we set in the
1827 * template.
1828 */
1829 ret = wl1271_cmd_build_probe_req(wl, NULL, 0,
1830 NULL, 0, wl->band);
1831
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02001832 /* enable the connection monitoring feature */
1833 ret = wl1271_acx_conn_monit_params(wl, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001834 if (ret < 0)
1835 goto out_sleep;
1836
1837 /* If we want to go in PSM but we're not there yet */
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001838 if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) &&
1839 !test_bit(WL1271_FLAG_PSM, &wl->flags)) {
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001840 mode = STATION_POWER_SAVE_MODE;
Juuso Oikarinen65cddbf12010-08-24 06:28:03 +03001841 ret = wl1271_ps_set_mode(wl, mode,
1842 wl->basic_rate_set,
1843 true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001844 if (ret < 0)
1845 goto out_sleep;
1846 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001847 } else {
1848 /* use defaults when not associated */
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03001849 clear_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags);
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001850 clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001851 wl->aid = 0;
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02001852
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001853 /* re-enable dynamic ps - just in case */
Juuso Oikarinenf532be62010-07-08 17:50:05 +03001854 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001855
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001856 /* revert back to minimum rates for the current band */
1857 wl1271_set_band_rate(wl);
1858 wl->basic_rate = wl1271_min_rate_get(wl);
1859 ret = wl1271_acx_rate_policies(wl);
1860 if (ret < 0)
1861 goto out_sleep;
1862
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02001863 /* disable connection monitor features */
1864 ret = wl1271_acx_conn_monit_params(wl, false);
Juuso Oikarinenc1899552010-03-26 12:53:32 +02001865
1866 /* Disable the keep-alive feature */
1867 ret = wl1271_acx_keep_alive_mode(wl, false);
1868
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02001869 if (ret < 0)
1870 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001871 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001872
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001873 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001874
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001875 if (changed & BSS_CHANGED_ERP_SLOT) {
1876 if (bss_conf->use_short_slot)
1877 ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
1878 else
1879 ret = wl1271_acx_slot(wl, SLOT_TIME_LONG);
1880 if (ret < 0) {
1881 wl1271_warning("Set slot time failed %d", ret);
1882 goto out_sleep;
1883 }
1884 }
1885
1886 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
1887 if (bss_conf->use_short_preamble)
1888 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT);
1889 else
1890 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG);
1891 }
1892
1893 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
1894 if (bss_conf->use_cts_prot)
1895 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE);
1896 else
1897 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE);
1898 if (ret < 0) {
1899 wl1271_warning("Set ctsprotect failed %d", ret);
1900 goto out_sleep;
1901 }
1902 }
1903
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03001904 if (changed & BSS_CHANGED_ARP_FILTER) {
1905 __be32 addr = bss_conf->arp_addr_list[0];
1906 WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
1907
1908 if (bss_conf->arp_addr_cnt == 1 && bss_conf->arp_filter_enabled)
1909 ret = wl1271_acx_arp_ip_filter(wl, true, addr);
1910 else
1911 ret = wl1271_acx_arp_ip_filter(wl, false, addr);
1912
1913 if (ret < 0)
1914 goto out_sleep;
1915 }
1916
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001917 if (do_join) {
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001918 ret = wl1271_join(wl, set_assoc);
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001919 if (ret < 0) {
1920 wl1271_warning("cmd join failed %d", ret);
1921 goto out_sleep;
1922 }
Juuso Oikarinenc1899552010-03-26 12:53:32 +02001923 }
1924
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001925out_sleep:
1926 wl1271_ps_elp_sleep(wl);
1927
1928out:
1929 mutex_unlock(&wl->mutex);
1930}
1931
Kalle Valoc6999d82010-02-18 13:25:41 +02001932static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
1933 const struct ieee80211_tx_queue_params *params)
1934{
1935 struct wl1271 *wl = hw->priv;
Kalle Valo4695dc92010-03-18 12:26:38 +02001936 u8 ps_scheme;
Kalle Valoc6999d82010-02-18 13:25:41 +02001937 int ret;
1938
1939 mutex_lock(&wl->mutex);
1940
1941 wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
1942
Kalle Valoc82c1dd2010-02-18 13:25:47 +02001943 ret = wl1271_ps_elp_wakeup(wl, false);
1944 if (ret < 0)
1945 goto out;
1946
Juuso Oikarinenb43316d2010-03-18 12:26:26 +02001947 /* the txop is confed in units of 32us by the mac80211, we need us */
Kalle Valoc6999d82010-02-18 13:25:41 +02001948 ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
1949 params->cw_min, params->cw_max,
Juuso Oikarinenb43316d2010-03-18 12:26:26 +02001950 params->aifs, params->txop << 5);
Kalle Valoc6999d82010-02-18 13:25:41 +02001951 if (ret < 0)
Kalle Valoc82c1dd2010-02-18 13:25:47 +02001952 goto out_sleep;
Kalle Valoc6999d82010-02-18 13:25:41 +02001953
Kalle Valo4695dc92010-03-18 12:26:38 +02001954 if (params->uapsd)
1955 ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
1956 else
1957 ps_scheme = CONF_PS_SCHEME_LEGACY;
1958
Kalle Valoc6999d82010-02-18 13:25:41 +02001959 ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
1960 CONF_CHANNEL_TYPE_EDCF,
1961 wl1271_tx_get_queue(queue),
Kalle Valo4695dc92010-03-18 12:26:38 +02001962 ps_scheme, CONF_ACK_POLICY_LEGACY, 0, 0);
Kalle Valoc6999d82010-02-18 13:25:41 +02001963 if (ret < 0)
Kalle Valoc82c1dd2010-02-18 13:25:47 +02001964 goto out_sleep;
1965
1966out_sleep:
1967 wl1271_ps_elp_sleep(wl);
Kalle Valoc6999d82010-02-18 13:25:41 +02001968
1969out:
1970 mutex_unlock(&wl->mutex);
1971
1972 return ret;
1973}
1974
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03001975static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw)
1976{
1977
1978 struct wl1271 *wl = hw->priv;
1979 u64 mactime = ULLONG_MAX;
1980 int ret;
1981
1982 wl1271_debug(DEBUG_MAC80211, "mac80211 get tsf");
1983
1984 mutex_lock(&wl->mutex);
1985
1986 ret = wl1271_ps_elp_wakeup(wl, false);
1987 if (ret < 0)
1988 goto out;
1989
1990 ret = wl1271_acx_tsf_info(wl, &mactime);
1991 if (ret < 0)
1992 goto out_sleep;
1993
1994out_sleep:
1995 wl1271_ps_elp_sleep(wl);
1996
1997out:
1998 mutex_unlock(&wl->mutex);
1999 return mactime;
2000}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002001
John W. Linvilleece550d2010-07-28 16:41:06 -04002002static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
2003 struct survey_info *survey)
2004{
2005 struct wl1271 *wl = hw->priv;
2006 struct ieee80211_conf *conf = &hw->conf;
2007
2008 if (idx != 0)
2009 return -ENOENT;
2010
2011 survey->channel = conf->channel;
2012 survey->filled = SURVEY_INFO_NOISE_DBM;
2013 survey->noise = wl->noise;
2014
2015 return 0;
2016}
2017
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002018/* can't be const, mac80211 writes to this */
2019static struct ieee80211_rate wl1271_rates[] = {
2020 { .bitrate = 10,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002021 .hw_value = CONF_HW_BIT_RATE_1MBPS,
2022 .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002023 { .bitrate = 20,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002024 .hw_value = CONF_HW_BIT_RATE_2MBPS,
2025 .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002026 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2027 { .bitrate = 55,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002028 .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
2029 .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002030 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2031 { .bitrate = 110,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002032 .hw_value = CONF_HW_BIT_RATE_11MBPS,
2033 .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002034 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2035 { .bitrate = 60,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002036 .hw_value = CONF_HW_BIT_RATE_6MBPS,
2037 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002038 { .bitrate = 90,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002039 .hw_value = CONF_HW_BIT_RATE_9MBPS,
2040 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002041 { .bitrate = 120,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002042 .hw_value = CONF_HW_BIT_RATE_12MBPS,
2043 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002044 { .bitrate = 180,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002045 .hw_value = CONF_HW_BIT_RATE_18MBPS,
2046 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002047 { .bitrate = 240,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002048 .hw_value = CONF_HW_BIT_RATE_24MBPS,
2049 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002050 { .bitrate = 360,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002051 .hw_value = CONF_HW_BIT_RATE_36MBPS,
2052 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002053 { .bitrate = 480,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002054 .hw_value = CONF_HW_BIT_RATE_48MBPS,
2055 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002056 { .bitrate = 540,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002057 .hw_value = CONF_HW_BIT_RATE_54MBPS,
2058 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002059};
2060
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002061/*
2062 * Can't be const, mac80211 writes to this. The order of the channels here
2063 * is designed to improve scanning.
2064 */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002065static struct ieee80211_channel wl1271_channels[] = {
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02002066 { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02002067 { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02002068 { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02002069 { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002070 { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
2071 { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
2072 { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
2073 { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
2074 { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
2075 { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
2076 { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
2077 { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
2078 { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002079};
2080
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002081/* mapping to indexes for wl1271_rates */
Tobias Klausera0ea9492010-05-20 10:38:11 +02002082static const u8 wl1271_rate_to_idx_2ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002083 /* MCS rates are used only with 11n */
2084 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS7 */
2085 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS6 */
2086 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS5 */
2087 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS4 */
2088 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS3 */
2089 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS2 */
2090 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS1 */
2091 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS0 */
2092
2093 11, /* CONF_HW_RXTX_RATE_54 */
2094 10, /* CONF_HW_RXTX_RATE_48 */
2095 9, /* CONF_HW_RXTX_RATE_36 */
2096 8, /* CONF_HW_RXTX_RATE_24 */
2097
2098 /* TI-specific rate */
2099 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
2100
2101 7, /* CONF_HW_RXTX_RATE_18 */
2102 6, /* CONF_HW_RXTX_RATE_12 */
2103 3, /* CONF_HW_RXTX_RATE_11 */
2104 5, /* CONF_HW_RXTX_RATE_9 */
2105 4, /* CONF_HW_RXTX_RATE_6 */
2106 2, /* CONF_HW_RXTX_RATE_5_5 */
2107 1, /* CONF_HW_RXTX_RATE_2 */
2108 0 /* CONF_HW_RXTX_RATE_1 */
2109};
2110
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002111/* can't be const, mac80211 writes to this */
2112static struct ieee80211_supported_band wl1271_band_2ghz = {
2113 .channels = wl1271_channels,
2114 .n_channels = ARRAY_SIZE(wl1271_channels),
2115 .bitrates = wl1271_rates,
2116 .n_bitrates = ARRAY_SIZE(wl1271_rates),
2117};
2118
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002119/* 5 GHz data rates for WL1273 */
2120static struct ieee80211_rate wl1271_rates_5ghz[] = {
2121 { .bitrate = 60,
2122 .hw_value = CONF_HW_BIT_RATE_6MBPS,
2123 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
2124 { .bitrate = 90,
2125 .hw_value = CONF_HW_BIT_RATE_9MBPS,
2126 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
2127 { .bitrate = 120,
2128 .hw_value = CONF_HW_BIT_RATE_12MBPS,
2129 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
2130 { .bitrate = 180,
2131 .hw_value = CONF_HW_BIT_RATE_18MBPS,
2132 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
2133 { .bitrate = 240,
2134 .hw_value = CONF_HW_BIT_RATE_24MBPS,
2135 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
2136 { .bitrate = 360,
2137 .hw_value = CONF_HW_BIT_RATE_36MBPS,
2138 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
2139 { .bitrate = 480,
2140 .hw_value = CONF_HW_BIT_RATE_48MBPS,
2141 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
2142 { .bitrate = 540,
2143 .hw_value = CONF_HW_BIT_RATE_54MBPS,
2144 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
2145};
2146
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002147/*
2148 * 5 GHz band channels for WL1273 - can't be const, mac80211 writes to this.
2149 * The order of the channels here is designed to improve scanning.
2150 */
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002151static struct ieee80211_channel wl1271_channels_5ghz[] = {
2152 { .hw_value = 183, .center_freq = 4915},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002153 { .hw_value = 188, .center_freq = 4940},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002154 { .hw_value = 8, .center_freq = 5040},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002155 { .hw_value = 34, .center_freq = 5170},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002156 { .hw_value = 44, .center_freq = 5220},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002157 { .hw_value = 60, .center_freq = 5300},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002158 { .hw_value = 112, .center_freq = 5560},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002159 { .hw_value = 132, .center_freq = 5660},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002160 { .hw_value = 157, .center_freq = 5785},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002161 { .hw_value = 184, .center_freq = 4920},
2162 { .hw_value = 189, .center_freq = 4945},
2163 { .hw_value = 9, .center_freq = 5045},
2164 { .hw_value = 36, .center_freq = 5180},
2165 { .hw_value = 46, .center_freq = 5230},
2166 { .hw_value = 64, .center_freq = 5320},
2167 { .hw_value = 116, .center_freq = 5580},
2168 { .hw_value = 136, .center_freq = 5680},
2169 { .hw_value = 192, .center_freq = 4960},
2170 { .hw_value = 11, .center_freq = 5055},
2171 { .hw_value = 38, .center_freq = 5190},
2172 { .hw_value = 48, .center_freq = 5240},
2173 { .hw_value = 100, .center_freq = 5500},
2174 { .hw_value = 120, .center_freq = 5600},
2175 { .hw_value = 140, .center_freq = 5700},
2176 { .hw_value = 185, .center_freq = 4925},
2177 { .hw_value = 196, .center_freq = 4980},
2178 { .hw_value = 12, .center_freq = 5060},
2179 { .hw_value = 40, .center_freq = 5200},
2180 { .hw_value = 52, .center_freq = 5260},
2181 { .hw_value = 104, .center_freq = 5520},
2182 { .hw_value = 124, .center_freq = 5620},
2183 { .hw_value = 149, .center_freq = 5745},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002184 { .hw_value = 161, .center_freq = 5805},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002185 { .hw_value = 187, .center_freq = 4935},
2186 { .hw_value = 7, .center_freq = 5035},
2187 { .hw_value = 16, .center_freq = 5080},
2188 { .hw_value = 42, .center_freq = 5210},
2189 { .hw_value = 56, .center_freq = 5280},
2190 { .hw_value = 108, .center_freq = 5540},
2191 { .hw_value = 128, .center_freq = 5640},
2192 { .hw_value = 153, .center_freq = 5765},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002193 { .hw_value = 165, .center_freq = 5825},
2194};
2195
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002196/* mapping to indexes for wl1271_rates_5ghz */
Tobias Klausera0ea9492010-05-20 10:38:11 +02002197static const u8 wl1271_rate_to_idx_5ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002198 /* MCS rates are used only with 11n */
2199 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS7 */
2200 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS6 */
2201 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS5 */
2202 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS4 */
2203 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS3 */
2204 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS2 */
2205 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS1 */
2206 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS0 */
2207
2208 7, /* CONF_HW_RXTX_RATE_54 */
2209 6, /* CONF_HW_RXTX_RATE_48 */
2210 5, /* CONF_HW_RXTX_RATE_36 */
2211 4, /* CONF_HW_RXTX_RATE_24 */
2212
2213 /* TI-specific rate */
2214 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
2215
2216 3, /* CONF_HW_RXTX_RATE_18 */
2217 2, /* CONF_HW_RXTX_RATE_12 */
2218 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_11 */
2219 1, /* CONF_HW_RXTX_RATE_9 */
2220 0, /* CONF_HW_RXTX_RATE_6 */
2221 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_5_5 */
2222 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_2 */
2223 CONF_HW_RXTX_RATE_UNSUPPORTED /* CONF_HW_RXTX_RATE_1 */
2224};
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002225
2226static struct ieee80211_supported_band wl1271_band_5ghz = {
2227 .channels = wl1271_channels_5ghz,
2228 .n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
2229 .bitrates = wl1271_rates_5ghz,
2230 .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
2231};
2232
Tobias Klausera0ea9492010-05-20 10:38:11 +02002233static const u8 *wl1271_band_rate_to_idx[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002234 [IEEE80211_BAND_2GHZ] = wl1271_rate_to_idx_2ghz,
2235 [IEEE80211_BAND_5GHZ] = wl1271_rate_to_idx_5ghz
2236};
2237
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002238static const struct ieee80211_ops wl1271_ops = {
2239 .start = wl1271_op_start,
2240 .stop = wl1271_op_stop,
2241 .add_interface = wl1271_op_add_interface,
2242 .remove_interface = wl1271_op_remove_interface,
2243 .config = wl1271_op_config,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002244 .prepare_multicast = wl1271_op_prepare_multicast,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002245 .configure_filter = wl1271_op_configure_filter,
2246 .tx = wl1271_op_tx,
2247 .set_key = wl1271_op_set_key,
2248 .hw_scan = wl1271_op_hw_scan,
2249 .bss_info_changed = wl1271_op_bss_info_changed,
2250 .set_rts_threshold = wl1271_op_set_rts_threshold,
Kalle Valoc6999d82010-02-18 13:25:41 +02002251 .conf_tx = wl1271_op_conf_tx,
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03002252 .get_tsf = wl1271_op_get_tsf,
John W. Linvilleece550d2010-07-28 16:41:06 -04002253 .get_survey = wl1271_op_get_survey,
Kalle Valoc8c90872010-02-18 13:25:53 +02002254 CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002255};
2256
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002257
2258u8 wl1271_rate_to_idx(struct wl1271 *wl, int rate)
2259{
2260 u8 idx;
2261
2262 BUG_ON(wl->band >= sizeof(wl1271_band_rate_to_idx)/sizeof(u8 *));
2263
2264 if (unlikely(rate >= CONF_HW_RXTX_RATE_MAX)) {
2265 wl1271_error("Illegal RX rate from HW: %d", rate);
2266 return 0;
2267 }
2268
2269 idx = wl1271_band_rate_to_idx[wl->band][rate];
2270 if (unlikely(idx == CONF_HW_RXTX_RATE_UNSUPPORTED)) {
2271 wl1271_error("Unsupported RX rate from HW: %d", rate);
2272 return 0;
2273 }
2274
2275 return idx;
2276}
2277
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002278static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
2279 struct device_attribute *attr,
2280 char *buf)
2281{
2282 struct wl1271 *wl = dev_get_drvdata(dev);
2283 ssize_t len;
2284
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02002285 len = PAGE_SIZE;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002286
2287 mutex_lock(&wl->mutex);
2288 len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
2289 wl->sg_enabled);
2290 mutex_unlock(&wl->mutex);
2291
2292 return len;
2293
2294}
2295
2296static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
2297 struct device_attribute *attr,
2298 const char *buf, size_t count)
2299{
2300 struct wl1271 *wl = dev_get_drvdata(dev);
2301 unsigned long res;
2302 int ret;
2303
2304 ret = strict_strtoul(buf, 10, &res);
2305
2306 if (ret < 0) {
2307 wl1271_warning("incorrect value written to bt_coex_mode");
2308 return count;
2309 }
2310
2311 mutex_lock(&wl->mutex);
2312
2313 res = !!res;
2314
2315 if (res == wl->sg_enabled)
2316 goto out;
2317
2318 wl->sg_enabled = res;
2319
2320 if (wl->state == WL1271_STATE_OFF)
2321 goto out;
2322
2323 ret = wl1271_ps_elp_wakeup(wl, false);
2324 if (ret < 0)
2325 goto out;
2326
2327 wl1271_acx_sg_enable(wl, wl->sg_enabled);
2328 wl1271_ps_elp_sleep(wl);
2329
2330 out:
2331 mutex_unlock(&wl->mutex);
2332 return count;
2333}
2334
2335static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
2336 wl1271_sysfs_show_bt_coex_state,
2337 wl1271_sysfs_store_bt_coex_state);
2338
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002339static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
2340 struct device_attribute *attr,
2341 char *buf)
2342{
2343 struct wl1271 *wl = dev_get_drvdata(dev);
2344 ssize_t len;
2345
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02002346 len = PAGE_SIZE;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002347
2348 mutex_lock(&wl->mutex);
2349 if (wl->hw_pg_ver >= 0)
2350 len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
2351 else
2352 len = snprintf(buf, len, "n/a\n");
2353 mutex_unlock(&wl->mutex);
2354
2355 return len;
2356}
2357
2358static DEVICE_ATTR(hw_pg_ver, S_IRUGO | S_IWUSR,
2359 wl1271_sysfs_show_hw_pg_ver, NULL);
2360
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002361int wl1271_register_hw(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002362{
2363 int ret;
2364
2365 if (wl->mac80211_registered)
2366 return 0;
2367
2368 SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
2369
2370 ret = ieee80211_register_hw(wl->hw);
2371 if (ret < 0) {
2372 wl1271_error("unable to register mac80211 hw: %d", ret);
2373 return ret;
2374 }
2375
2376 wl->mac80211_registered = true;
2377
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03002378 register_netdevice_notifier(&wl1271_dev_notifier);
2379
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002380 wl1271_notice("loaded");
2381
2382 return 0;
2383}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002384EXPORT_SYMBOL_GPL(wl1271_register_hw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002385
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002386void wl1271_unregister_hw(struct wl1271 *wl)
2387{
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03002388 unregister_netdevice_notifier(&wl1271_dev_notifier);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002389 ieee80211_unregister_hw(wl->hw);
2390 wl->mac80211_registered = false;
2391
2392}
2393EXPORT_SYMBOL_GPL(wl1271_unregister_hw);
2394
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002395int wl1271_init_ieee80211(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002396{
Juuso Oikarinen1e2b7972009-10-08 21:56:20 +03002397 /* The tx descriptor buffer and the TKIP space. */
2398 wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
2399 sizeof(struct wl1271_tx_hw_descr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002400
2401 /* unit us */
2402 /* FIXME: find a proper value */
2403 wl->hw->channel_change_time = 10000;
Juuso Oikarinen50c500a2010-04-01 11:38:22 +03002404 wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002405
2406 wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
Juuso Oikarinen03442a32009-11-23 23:22:14 +02002407 IEEE80211_HW_BEACON_FILTER |
Juuso Oikarinen0a343322010-02-22 08:38:41 +02002408 IEEE80211_HW_SUPPORTS_PS |
Kalle Valo4695dc92010-03-18 12:26:38 +02002409 IEEE80211_HW_SUPPORTS_UAPSD |
Juuso Oikarinena9af0922010-03-26 12:53:30 +02002410 IEEE80211_HW_HAS_RATE_CONTROL |
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03002411 IEEE80211_HW_CONNECTION_MONITOR |
2412 IEEE80211_HW_SUPPORTS_CQM_RSSI;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002413
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02002414 wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
2415 BIT(NL80211_IFTYPE_ADHOC);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002416 wl->hw->wiphy->max_scan_ssids = 1;
2417 wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz;
Juuso Oikarinen11eb5422010-08-24 06:28:03 +03002418 wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz;
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002419
Kalle Valo12bd8942010-03-18 12:26:33 +02002420 wl->hw->queues = 4;
Juuso Oikarinen31627dc2010-03-26 12:53:12 +02002421 wl->hw->max_rates = 1;
Kalle Valo12bd8942010-03-18 12:26:33 +02002422
Teemu Paasikivi8197b712010-02-22 08:38:23 +02002423 SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002424
2425 return 0;
2426}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002427EXPORT_SYMBOL_GPL(wl1271_init_ieee80211);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002428
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002429#define WL1271_DEFAULT_CHANNEL 0
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002430
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002431struct ieee80211_hw *wl1271_alloc_hw(void)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002432{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002433 struct ieee80211_hw *hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002434 struct platform_device *plat_dev = NULL;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002435 struct wl1271 *wl;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002436 int i, ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002437
2438 hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
2439 if (!hw) {
2440 wl1271_error("could not alloc ieee80211_hw");
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002441 ret = -ENOMEM;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002442 goto err_hw_alloc;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002443 }
2444
Julia Lawall929ebd32010-05-15 23:16:39 +02002445 plat_dev = kmemdup(&wl1271_device, sizeof(wl1271_device), GFP_KERNEL);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002446 if (!plat_dev) {
2447 wl1271_error("could not allocate platform_device");
2448 ret = -ENOMEM;
2449 goto err_plat_alloc;
2450 }
2451
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002452 wl = hw->priv;
2453 memset(wl, 0, sizeof(*wl));
2454
Juuso Oikarinen01c09162009-10-13 12:47:55 +03002455 INIT_LIST_HEAD(&wl->list);
2456
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002457 wl->hw = hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002458 wl->plat_dev = plat_dev;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002459
2460 skb_queue_head_init(&wl->tx_queue);
2461
Juuso Oikarinen37b70a82009-10-08 21:56:21 +03002462 INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03002463 INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002464 wl->channel = WL1271_DEFAULT_CHANNEL;
Juuso Oikarinen60e84c22010-03-26 12:53:25 +02002465 wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002466 wl->default_key = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002467 wl->rx_counter = 0;
2468 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
2469 wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02002470 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002471 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
Juuso Oikarinena6fe2312009-12-11 15:40:58 +02002472 wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002473 wl->basic_rate = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02002474 wl->rate_set = CONF_TX_RATE_MASK_BASIC;
2475 wl->sta_rate_set = 0;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03002476 wl->band = IEEE80211_BAND_2GHZ;
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03002477 wl->vif = NULL;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02002478 wl->flags = 0;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002479 wl->sg_enabled = true;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002480 wl->hw_pg_ver = -1;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002481
Juuso Oikarinenbe7078c2009-10-08 21:56:26 +03002482 for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002483 wl->tx_frames[i] = NULL;
2484
2485 spin_lock_init(&wl->wl_lock);
2486
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002487 wl->state = WL1271_STATE_OFF;
2488 mutex_init(&wl->mutex);
2489
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002490 /* Apply default driver configuration. */
2491 wl1271_conf_init(wl);
2492
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002493 wl1271_debugfs_init(wl);
2494
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002495 /* Register platform device */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002496 ret = platform_device_register(wl->plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002497 if (ret) {
2498 wl1271_error("couldn't register platform device");
2499 goto err_hw;
2500 }
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002501 dev_set_drvdata(&wl->plat_dev->dev, wl);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002502
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002503 /* Create sysfs file to control bt coex state */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002504 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002505 if (ret < 0) {
2506 wl1271_error("failed to create sysfs file bt_coex_state");
2507 goto err_platform;
2508 }
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002509
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002510 /* Create sysfs file to get HW PG version */
2511 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
2512 if (ret < 0) {
2513 wl1271_error("failed to create sysfs file hw_pg_ver");
2514 goto err_bt_coex_state;
2515 }
2516
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002517 return hw;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002518
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002519err_bt_coex_state:
2520 device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
2521
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002522err_platform:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002523 platform_device_unregister(wl->plat_dev);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002524
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002525err_hw:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002526 wl1271_debugfs_exit(wl);
2527 kfree(plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002528
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002529err_plat_alloc:
2530 ieee80211_free_hw(hw);
2531
2532err_hw_alloc:
2533
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002534 return ERR_PTR(ret);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002535}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002536EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002537
2538int wl1271_free_hw(struct wl1271 *wl)
2539{
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002540 platform_device_unregister(wl->plat_dev);
2541 kfree(wl->plat_dev);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002542
2543 wl1271_debugfs_exit(wl);
2544
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002545 vfree(wl->fw);
2546 wl->fw = NULL;
2547 kfree(wl->nvs);
2548 wl->nvs = NULL;
2549
2550 kfree(wl->fw_status);
2551 kfree(wl->tx_res_if);
2552
2553 ieee80211_free_hw(wl->hw);
2554
2555 return 0;
2556}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002557EXPORT_SYMBOL_GPL(wl1271_free_hw);
2558
2559MODULE_LICENSE("GPL");
2560MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
2561MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");