blob: b7e9c69f3772cfa535dff8d046e4b7759b6804fd [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 Oikarinen8eab7b42010-09-24 03:10:11 +0200217 .psm_entry_retries = 5,
218 .psm_entry_nullfunc_retries = 3,
219 .psm_entry_hangover_period = 1,
Juuso Oikarinen50c500a2010-04-01 11:38:22 +0300220 .keep_alive_interval = 55000,
221 .max_listen_interval = 20,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300222 },
Luciano Coelho6e92b412009-12-11 15:40:50 +0200223 .itrim = {
224 .enable = false,
225 .timeout = 50000,
Juuso Oikarinen38ad2d82009-12-11 15:41:08 +0200226 },
227 .pm_config = {
228 .host_clk_settling_time = 5000,
229 .host_fast_wakeup_support = false
Juuso Oikarinen00236aed2010-04-09 11:07:30 +0300230 },
231 .roam_trigger = {
Juuso Oikarinen00236aed2010-04-09 11:07:30 +0300232 .trigger_pacing = 1,
233 .avg_weight_rssi_beacon = 20,
234 .avg_weight_rssi_data = 10,
235 .avg_weight_snr_beacon = 20,
236 .avg_weight_snr_data = 10
Juuso Oikarinenbea39d62010-09-21 08:14:31 +0200237 },
238 .scan = {
239 .min_dwell_time_active = 7500,
240 .max_dwell_time_active = 30000,
241 .min_dwell_time_passive = 30000,
242 .max_dwell_time_passive = 60000,
243 .num_probe_reqs = 2,
244 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300245};
246
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200247static void __wl1271_op_remove_interface(struct wl1271 *wl);
248
249
Juuso Oikarinena1dd8182010-03-18 12:26:31 +0200250static void wl1271_device_release(struct device *dev)
251{
252
253}
254
255static struct platform_device wl1271_device = {
256 .name = "wl1271",
257 .id = -1,
258
259 /* device model insists to have a release function */
260 .dev = {
261 .release = wl1271_device_release,
262 },
263};
264
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300265static LIST_HEAD(wl_list);
266
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300267static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
268 void *arg)
269{
270 struct net_device *dev = arg;
271 struct wireless_dev *wdev;
272 struct wiphy *wiphy;
273 struct ieee80211_hw *hw;
274 struct wl1271 *wl;
275 struct wl1271 *wl_temp;
276 int ret = 0;
277
278 /* Check that this notification is for us. */
279 if (what != NETDEV_CHANGE)
280 return NOTIFY_DONE;
281
282 wdev = dev->ieee80211_ptr;
283 if (wdev == NULL)
284 return NOTIFY_DONE;
285
286 wiphy = wdev->wiphy;
287 if (wiphy == NULL)
288 return NOTIFY_DONE;
289
290 hw = wiphy_priv(wiphy);
291 if (hw == NULL)
292 return NOTIFY_DONE;
293
294 wl_temp = hw->priv;
295 list_for_each_entry(wl, &wl_list, list) {
296 if (wl == wl_temp)
297 break;
298 }
299 if (wl != wl_temp)
300 return NOTIFY_DONE;
301
302 mutex_lock(&wl->mutex);
303
304 if (wl->state == WL1271_STATE_OFF)
305 goto out;
306
307 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
308 goto out;
309
310 ret = wl1271_ps_elp_wakeup(wl, false);
311 if (ret < 0)
312 goto out;
313
314 if ((dev->operstate == IF_OPER_UP) &&
315 !test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags)) {
316 wl1271_cmd_set_sta_state(wl);
317 wl1271_info("Association completed.");
318 }
319
320 wl1271_ps_elp_sleep(wl);
321
322out:
323 mutex_unlock(&wl->mutex);
324
325 return NOTIFY_OK;
326}
327
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300328static void wl1271_conf_init(struct wl1271 *wl)
329{
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300330
331 /*
332 * This function applies the default configuration to the driver. This
333 * function is invoked upon driver load (spi probe.)
334 *
335 * The configuration is stored in a run-time structure in order to
336 * facilitate for run-time adjustment of any of the parameters. Making
337 * changes to the configuration structure will apply the new values on
338 * the next interface up (wl1271_op_start.)
339 */
340
341 /* apply driver default configuration */
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300342 memcpy(&wl->conf, &default_conf, sizeof(default_conf));
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300343}
344
345
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300346static int wl1271_plt_init(struct wl1271 *wl)
347{
Luciano Coelho12419cc2010-02-18 13:25:44 +0200348 struct conf_tx_ac_category *conf_ac;
349 struct conf_tx_tid *conf_tid;
350 int ret, i;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300351
Luciano Coelho98b5dd52009-11-23 23:22:17 +0200352 ret = wl1271_cmd_general_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200353 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200354 return ret;
355
Luciano Coelho98b5dd52009-11-23 23:22:17 +0200356 ret = wl1271_cmd_radio_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200357 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200358 return ret;
359
Luciano Coelho12419cc2010-02-18 13:25:44 +0200360 ret = wl1271_init_templates_config(wl);
361 if (ret < 0)
362 return ret;
363
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300364 ret = wl1271_acx_init_mem_config(wl);
365 if (ret < 0)
366 return ret;
367
Luciano Coelho12419cc2010-02-18 13:25:44 +0200368 /* PHY layer config */
369 ret = wl1271_init_phy_config(wl);
370 if (ret < 0)
371 goto out_free_memmap;
372
373 ret = wl1271_acx_dco_itrim_params(wl);
374 if (ret < 0)
375 goto out_free_memmap;
376
377 /* Initialize connection monitoring thresholds */
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +0200378 ret = wl1271_acx_conn_monit_params(wl, false);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200379 if (ret < 0)
380 goto out_free_memmap;
381
382 /* Bluetooth WLAN coexistence */
383 ret = wl1271_init_pta(wl);
384 if (ret < 0)
385 goto out_free_memmap;
386
387 /* Energy detection */
388 ret = wl1271_init_energy_detection(wl);
389 if (ret < 0)
390 goto out_free_memmap;
391
392 /* Default fragmentation threshold */
393 ret = wl1271_acx_frag_threshold(wl);
394 if (ret < 0)
395 goto out_free_memmap;
396
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200397 /* Default TID/AC configuration */
398 BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200399 for (i = 0; i < wl->conf.tx.tid_conf_count; i++) {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200400 conf_ac = &wl->conf.tx.ac_conf[i];
401 ret = wl1271_acx_ac_cfg(wl, conf_ac->ac, conf_ac->cw_min,
402 conf_ac->cw_max, conf_ac->aifsn,
403 conf_ac->tx_op_limit);
404 if (ret < 0)
405 goto out_free_memmap;
406
Luciano Coelho12419cc2010-02-18 13:25:44 +0200407 conf_tid = &wl->conf.tx.tid_conf[i];
408 ret = wl1271_acx_tid_cfg(wl, conf_tid->queue_id,
409 conf_tid->channel_type,
410 conf_tid->tsid,
411 conf_tid->ps_scheme,
412 conf_tid->ack_policy,
413 conf_tid->apsd_conf[0],
414 conf_tid->apsd_conf[1]);
415 if (ret < 0)
416 goto out_free_memmap;
417 }
418
Luciano Coelho12419cc2010-02-18 13:25:44 +0200419 /* Enable data path */
Luciano Coelho94210892009-12-11 15:40:55 +0200420 ret = wl1271_cmd_data_path(wl, 1);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300421 if (ret < 0)
Luciano Coelho12419cc2010-02-18 13:25:44 +0200422 goto out_free_memmap;
423
424 /* Configure for CAM power saving (ie. always active) */
425 ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
426 if (ret < 0)
427 goto out_free_memmap;
428
429 /* configure PM */
430 ret = wl1271_acx_pm_config(wl);
431 if (ret < 0)
432 goto out_free_memmap;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300433
434 return 0;
Luciano Coelho12419cc2010-02-18 13:25:44 +0200435
436 out_free_memmap:
437 kfree(wl->target_mem_map);
438 wl->target_mem_map = NULL;
439
440 return ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300441}
442
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300443static void wl1271_fw_status(struct wl1271 *wl,
444 struct wl1271_fw_status *status)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300445{
Juuso Oikarinenac5e1e32010-02-22 08:38:38 +0200446 struct timespec ts;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300447 u32 total = 0;
448 int i;
449
Teemu Paasikivi09a9c2b2010-02-22 08:38:28 +0200450 wl1271_raw_read(wl, FW_STATUS_ADDR, status, sizeof(*status), false);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300451
452 wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
453 "drv_rx_counter = %d, tx_results_counter = %d)",
454 status->intr,
455 status->fw_rx_counter,
456 status->drv_rx_counter,
457 status->tx_results_counter);
458
459 /* update number of available TX blocks */
460 for (i = 0; i < NUM_TX_QUEUES; i++) {
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300461 u32 cnt = le32_to_cpu(status->tx_released_blks[i]) -
462 wl->tx_blocks_freed[i];
463
464 wl->tx_blocks_freed[i] =
465 le32_to_cpu(status->tx_released_blks[i]);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300466 wl->tx_blocks_available += cnt;
467 total += cnt;
468 }
469
470 /* if more blocks are available now, schedule some tx work */
471 if (total && !skb_queue_empty(&wl->tx_queue))
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300472 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300473
474 /* update the host-chipset time offset */
Juuso Oikarinenac5e1e32010-02-22 08:38:38 +0200475 getnstimeofday(&ts);
476 wl->time_offset = (timespec_to_ns(&ts) >> 10) -
477 (s64)le32_to_cpu(status->fw_localtime);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300478}
479
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200480#define WL1271_IRQ_MAX_LOOPS 10
481
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300482static void wl1271_irq_work(struct work_struct *work)
483{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300484 int ret;
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300485 u32 intr;
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200486 int loopcount = WL1271_IRQ_MAX_LOOPS;
487 unsigned long flags;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300488 struct wl1271 *wl =
489 container_of(work, struct wl1271, irq_work);
490
491 mutex_lock(&wl->mutex);
492
493 wl1271_debug(DEBUG_IRQ, "IRQ work");
494
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200495 if (unlikely(wl->state == WL1271_STATE_OFF))
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300496 goto out;
497
498 ret = wl1271_ps_elp_wakeup(wl, true);
499 if (ret < 0)
500 goto out;
501
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200502 spin_lock_irqsave(&wl->wl_lock, flags);
503 while (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags) && loopcount) {
504 clear_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags);
505 spin_unlock_irqrestore(&wl->wl_lock, flags);
506 loopcount--;
507
508 wl1271_fw_status(wl, wl->fw_status);
509 intr = le32_to_cpu(wl->fw_status->intr);
510 if (!intr) {
511 wl1271_debug(DEBUG_IRQ, "Zero interrupt received.");
Dan Carpentercdd08642010-05-08 18:25:17 +0200512 spin_lock_irqsave(&wl->wl_lock, flags);
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200513 continue;
514 }
515
516 intr &= WL1271_INTR_MASK;
517
518 if (intr & WL1271_ACX_INTR_DATA) {
519 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
520
521 /* check for tx results */
522 if (wl->fw_status->tx_results_counter !=
523 (wl->tx_results_count & 0xff))
524 wl1271_tx_complete(wl);
525
526 wl1271_rx(wl, wl->fw_status);
527 }
528
529 if (intr & WL1271_ACX_INTR_EVENT_A) {
530 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
531 wl1271_event_handle(wl, 0);
532 }
533
534 if (intr & WL1271_ACX_INTR_EVENT_B) {
535 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
536 wl1271_event_handle(wl, 1);
537 }
538
539 if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
540 wl1271_debug(DEBUG_IRQ,
541 "WL1271_ACX_INTR_INIT_COMPLETE");
542
543 if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
544 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
545
546 spin_lock_irqsave(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300547 }
548
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200549 if (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags))
550 ieee80211_queue_work(wl->hw, &wl->irq_work);
551 else
552 clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
553 spin_unlock_irqrestore(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300554
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300555 wl1271_ps_elp_sleep(wl);
556
557out:
558 mutex_unlock(&wl->mutex);
559}
560
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300561static int wl1271_fetch_firmware(struct wl1271 *wl)
562{
563 const struct firmware *fw;
564 int ret;
565
Teemu Paasikivi8197b712010-02-22 08:38:23 +0200566 ret = request_firmware(&fw, WL1271_FW_NAME, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300567
568 if (ret < 0) {
569 wl1271_error("could not get firmware: %d", ret);
570 return ret;
571 }
572
573 if (fw->size % 4) {
574 wl1271_error("firmware size is not multiple of 32 bits: %zu",
575 fw->size);
576 ret = -EILSEQ;
577 goto out;
578 }
579
580 wl->fw_len = fw->size;
Juuso Oikarinen1fba4972009-10-08 21:56:32 +0300581 wl->fw = vmalloc(wl->fw_len);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300582
583 if (!wl->fw) {
584 wl1271_error("could not allocate memory for the firmware");
585 ret = -ENOMEM;
586 goto out;
587 }
588
589 memcpy(wl->fw, fw->data, wl->fw_len);
590
591 ret = 0;
592
593out:
594 release_firmware(fw);
595
596 return ret;
597}
598
599static int wl1271_fetch_nvs(struct wl1271 *wl)
600{
601 const struct firmware *fw;
602 int ret;
603
Teemu Paasikivi8197b712010-02-22 08:38:23 +0200604 ret = request_firmware(&fw, WL1271_NVS_NAME, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300605
606 if (ret < 0) {
607 wl1271_error("could not get nvs file: %d", ret);
608 return ret;
609 }
610
Julia Lawall929ebd32010-05-15 23:16:39 +0200611 wl->nvs = kmemdup(fw->data, sizeof(struct wl1271_nvs_file), GFP_KERNEL);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300612
613 if (!wl->nvs) {
614 wl1271_error("could not allocate memory for the nvs file");
615 ret = -ENOMEM;
616 goto out;
617 }
618
Juuso Oikarinen02fabb02010-08-19 04:41:15 +0200619 wl->nvs_len = fw->size;
620
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300621out:
622 release_firmware(fw);
623
624 return ret;
625}
626
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200627static void wl1271_recovery_work(struct work_struct *work)
628{
629 struct wl1271 *wl =
630 container_of(work, struct wl1271, recovery_work);
631
632 mutex_lock(&wl->mutex);
633
634 if (wl->state != WL1271_STATE_ON)
635 goto out;
636
637 wl1271_info("Hardware recovery in progress.");
638
639 /* reboot the chipset */
640 __wl1271_op_remove_interface(wl);
641 ieee80211_restart_hw(wl->hw);
642
643out:
644 mutex_unlock(&wl->mutex);
645}
646
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300647static void wl1271_fw_wakeup(struct wl1271 *wl)
648{
649 u32 elp_reg;
650
651 elp_reg = ELPCTRL_WAKE_UP;
Juuso Oikarinen74621412009-10-12 15:08:54 +0300652 wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300653}
654
655static int wl1271_setup(struct wl1271 *wl)
656{
657 wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL);
658 if (!wl->fw_status)
659 return -ENOMEM;
660
661 wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
662 if (!wl->tx_res_if) {
663 kfree(wl->fw_status);
664 return -ENOMEM;
665 }
666
667 INIT_WORK(&wl->irq_work, wl1271_irq_work);
668 INIT_WORK(&wl->tx_work, wl1271_tx_work);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200669 INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
Juuso Oikarinen78abd322010-09-21 06:23:32 +0200670 INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
Juuso Oikarinenc454f1d2010-08-24 06:28:03 +0300671
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300672 return 0;
673}
674
675static int wl1271_chip_wakeup(struct wl1271 *wl)
676{
Juuso Oikarinen451de972009-10-12 15:08:46 +0300677 struct wl1271_partition_set partition;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300678 int ret = 0;
679
Juuso Oikarinen01ac17e2009-12-11 15:41:02 +0200680 msleep(WL1271_PRE_POWER_ON_SLEEP);
Ohad Ben-Cohen2cc78ff2010-09-16 01:22:04 +0200681 ret = wl1271_power_on(wl);
682 if (ret < 0)
683 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300684 msleep(WL1271_POWER_ON_SLEEP);
Teemu Paasikivi9b280722010-02-18 13:25:56 +0200685 wl1271_io_reset(wl);
686 wl1271_io_init(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300687
688 /* We don't need a real memory partition here, because we only want
689 * to use the registers at this point. */
Juuso Oikarinen451de972009-10-12 15:08:46 +0300690 memset(&partition, 0, sizeof(partition));
691 partition.reg.start = REGISTERS_BASE;
692 partition.reg.size = REGISTERS_DOWN_SIZE;
693 wl1271_set_partition(wl, &partition);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300694
695 /* ELP module wake up */
696 wl1271_fw_wakeup(wl);
697
698 /* whal_FwCtrl_BootSm() */
699
700 /* 0. read chip id from CHIP_ID */
Teemu Paasikivi7b048c52010-02-18 13:25:55 +0200701 wl->chip.id = wl1271_read32(wl, CHIP_ID_B);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300702
703 /* 1. check if chip id is valid */
704
705 switch (wl->chip.id) {
706 case CHIP_ID_1271_PG10:
707 wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
708 wl->chip.id);
709
710 ret = wl1271_setup(wl);
711 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200712 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300713 break;
714 case CHIP_ID_1271_PG20:
715 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
716 wl->chip.id);
717
718 ret = wl1271_setup(wl);
719 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200720 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300721 break;
722 default:
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200723 wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300724 ret = -ENODEV;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200725 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300726 }
727
728 if (wl->fw == NULL) {
729 ret = wl1271_fetch_firmware(wl);
730 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200731 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300732 }
733
734 /* No NVS from netlink, try to get it from the filesystem */
735 if (wl->nvs == NULL) {
736 ret = wl1271_fetch_nvs(wl);
737 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200738 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300739 }
740
741out:
742 return ret;
743}
744
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300745int wl1271_plt_start(struct wl1271 *wl)
746{
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200747 int retries = WL1271_BOOT_RETRIES;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300748 int ret;
749
750 mutex_lock(&wl->mutex);
751
752 wl1271_notice("power up");
753
754 if (wl->state != WL1271_STATE_OFF) {
755 wl1271_error("cannot go into PLT state because not "
756 "in off state: %d", wl->state);
757 ret = -EBUSY;
758 goto out;
759 }
760
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200761 while (retries) {
762 retries--;
763 ret = wl1271_chip_wakeup(wl);
764 if (ret < 0)
765 goto power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300766
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200767 ret = wl1271_boot(wl);
768 if (ret < 0)
769 goto power_off;
770
771 ret = wl1271_plt_init(wl);
772 if (ret < 0)
773 goto irq_disable;
774
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200775 wl->state = WL1271_STATE_PLT;
776 wl1271_notice("firmware booted in PLT mode (%s)",
777 wl->chip.fw_ver);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300778 goto out;
779
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200780irq_disable:
781 wl1271_disable_interrupts(wl);
782 mutex_unlock(&wl->mutex);
783 /* Unlocking the mutex in the middle of handling is
784 inherently unsafe. In this case we deem it safe to do,
785 because we need to let any possibly pending IRQ out of
786 the system (and while we are WL1271_STATE_OFF the IRQ
787 work function will not do anything.) Also, any other
788 possible concurrent operations will fail due to the
789 current state, hence the wl1271 struct should be safe. */
790 cancel_work_sync(&wl->irq_work);
791 mutex_lock(&wl->mutex);
792power_off:
793 wl1271_power_off(wl);
794 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300795
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200796 wl1271_error("firmware boot in PLT mode failed despite %d retries",
797 WL1271_BOOT_RETRIES);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300798out:
799 mutex_unlock(&wl->mutex);
800
801 return ret;
802}
803
804int wl1271_plt_stop(struct wl1271 *wl)
805{
806 int ret = 0;
807
808 mutex_lock(&wl->mutex);
809
810 wl1271_notice("power down");
811
812 if (wl->state != WL1271_STATE_PLT) {
813 wl1271_error("cannot power down because not in PLT "
814 "state: %d", wl->state);
815 ret = -EBUSY;
816 goto out;
817 }
818
819 wl1271_disable_interrupts(wl);
820 wl1271_power_off(wl);
821
822 wl->state = WL1271_STATE_OFF;
Luciano Coelhobd5ea182009-10-13 12:47:58 +0300823 wl->rx_counter = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300824
825out:
826 mutex_unlock(&wl->mutex);
827
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +0200828 cancel_work_sync(&wl->irq_work);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200829 cancel_work_sync(&wl->recovery_work);
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +0200830
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300831 return ret;
832}
833
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300834static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
835{
836 struct wl1271 *wl = hw->priv;
Juuso Oikarinen830fb672009-12-11 15:41:06 +0200837 struct ieee80211_conf *conf = &hw->conf;
838 struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
839 struct ieee80211_sta *sta = txinfo->control.sta;
840 unsigned long flags;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300841
Juuso Oikarinen830fb672009-12-11 15:41:06 +0200842 /* peek into the rates configured in the STA entry */
843 spin_lock_irqsave(&wl->wl_lock, flags);
844 if (sta && sta->supp_rates[conf->channel->band] != wl->sta_rate_set) {
845 wl->sta_rate_set = sta->supp_rates[conf->channel->band];
846 set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags);
847 }
848 spin_unlock_irqrestore(&wl->wl_lock, flags);
849
850 /* queue the packet */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300851 skb_queue_tail(&wl->tx_queue, skb);
852
853 /*
854 * The chip specific setup must run before the first TX packet -
855 * before that, the tx_work will not be initialized!
856 */
857
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300858 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300859
860 /*
861 * The workqueue is slow to process the tx_queue and we need stop
862 * the queue here, otherwise the queue will get too long.
863 */
Juuso Oikarinen06f7bc72010-02-22 08:38:33 +0200864 if (skb_queue_len(&wl->tx_queue) >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
865 wl1271_debug(DEBUG_TX, "op_tx: stopping queues");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300866
Juuso Oikarinen06f7bc72010-02-22 08:38:33 +0200867 spin_lock_irqsave(&wl->wl_lock, flags);
868 ieee80211_stop_queues(wl->hw);
Juuso Oikarinen71449f82009-12-11 15:41:07 +0200869 set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
Juuso Oikarinen06f7bc72010-02-22 08:38:33 +0200870 spin_unlock_irqrestore(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300871 }
872
873 return NETDEV_TX_OK;
874}
875
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300876static struct notifier_block wl1271_dev_notifier = {
877 .notifier_call = wl1271_dev_notify,
878};
879
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300880static int wl1271_op_start(struct ieee80211_hw *hw)
881{
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200882 wl1271_debug(DEBUG_MAC80211, "mac80211 start");
883
884 /*
885 * We have to delay the booting of the hardware because
886 * we need to know the local MAC address before downloading and
887 * initializing the firmware. The MAC address cannot be changed
888 * after boot, and without the proper MAC address, the firmware
889 * will not function properly.
890 *
891 * The MAC address is first known when the corresponding interface
892 * is added. That is where we will initialize the hardware.
893 */
894
895 return 0;
896}
897
898static void wl1271_op_stop(struct ieee80211_hw *hw)
899{
900 wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
901}
902
903static int wl1271_op_add_interface(struct ieee80211_hw *hw,
904 struct ieee80211_vif *vif)
905{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300906 struct wl1271 *wl = hw->priv;
John W. Linvilleac01e942010-07-28 17:09:41 -0400907 struct wiphy *wiphy = hw->wiphy;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200908 int retries = WL1271_BOOT_RETRIES;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300909 int ret = 0;
910
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200911 wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
912 vif->type, vif->addr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300913
914 mutex_lock(&wl->mutex);
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200915 if (wl->vif) {
916 ret = -EBUSY;
917 goto out;
918 }
919
920 wl->vif = vif;
921
922 switch (vif->type) {
923 case NL80211_IFTYPE_STATION:
924 wl->bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +0200925 wl->set_bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200926 break;
927 case NL80211_IFTYPE_ADHOC:
928 wl->bss_type = BSS_TYPE_IBSS;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +0200929 wl->set_bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200930 break;
931 default:
932 ret = -EOPNOTSUPP;
933 goto out;
934 }
935
936 memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300937
938 if (wl->state != WL1271_STATE_OFF) {
939 wl1271_error("cannot start because not in off state: %d",
940 wl->state);
941 ret = -EBUSY;
942 goto out;
943 }
944
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200945 while (retries) {
946 retries--;
947 ret = wl1271_chip_wakeup(wl);
948 if (ret < 0)
949 goto power_off;
950
951 ret = wl1271_boot(wl);
952 if (ret < 0)
953 goto power_off;
954
955 ret = wl1271_hw_init(wl);
956 if (ret < 0)
957 goto irq_disable;
958
959 wl->state = WL1271_STATE_ON;
960 wl1271_info("firmware booted (%s)", wl->chip.fw_ver);
John W. Linvilleac01e942010-07-28 17:09:41 -0400961
962 /* update hw/fw version info in wiphy struct */
963 wiphy->hw_version = wl->chip.id;
964 strncpy(wiphy->fw_version, wl->chip.fw_ver,
965 sizeof(wiphy->fw_version));
966
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300967 goto out;
968
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200969irq_disable:
970 wl1271_disable_interrupts(wl);
971 mutex_unlock(&wl->mutex);
972 /* Unlocking the mutex in the middle of handling is
973 inherently unsafe. In this case we deem it safe to do,
974 because we need to let any possibly pending IRQ out of
975 the system (and while we are WL1271_STATE_OFF the IRQ
976 work function will not do anything.) Also, any other
977 possible concurrent operations will fail due to the
978 current state, hence the wl1271 struct should be safe. */
979 cancel_work_sync(&wl->irq_work);
980 mutex_lock(&wl->mutex);
981power_off:
982 wl1271_power_off(wl);
983 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300984
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200985 wl1271_error("firmware boot failed despite %d retries",
986 WL1271_BOOT_RETRIES);
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300987out:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300988 mutex_unlock(&wl->mutex);
989
Juuso Oikarineneb887df2010-07-08 17:49:58 +0300990 if (!ret)
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300991 list_add(&wl->list, &wl_list);
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300992
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300993 return ret;
994}
995
Juuso Oikarinen52a2a372010-09-21 06:23:30 +0200996static void __wl1271_op_remove_interface(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300997{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300998 int i;
999
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001000 wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001001
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001002 wl1271_info("down");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001003
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001004 list_del(&wl->list);
1005
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001006 WARN_ON(wl->state != WL1271_STATE_ON);
1007
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001008 /* enable dyn ps just in case (if left on due to fw crash etc) */
Juuso Oikarinen9a547bf2010-07-08 17:50:04 +03001009 if (wl->bss_type == BSS_TYPE_STA_BSS)
Juuso Oikarinenf532be62010-07-08 17:50:05 +03001010 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001011
Luciano Coelho08688d62010-07-08 17:50:07 +03001012 if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
Luciano Coelho08688d62010-07-08 17:50:07 +03001013 wl->scan.state = WL1271_SCAN_STATE_IDLE;
1014 kfree(wl->scan.scanned_ch);
1015 wl->scan.scanned_ch = NULL;
Juuso Oikarinen76a029f2010-07-29 04:54:45 +03001016 ieee80211_scan_completed(wl->hw, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001017 }
1018
1019 wl->state = WL1271_STATE_OFF;
1020
1021 wl1271_disable_interrupts(wl);
1022
1023 mutex_unlock(&wl->mutex);
1024
Juuso Oikarinen78abd322010-09-21 06:23:32 +02001025 cancel_delayed_work_sync(&wl->scan_complete_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001026 cancel_work_sync(&wl->irq_work);
1027 cancel_work_sync(&wl->tx_work);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001028 cancel_delayed_work_sync(&wl->pspoll_work);
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +02001029 cancel_delayed_work_sync(&wl->elp_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001030
1031 mutex_lock(&wl->mutex);
1032
1033 /* let's notify MAC80211 about the remaining pending TX frames */
Juuso Oikarinen781608c2010-05-24 11:18:17 +03001034 wl1271_tx_reset(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001035 wl1271_power_off(wl);
1036
1037 memset(wl->bssid, 0, ETH_ALEN);
1038 memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1);
1039 wl->ssid_len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001040 wl->bss_type = MAX_BSS_TYPE;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001041 wl->set_bss_type = MAX_BSS_TYPE;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001042 wl->band = IEEE80211_BAND_2GHZ;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001043
1044 wl->rx_counter = 0;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02001045 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001046 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
1047 wl->tx_blocks_available = 0;
1048 wl->tx_results_count = 0;
1049 wl->tx_packets_count = 0;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001050 wl->tx_security_last_seq = 0;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001051 wl->tx_security_seq = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001052 wl->time_offset = 0;
1053 wl->session_counter = 0;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001054 wl->rate_set = CONF_TX_RATE_MASK_BASIC;
1055 wl->sta_rate_set = 0;
1056 wl->flags = 0;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001057 wl->vif = NULL;
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001058 wl->filters = 0;
Luciano Coelhod6e19d12009-10-12 15:08:43 +03001059
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001060 for (i = 0; i < NUM_TX_QUEUES; i++)
1061 wl->tx_blocks_freed[i] = 0;
1062
1063 wl1271_debugfs_reset(wl);
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03001064
1065 kfree(wl->fw_status);
1066 wl->fw_status = NULL;
1067 kfree(wl->tx_res_if);
1068 wl->tx_res_if = NULL;
1069 kfree(wl->target_mem_map);
1070 wl->target_mem_map = NULL;
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001071}
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03001072
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001073static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
1074 struct ieee80211_vif *vif)
1075{
1076 struct wl1271 *wl = hw->priv;
1077
1078 mutex_lock(&wl->mutex);
1079 WARN_ON(wl->vif != vif);
1080 __wl1271_op_remove_interface(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001081 mutex_unlock(&wl->mutex);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001082
1083 cancel_work_sync(&wl->recovery_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001084}
1085
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001086static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
1087{
1088 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
1089 wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
1090
1091 /* combine requested filters with current filter config */
1092 filters = wl->filters | filters;
1093
1094 wl1271_debug(DEBUG_FILTERS, "RX filters set: ");
1095
1096 if (filters & FIF_PROMISC_IN_BSS) {
1097 wl1271_debug(DEBUG_FILTERS, " - FIF_PROMISC_IN_BSS");
1098 wl->rx_config &= ~CFG_UNI_FILTER_EN;
1099 wl->rx_config |= CFG_BSSID_FILTER_EN;
1100 }
1101 if (filters & FIF_BCN_PRBRESP_PROMISC) {
1102 wl1271_debug(DEBUG_FILTERS, " - FIF_BCN_PRBRESP_PROMISC");
1103 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1104 wl->rx_config &= ~CFG_SSID_FILTER_EN;
1105 }
1106 if (filters & FIF_OTHER_BSS) {
1107 wl1271_debug(DEBUG_FILTERS, " - FIF_OTHER_BSS");
1108 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1109 }
1110 if (filters & FIF_CONTROL) {
1111 wl1271_debug(DEBUG_FILTERS, " - FIF_CONTROL");
1112 wl->rx_filter |= CFG_RX_CTL_EN;
1113 }
1114 if (filters & FIF_FCSFAIL) {
1115 wl1271_debug(DEBUG_FILTERS, " - FIF_FCSFAIL");
1116 wl->rx_filter |= CFG_RX_FCS_ERROR;
1117 }
1118}
1119
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001120static int wl1271_dummy_join(struct wl1271 *wl)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001121{
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001122 int ret = 0;
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001123 /* we need to use a dummy BSSID for now */
1124 static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
1125 0xad, 0xbe, 0xef };
1126
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001127 memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
1128
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001129 /* pass through frames from all BSS */
1130 wl1271_configure_filters(wl, FIF_OTHER_BSS);
1131
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001132 ret = wl1271_cmd_join(wl, wl->set_bss_type);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001133 if (ret < 0)
1134 goto out;
1135
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001136 set_bit(WL1271_FLAG_JOINED, &wl->flags);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001137
1138out:
1139 return ret;
1140}
1141
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001142static int wl1271_join(struct wl1271 *wl, bool set_assoc)
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001143{
1144 int ret;
1145
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001146 /*
1147 * One of the side effects of the JOIN command is that is clears
1148 * WPA/WPA2 keys from the chipset. Performing a JOIN while associated
1149 * to a WPA/WPA2 access point will therefore kill the data-path.
1150 * Currently there is no supported scenario for JOIN during
1151 * association - if it becomes a supported scenario, the WPA/WPA2 keys
1152 * must be handled somehow.
1153 *
1154 */
1155 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1156 wl1271_info("JOIN while associated.");
1157
1158 if (set_assoc)
1159 set_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
1160
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001161 ret = wl1271_cmd_join(wl, wl->set_bss_type);
1162 if (ret < 0)
1163 goto out;
1164
1165 set_bit(WL1271_FLAG_JOINED, &wl->flags);
1166
1167 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1168 goto out;
1169
1170 /*
1171 * The join command disable the keep-alive mode, shut down its process,
1172 * and also clear the template config, so we need to reset it all after
1173 * the join. The acx_aid starts the keep-alive process, and the order
1174 * of the commands below is relevant.
1175 */
1176 ret = wl1271_acx_keep_alive_mode(wl, true);
1177 if (ret < 0)
1178 goto out;
1179
1180 ret = wl1271_acx_aid(wl, wl->aid);
1181 if (ret < 0)
1182 goto out;
1183
1184 ret = wl1271_cmd_build_klv_null_data(wl);
1185 if (ret < 0)
1186 goto out;
1187
1188 ret = wl1271_acx_keep_alive_config(wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
1189 ACX_KEEP_ALIVE_TPL_VALID);
1190 if (ret < 0)
1191 goto out;
1192
1193out:
1194 return ret;
1195}
1196
1197static int wl1271_unjoin(struct wl1271 *wl)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001198{
1199 int ret;
1200
1201 /* to stop listening to a channel, we disconnect */
1202 ret = wl1271_cmd_disconnect(wl);
1203 if (ret < 0)
1204 goto out;
1205
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001206 clear_bit(WL1271_FLAG_JOINED, &wl->flags);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001207 memset(wl->bssid, 0, ETH_ALEN);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001208
1209 /* stop filterting packets based on bssid */
1210 wl1271_configure_filters(wl, FIF_OTHER_BSS);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001211
1212out:
1213 return ret;
1214}
1215
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001216static void wl1271_set_band_rate(struct wl1271 *wl)
1217{
1218 if (wl->band == IEEE80211_BAND_2GHZ)
1219 wl->basic_rate_set = wl->conf.tx.basic_rate;
1220 else
1221 wl->basic_rate_set = wl->conf.tx.basic_rate_5;
1222}
1223
1224static u32 wl1271_min_rate_get(struct wl1271 *wl)
1225{
1226 int i;
1227 u32 rate = 0;
1228
1229 if (!wl->basic_rate_set) {
1230 WARN_ON(1);
1231 wl->basic_rate_set = wl->conf.tx.basic_rate;
1232 }
1233
1234 for (i = 0; !rate; i++) {
1235 if ((wl->basic_rate_set >> i) & 0x1)
1236 rate = 1 << i;
1237 }
1238
1239 return rate;
1240}
1241
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03001242static int wl1271_handle_idle(struct wl1271 *wl, bool idle)
1243{
1244 int ret;
1245
1246 if (idle) {
1247 if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
1248 ret = wl1271_unjoin(wl);
1249 if (ret < 0)
1250 goto out;
1251 }
1252 wl->rate_set = wl1271_min_rate_get(wl);
1253 wl->sta_rate_set = 0;
1254 ret = wl1271_acx_rate_policies(wl);
1255 if (ret < 0)
1256 goto out;
1257 ret = wl1271_acx_keep_alive_config(
1258 wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
1259 ACX_KEEP_ALIVE_TPL_INVALID);
1260 if (ret < 0)
1261 goto out;
1262 set_bit(WL1271_FLAG_IDLE, &wl->flags);
1263 } else {
1264 /* increment the session counter */
1265 wl->session_counter++;
1266 if (wl->session_counter >= SESSION_COUNTER_MAX)
1267 wl->session_counter = 0;
1268 ret = wl1271_dummy_join(wl);
1269 if (ret < 0)
1270 goto out;
1271 clear_bit(WL1271_FLAG_IDLE, &wl->flags);
1272 }
1273
1274out:
1275 return ret;
1276}
1277
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001278static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
1279{
1280 struct wl1271 *wl = hw->priv;
1281 struct ieee80211_conf *conf = &hw->conf;
1282 int channel, ret = 0;
1283
1284 channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
1285
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001286 wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s",
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001287 channel,
1288 conf->flags & IEEE80211_CONF_PS ? "on" : "off",
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001289 conf->power_level,
1290 conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001291
Juuso Oikarinen781608c2010-05-24 11:18:17 +03001292 /*
1293 * mac80211 will go to idle nearly immediately after transmitting some
1294 * frames, such as the deauth. To make sure those frames reach the air,
1295 * wait here until the TX queue is fully flushed.
1296 */
1297 if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
1298 (conf->flags & IEEE80211_CONF_IDLE))
1299 wl1271_tx_flush(wl);
1300
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001301 mutex_lock(&wl->mutex);
1302
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001303 if (unlikely(wl->state == WL1271_STATE_OFF))
1304 goto out;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001305
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001306 ret = wl1271_ps_elp_wakeup(wl, false);
1307 if (ret < 0)
1308 goto out;
1309
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001310 /* if the channel changes while joined, join again */
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001311 if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
1312 ((wl->band != conf->channel->band) ||
1313 (wl->channel != channel))) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001314 wl->band = conf->channel->band;
1315 wl->channel = channel;
1316
1317 /*
1318 * FIXME: the mac80211 should really provide a fixed rate
1319 * to use here. for now, just use the smallest possible rate
1320 * for the band as a fixed rate for association frames and
1321 * other control messages.
1322 */
1323 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1324 wl1271_set_band_rate(wl);
1325
1326 wl->basic_rate = wl1271_min_rate_get(wl);
1327 ret = wl1271_acx_rate_policies(wl);
1328 if (ret < 0)
1329 wl1271_warning("rate policy for update channel "
1330 "failed %d", ret);
1331
1332 if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001333 ret = wl1271_join(wl, false);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001334 if (ret < 0)
1335 wl1271_warning("cmd join to update channel "
1336 "failed %d", ret);
1337 }
1338 }
1339
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001340 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03001341 ret = wl1271_handle_idle(wl, conf->flags & IEEE80211_CONF_IDLE);
1342 if (ret < 0)
1343 wl1271_warning("idle mode change failed %d", ret);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001344 }
1345
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001346 /*
1347 * if mac80211 changes the PSM mode, make sure the mode is not
1348 * incorrectly changed after the pspoll failure active window.
1349 */
1350 if (changed & IEEE80211_CONF_CHANGE_PS)
1351 clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
1352
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001353 if (conf->flags & IEEE80211_CONF_PS &&
1354 !test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
1355 set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001356
1357 /*
1358 * We enter PSM only if we're already associated.
1359 * If we're not, we'll enter it when joining an SSID,
1360 * through the bss_info_changed() hook.
1361 */
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001362 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02001363 wl1271_debug(DEBUG_PSM, "psm enabled");
Juuso Oikarinend8c42c02010-02-18 13:25:36 +02001364 ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02001365 wl->basic_rate, true);
Juuso Oikarinenaf5e0842009-12-11 15:41:00 +02001366 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001367 } else if (!(conf->flags & IEEE80211_CONF_PS) &&
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001368 test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02001369 wl1271_debug(DEBUG_PSM, "psm disabled");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001370
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001371 clear_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001372
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001373 if (test_bit(WL1271_FLAG_PSM, &wl->flags))
Juuso Oikarinend8c42c02010-02-18 13:25:36 +02001374 ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02001375 wl->basic_rate, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001376 }
1377
1378 if (conf->power_level != wl->power_level) {
1379 ret = wl1271_acx_tx_power(wl, conf->power_level);
1380 if (ret < 0)
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02001381 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001382
1383 wl->power_level = conf->power_level;
1384 }
1385
1386out_sleep:
1387 wl1271_ps_elp_sleep(wl);
1388
1389out:
1390 mutex_unlock(&wl->mutex);
1391
1392 return ret;
1393}
1394
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001395struct wl1271_filter_params {
1396 bool enabled;
1397 int mc_list_length;
1398 u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
1399};
1400
Jiri Pirko22bedad2010-04-01 21:22:57 +00001401static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw,
1402 struct netdev_hw_addr_list *mc_list)
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001403{
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001404 struct wl1271_filter_params *fp;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001405 struct netdev_hw_addr *ha;
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001406 struct wl1271 *wl = hw->priv;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001407
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001408 if (unlikely(wl->state == WL1271_STATE_OFF))
1409 return 0;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001410
Juuso Oikarinen74441132009-10-13 12:47:53 +03001411 fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001412 if (!fp) {
1413 wl1271_error("Out of memory setting filters.");
1414 return 0;
1415 }
1416
1417 /* update multicast filtering parameters */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001418 fp->mc_list_length = 0;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001419 if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) {
1420 fp->enabled = false;
1421 } else {
1422 fp->enabled = true;
1423 netdev_hw_addr_list_for_each(ha, mc_list) {
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001424 memcpy(fp->mc_list[fp->mc_list_length],
Jiri Pirko22bedad2010-04-01 21:22:57 +00001425 ha->addr, ETH_ALEN);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001426 fp->mc_list_length++;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001427 }
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001428 }
1429
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001430 return (u64)(unsigned long)fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001431}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001432
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001433#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
1434 FIF_ALLMULTI | \
1435 FIF_FCSFAIL | \
1436 FIF_BCN_PRBRESP_PROMISC | \
1437 FIF_CONTROL | \
1438 FIF_OTHER_BSS)
1439
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001440static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
1441 unsigned int changed,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001442 unsigned int *total, u64 multicast)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001443{
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001444 struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001445 struct wl1271 *wl = hw->priv;
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001446 int ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001447
1448 wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter");
1449
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001450 mutex_lock(&wl->mutex);
1451
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001452 *total &= WL1271_SUPPORTED_FILTERS;
1453 changed &= WL1271_SUPPORTED_FILTERS;
1454
1455 if (unlikely(wl->state == WL1271_STATE_OFF))
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001456 goto out;
1457
1458 ret = wl1271_ps_elp_wakeup(wl, false);
1459 if (ret < 0)
1460 goto out;
1461
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001462
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001463 if (*total & FIF_ALLMULTI)
1464 ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
1465 else if (fp)
1466 ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
1467 fp->mc_list,
1468 fp->mc_list_length);
1469 if (ret < 0)
1470 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001471
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001472 /* determine, whether supported filter values have changed */
1473 if (changed == 0)
1474 goto out_sleep;
1475
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001476 /* configure filters */
1477 wl->filters = *total;
1478 wl1271_configure_filters(wl, 0);
1479
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001480 /* apply configured filters */
1481 ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
1482 if (ret < 0)
1483 goto out_sleep;
1484
1485out_sleep:
1486 wl1271_ps_elp_sleep(wl);
1487
1488out:
1489 mutex_unlock(&wl->mutex);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001490 kfree(fp);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001491}
1492
1493static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
1494 struct ieee80211_vif *vif,
1495 struct ieee80211_sta *sta,
1496 struct ieee80211_key_conf *key_conf)
1497{
1498 struct wl1271 *wl = hw->priv;
1499 const u8 *addr;
1500 int ret;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001501 u32 tx_seq_32 = 0;
1502 u16 tx_seq_16 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001503 u8 key_type;
1504
1505 static const u8 bcast_addr[ETH_ALEN] =
1506 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1507
1508 wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
1509
1510 addr = sta ? sta->addr : bcast_addr;
1511
1512 wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd);
1513 wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN);
1514 wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
Johannes Berg97359d12010-08-10 09:46:38 +02001515 key_conf->cipher, key_conf->keyidx,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001516 key_conf->keylen, key_conf->flags);
1517 wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
1518
1519 if (is_zero_ether_addr(addr)) {
1520 /* We dont support TX only encryption */
1521 ret = -EOPNOTSUPP;
1522 goto out;
1523 }
1524
1525 mutex_lock(&wl->mutex);
1526
1527 ret = wl1271_ps_elp_wakeup(wl, false);
1528 if (ret < 0)
1529 goto out_unlock;
1530
Johannes Berg97359d12010-08-10 09:46:38 +02001531 switch (key_conf->cipher) {
1532 case WLAN_CIPHER_SUITE_WEP40:
1533 case WLAN_CIPHER_SUITE_WEP104:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001534 key_type = KEY_WEP;
1535
1536 key_conf->hw_key_idx = key_conf->keyidx;
1537 break;
Johannes Berg97359d12010-08-10 09:46:38 +02001538 case WLAN_CIPHER_SUITE_TKIP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001539 key_type = KEY_TKIP;
1540
1541 key_conf->hw_key_idx = key_conf->keyidx;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001542 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1543 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001544 break;
Johannes Berg97359d12010-08-10 09:46:38 +02001545 case WLAN_CIPHER_SUITE_CCMP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001546 key_type = KEY_AES;
1547
1548 key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001549 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1550 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001551 break;
1552 default:
Johannes Berg97359d12010-08-10 09:46:38 +02001553 wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001554
1555 ret = -EOPNOTSUPP;
1556 goto out_sleep;
1557 }
1558
1559 switch (cmd) {
1560 case SET_KEY:
1561 ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE,
1562 key_conf->keyidx, key_type,
1563 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001564 addr, tx_seq_32, tx_seq_16);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001565 if (ret < 0) {
1566 wl1271_error("Could not add or replace key");
1567 goto out_sleep;
1568 }
Juuso Oikarinenee444cf2010-02-18 13:25:50 +02001569
1570 /* the default WEP key needs to be configured at least once */
1571 if (key_type == KEY_WEP) {
1572 ret = wl1271_cmd_set_default_wep_key(wl,
1573 wl->default_key);
1574 if (ret < 0)
1575 goto out_sleep;
1576 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001577 break;
1578
1579 case DISABLE_KEY:
Juuso Oikarinenfddc7dd2010-02-18 13:25:49 +02001580 /* The wl1271 does not allow to remove unicast keys - they
1581 will be cleared automatically on next CMD_JOIN. Ignore the
1582 request silently, as we dont want the mac80211 to emit
1583 an error message. */
1584 if (!is_broadcast_ether_addr(addr))
1585 break;
1586
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001587 ret = wl1271_cmd_set_key(wl, KEY_REMOVE,
1588 key_conf->keyidx, key_type,
1589 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001590 addr, 0, 0);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001591 if (ret < 0) {
1592 wl1271_error("Could not remove key");
1593 goto out_sleep;
1594 }
1595 break;
1596
1597 default:
1598 wl1271_error("Unsupported key cmd 0x%x", cmd);
1599 ret = -EOPNOTSUPP;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001600 break;
1601 }
1602
1603out_sleep:
1604 wl1271_ps_elp_sleep(wl);
1605
1606out_unlock:
1607 mutex_unlock(&wl->mutex);
1608
1609out:
1610 return ret;
1611}
1612
1613static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
Johannes Berga060bbf2010-04-27 11:59:34 +02001614 struct ieee80211_vif *vif,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001615 struct cfg80211_scan_request *req)
1616{
1617 struct wl1271 *wl = hw->priv;
1618 int ret;
1619 u8 *ssid = NULL;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001620 size_t len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001621
1622 wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
1623
1624 if (req->n_ssids) {
1625 ssid = req->ssids[0].ssid;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001626 len = req->ssids[0].ssid_len;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001627 }
1628
1629 mutex_lock(&wl->mutex);
1630
1631 ret = wl1271_ps_elp_wakeup(wl, false);
1632 if (ret < 0)
1633 goto out;
1634
Luciano Coelho5924f892010-08-04 03:46:22 +03001635 ret = wl1271_scan(hw->priv, ssid, len, req);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001636
1637 wl1271_ps_elp_sleep(wl);
1638
1639out:
1640 mutex_unlock(&wl->mutex);
1641
1642 return ret;
1643}
1644
1645static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1646{
1647 struct wl1271 *wl = hw->priv;
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03001648 int ret = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001649
1650 mutex_lock(&wl->mutex);
1651
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03001652 if (unlikely(wl->state == WL1271_STATE_OFF))
1653 goto out;
1654
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001655 ret = wl1271_ps_elp_wakeup(wl, false);
1656 if (ret < 0)
1657 goto out;
1658
1659 ret = wl1271_acx_rts_threshold(wl, (u16) value);
1660 if (ret < 0)
1661 wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret);
1662
1663 wl1271_ps_elp_sleep(wl);
1664
1665out:
1666 mutex_unlock(&wl->mutex);
1667
1668 return ret;
1669}
1670
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001671static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *beacon)
1672{
1673 u8 *ptr = beacon->data +
1674 offsetof(struct ieee80211_mgmt, u.beacon.variable);
1675
1676 /* find the location of the ssid in the beacon */
1677 while (ptr < beacon->data + beacon->len) {
1678 if (ptr[0] == WLAN_EID_SSID) {
1679 wl->ssid_len = ptr[1];
1680 memcpy(wl->ssid, ptr+2, wl->ssid_len);
1681 return;
1682 }
1683 ptr += ptr[1];
1684 }
1685 wl1271_error("ad-hoc beacon template has no SSID!\n");
1686}
1687
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001688static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
1689 struct ieee80211_vif *vif,
1690 struct ieee80211_bss_conf *bss_conf,
1691 u32 changed)
1692{
1693 enum wl1271_cmd_ps_mode mode;
1694 struct wl1271 *wl = hw->priv;
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001695 bool do_join = false;
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001696 bool set_assoc = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001697 int ret;
1698
1699 wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed");
1700
1701 mutex_lock(&wl->mutex);
1702
1703 ret = wl1271_ps_elp_wakeup(wl, false);
1704 if (ret < 0)
1705 goto out;
1706
Eliad Peller9ee82d52010-09-19 18:55:09 +02001707 if ((changed & BSS_CHANGED_BEACON_INT) &&
Juuso Oikarinen60e84c22010-03-26 12:53:25 +02001708 (wl->bss_type == BSS_TYPE_IBSS)) {
1709 wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon interval updated: %d",
1710 bss_conf->beacon_int);
1711
1712 wl->beacon_int = bss_conf->beacon_int;
1713 do_join = true;
1714 }
1715
Eliad Peller9ee82d52010-09-19 18:55:09 +02001716 if ((changed & BSS_CHANGED_BEACON) &&
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001717 (wl->bss_type == BSS_TYPE_IBSS)) {
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001718 struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
1719
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001720 wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon updated");
1721
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001722 if (beacon) {
1723 struct ieee80211_hdr *hdr;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001724
1725 wl1271_ssid_set(wl, beacon);
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001726 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON,
1727 beacon->data,
Juuso Oikarinen606c1482010-04-01 11:38:21 +03001728 beacon->len, 0,
1729 wl1271_min_rate_get(wl));
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001730
1731 if (ret < 0) {
1732 dev_kfree_skb(beacon);
1733 goto out_sleep;
1734 }
1735
1736 hdr = (struct ieee80211_hdr *) beacon->data;
1737 hdr->frame_control = cpu_to_le16(
1738 IEEE80211_FTYPE_MGMT |
1739 IEEE80211_STYPE_PROBE_RESP);
1740
1741 ret = wl1271_cmd_template_set(wl,
1742 CMD_TEMPL_PROBE_RESPONSE,
1743 beacon->data,
Juuso Oikarinen606c1482010-04-01 11:38:21 +03001744 beacon->len, 0,
1745 wl1271_min_rate_get(wl));
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001746 dev_kfree_skb(beacon);
1747 if (ret < 0)
1748 goto out_sleep;
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001749
1750 /* Need to update the SSID (for filtering etc) */
1751 do_join = true;
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001752 }
1753 }
1754
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001755 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1756 (wl->bss_type == BSS_TYPE_IBSS)) {
1757 wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
1758 bss_conf->enable_beacon ? "enabled" : "disabled");
1759
1760 if (bss_conf->enable_beacon)
1761 wl->set_bss_type = BSS_TYPE_IBSS;
1762 else
1763 wl->set_bss_type = BSS_TYPE_STA_BSS;
1764 do_join = true;
1765 }
1766
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03001767 if (changed & BSS_CHANGED_CQM) {
1768 bool enable = false;
1769 if (bss_conf->cqm_rssi_thold)
1770 enable = true;
1771 ret = wl1271_acx_rssi_snr_trigger(wl, enable,
1772 bss_conf->cqm_rssi_thold,
1773 bss_conf->cqm_rssi_hyst);
1774 if (ret < 0)
1775 goto out;
1776 wl->rssi_thold = bss_conf->cqm_rssi_thold;
1777 }
1778
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001779 if ((changed & BSS_CHANGED_BSSID) &&
1780 /*
1781 * Now we know the correct bssid, so we send a new join command
1782 * and enable the BSSID filter
1783 */
1784 memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001785 memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
Juuso Oikarinena0cb7be2010-03-18 12:26:44 +02001786
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001787 ret = wl1271_cmd_build_null_data(wl);
Juuso Oikarinena0cb7be2010-03-18 12:26:44 +02001788 if (ret < 0)
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001789 goto out_sleep;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001790
Saravanan Dhanabal141418c2010-04-28 09:50:00 +03001791 ret = wl1271_build_qos_null_data(wl);
1792 if (ret < 0)
1793 goto out_sleep;
1794
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001795 /* filter out all packets not from this BSSID */
1796 wl1271_configure_filters(wl, 0);
1797
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001798 /* Need to update the BSSID (for filtering etc) */
1799 do_join = true;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001800 }
1801
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001802 if (changed & BSS_CHANGED_ASSOC) {
1803 if (bss_conf->assoc) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001804 u32 rates;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001805 wl->aid = bss_conf->aid;
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001806 set_assoc = true;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001807
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001808 wl->ps_poll_failures = 0;
1809
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001810 /*
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001811 * use basic rates from AP, and determine lowest rate
1812 * to use with control frames.
1813 */
1814 rates = bss_conf->basic_rates;
1815 wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
1816 rates);
1817 wl->basic_rate = wl1271_min_rate_get(wl);
1818 ret = wl1271_acx_rate_policies(wl);
1819 if (ret < 0)
1820 goto out_sleep;
1821
1822 /*
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001823 * with wl1271, we don't need to update the
1824 * beacon_int and dtim_period, because the firmware
1825 * updates it by itself when the first beacon is
1826 * received after a join.
1827 */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001828 ret = wl1271_cmd_build_ps_poll(wl, wl->aid);
1829 if (ret < 0)
1830 goto out_sleep;
1831
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02001832 /*
1833 * The SSID is intentionally set to NULL here - the
1834 * firmware will set the probe request with a
1835 * broadcast SSID regardless of what we set in the
1836 * template.
1837 */
1838 ret = wl1271_cmd_build_probe_req(wl, NULL, 0,
1839 NULL, 0, wl->band);
1840
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02001841 /* enable the connection monitoring feature */
1842 ret = wl1271_acx_conn_monit_params(wl, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001843 if (ret < 0)
1844 goto out_sleep;
1845
1846 /* If we want to go in PSM but we're not there yet */
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001847 if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) &&
1848 !test_bit(WL1271_FLAG_PSM, &wl->flags)) {
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001849 mode = STATION_POWER_SAVE_MODE;
Juuso Oikarinen65cddbf12010-08-24 06:28:03 +03001850 ret = wl1271_ps_set_mode(wl, mode,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02001851 wl->basic_rate,
Juuso Oikarinen65cddbf12010-08-24 06:28:03 +03001852 true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001853 if (ret < 0)
1854 goto out_sleep;
1855 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001856 } else {
1857 /* use defaults when not associated */
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03001858 clear_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags);
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001859 clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001860 wl->aid = 0;
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02001861
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001862 /* re-enable dynamic ps - just in case */
Juuso Oikarinenf532be62010-07-08 17:50:05 +03001863 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001864
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001865 /* revert back to minimum rates for the current band */
1866 wl1271_set_band_rate(wl);
1867 wl->basic_rate = wl1271_min_rate_get(wl);
1868 ret = wl1271_acx_rate_policies(wl);
1869 if (ret < 0)
1870 goto out_sleep;
1871
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02001872 /* disable connection monitor features */
1873 ret = wl1271_acx_conn_monit_params(wl, false);
Juuso Oikarinenc1899552010-03-26 12:53:32 +02001874
1875 /* Disable the keep-alive feature */
1876 ret = wl1271_acx_keep_alive_mode(wl, false);
1877
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02001878 if (ret < 0)
1879 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001880 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001881
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001882 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001883
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001884 if (changed & BSS_CHANGED_ERP_SLOT) {
1885 if (bss_conf->use_short_slot)
1886 ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
1887 else
1888 ret = wl1271_acx_slot(wl, SLOT_TIME_LONG);
1889 if (ret < 0) {
1890 wl1271_warning("Set slot time failed %d", ret);
1891 goto out_sleep;
1892 }
1893 }
1894
1895 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
1896 if (bss_conf->use_short_preamble)
1897 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT);
1898 else
1899 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG);
1900 }
1901
1902 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
1903 if (bss_conf->use_cts_prot)
1904 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE);
1905 else
1906 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE);
1907 if (ret < 0) {
1908 wl1271_warning("Set ctsprotect failed %d", ret);
1909 goto out_sleep;
1910 }
1911 }
1912
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03001913 if (changed & BSS_CHANGED_ARP_FILTER) {
1914 __be32 addr = bss_conf->arp_addr_list[0];
1915 WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
1916
1917 if (bss_conf->arp_addr_cnt == 1 && bss_conf->arp_filter_enabled)
1918 ret = wl1271_acx_arp_ip_filter(wl, true, addr);
1919 else
1920 ret = wl1271_acx_arp_ip_filter(wl, false, addr);
1921
1922 if (ret < 0)
1923 goto out_sleep;
1924 }
1925
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001926 if (do_join) {
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001927 ret = wl1271_join(wl, set_assoc);
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001928 if (ret < 0) {
1929 wl1271_warning("cmd join failed %d", ret);
1930 goto out_sleep;
1931 }
Juuso Oikarinenc1899552010-03-26 12:53:32 +02001932 }
1933
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001934out_sleep:
1935 wl1271_ps_elp_sleep(wl);
1936
1937out:
1938 mutex_unlock(&wl->mutex);
1939}
1940
Kalle Valoc6999d82010-02-18 13:25:41 +02001941static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
1942 const struct ieee80211_tx_queue_params *params)
1943{
1944 struct wl1271 *wl = hw->priv;
Kalle Valo4695dc92010-03-18 12:26:38 +02001945 u8 ps_scheme;
Kalle Valoc6999d82010-02-18 13:25:41 +02001946 int ret;
1947
1948 mutex_lock(&wl->mutex);
1949
1950 wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
1951
Kalle Valoc82c1dd2010-02-18 13:25:47 +02001952 ret = wl1271_ps_elp_wakeup(wl, false);
1953 if (ret < 0)
1954 goto out;
1955
Juuso Oikarinenb43316d2010-03-18 12:26:26 +02001956 /* the txop is confed in units of 32us by the mac80211, we need us */
Kalle Valoc6999d82010-02-18 13:25:41 +02001957 ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
1958 params->cw_min, params->cw_max,
Juuso Oikarinenb43316d2010-03-18 12:26:26 +02001959 params->aifs, params->txop << 5);
Kalle Valoc6999d82010-02-18 13:25:41 +02001960 if (ret < 0)
Kalle Valoc82c1dd2010-02-18 13:25:47 +02001961 goto out_sleep;
Kalle Valoc6999d82010-02-18 13:25:41 +02001962
Kalle Valo4695dc92010-03-18 12:26:38 +02001963 if (params->uapsd)
1964 ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
1965 else
1966 ps_scheme = CONF_PS_SCHEME_LEGACY;
1967
Kalle Valoc6999d82010-02-18 13:25:41 +02001968 ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
1969 CONF_CHANNEL_TYPE_EDCF,
1970 wl1271_tx_get_queue(queue),
Kalle Valo4695dc92010-03-18 12:26:38 +02001971 ps_scheme, CONF_ACK_POLICY_LEGACY, 0, 0);
Kalle Valoc6999d82010-02-18 13:25:41 +02001972 if (ret < 0)
Kalle Valoc82c1dd2010-02-18 13:25:47 +02001973 goto out_sleep;
1974
1975out_sleep:
1976 wl1271_ps_elp_sleep(wl);
Kalle Valoc6999d82010-02-18 13:25:41 +02001977
1978out:
1979 mutex_unlock(&wl->mutex);
1980
1981 return ret;
1982}
1983
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03001984static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw)
1985{
1986
1987 struct wl1271 *wl = hw->priv;
1988 u64 mactime = ULLONG_MAX;
1989 int ret;
1990
1991 wl1271_debug(DEBUG_MAC80211, "mac80211 get tsf");
1992
1993 mutex_lock(&wl->mutex);
1994
1995 ret = wl1271_ps_elp_wakeup(wl, false);
1996 if (ret < 0)
1997 goto out;
1998
1999 ret = wl1271_acx_tsf_info(wl, &mactime);
2000 if (ret < 0)
2001 goto out_sleep;
2002
2003out_sleep:
2004 wl1271_ps_elp_sleep(wl);
2005
2006out:
2007 mutex_unlock(&wl->mutex);
2008 return mactime;
2009}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002010
John W. Linvilleece550d2010-07-28 16:41:06 -04002011static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
2012 struct survey_info *survey)
2013{
2014 struct wl1271 *wl = hw->priv;
2015 struct ieee80211_conf *conf = &hw->conf;
2016
2017 if (idx != 0)
2018 return -ENOENT;
2019
2020 survey->channel = conf->channel;
2021 survey->filled = SURVEY_INFO_NOISE_DBM;
2022 survey->noise = wl->noise;
2023
2024 return 0;
2025}
2026
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002027/* can't be const, mac80211 writes to this */
2028static struct ieee80211_rate wl1271_rates[] = {
2029 { .bitrate = 10,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002030 .hw_value = CONF_HW_BIT_RATE_1MBPS,
2031 .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002032 { .bitrate = 20,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002033 .hw_value = CONF_HW_BIT_RATE_2MBPS,
2034 .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002035 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2036 { .bitrate = 55,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002037 .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
2038 .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002039 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2040 { .bitrate = 110,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002041 .hw_value = CONF_HW_BIT_RATE_11MBPS,
2042 .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002043 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2044 { .bitrate = 60,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002045 .hw_value = CONF_HW_BIT_RATE_6MBPS,
2046 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002047 { .bitrate = 90,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002048 .hw_value = CONF_HW_BIT_RATE_9MBPS,
2049 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002050 { .bitrate = 120,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002051 .hw_value = CONF_HW_BIT_RATE_12MBPS,
2052 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002053 { .bitrate = 180,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002054 .hw_value = CONF_HW_BIT_RATE_18MBPS,
2055 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002056 { .bitrate = 240,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002057 .hw_value = CONF_HW_BIT_RATE_24MBPS,
2058 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002059 { .bitrate = 360,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002060 .hw_value = CONF_HW_BIT_RATE_36MBPS,
2061 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002062 { .bitrate = 480,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002063 .hw_value = CONF_HW_BIT_RATE_48MBPS,
2064 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002065 { .bitrate = 540,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002066 .hw_value = CONF_HW_BIT_RATE_54MBPS,
2067 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002068};
2069
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002070/*
2071 * Can't be const, mac80211 writes to this. The order of the channels here
2072 * is designed to improve scanning.
2073 */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002074static struct ieee80211_channel wl1271_channels[] = {
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02002075 { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02002076 { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02002077 { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02002078 { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002079 { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
2080 { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
2081 { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
2082 { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
2083 { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
2084 { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
2085 { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
2086 { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
2087 { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002088};
2089
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002090/* mapping to indexes for wl1271_rates */
Tobias Klausera0ea9492010-05-20 10:38:11 +02002091static const u8 wl1271_rate_to_idx_2ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002092 /* MCS rates are used only with 11n */
2093 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS7 */
2094 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS6 */
2095 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS5 */
2096 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS4 */
2097 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS3 */
2098 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS2 */
2099 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS1 */
2100 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS0 */
2101
2102 11, /* CONF_HW_RXTX_RATE_54 */
2103 10, /* CONF_HW_RXTX_RATE_48 */
2104 9, /* CONF_HW_RXTX_RATE_36 */
2105 8, /* CONF_HW_RXTX_RATE_24 */
2106
2107 /* TI-specific rate */
2108 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
2109
2110 7, /* CONF_HW_RXTX_RATE_18 */
2111 6, /* CONF_HW_RXTX_RATE_12 */
2112 3, /* CONF_HW_RXTX_RATE_11 */
2113 5, /* CONF_HW_RXTX_RATE_9 */
2114 4, /* CONF_HW_RXTX_RATE_6 */
2115 2, /* CONF_HW_RXTX_RATE_5_5 */
2116 1, /* CONF_HW_RXTX_RATE_2 */
2117 0 /* CONF_HW_RXTX_RATE_1 */
2118};
2119
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002120/* can't be const, mac80211 writes to this */
2121static struct ieee80211_supported_band wl1271_band_2ghz = {
2122 .channels = wl1271_channels,
2123 .n_channels = ARRAY_SIZE(wl1271_channels),
2124 .bitrates = wl1271_rates,
2125 .n_bitrates = ARRAY_SIZE(wl1271_rates),
2126};
2127
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002128/* 5 GHz data rates for WL1273 */
2129static struct ieee80211_rate wl1271_rates_5ghz[] = {
2130 { .bitrate = 60,
2131 .hw_value = CONF_HW_BIT_RATE_6MBPS,
2132 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
2133 { .bitrate = 90,
2134 .hw_value = CONF_HW_BIT_RATE_9MBPS,
2135 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
2136 { .bitrate = 120,
2137 .hw_value = CONF_HW_BIT_RATE_12MBPS,
2138 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
2139 { .bitrate = 180,
2140 .hw_value = CONF_HW_BIT_RATE_18MBPS,
2141 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
2142 { .bitrate = 240,
2143 .hw_value = CONF_HW_BIT_RATE_24MBPS,
2144 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
2145 { .bitrate = 360,
2146 .hw_value = CONF_HW_BIT_RATE_36MBPS,
2147 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
2148 { .bitrate = 480,
2149 .hw_value = CONF_HW_BIT_RATE_48MBPS,
2150 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
2151 { .bitrate = 540,
2152 .hw_value = CONF_HW_BIT_RATE_54MBPS,
2153 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
2154};
2155
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002156/*
2157 * 5 GHz band channels for WL1273 - can't be const, mac80211 writes to this.
2158 * The order of the channels here is designed to improve scanning.
2159 */
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002160static struct ieee80211_channel wl1271_channels_5ghz[] = {
2161 { .hw_value = 183, .center_freq = 4915},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002162 { .hw_value = 188, .center_freq = 4940},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002163 { .hw_value = 8, .center_freq = 5040},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002164 { .hw_value = 34, .center_freq = 5170},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002165 { .hw_value = 44, .center_freq = 5220},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002166 { .hw_value = 60, .center_freq = 5300},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002167 { .hw_value = 112, .center_freq = 5560},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002168 { .hw_value = 132, .center_freq = 5660},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002169 { .hw_value = 157, .center_freq = 5785},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002170 { .hw_value = 184, .center_freq = 4920},
2171 { .hw_value = 189, .center_freq = 4945},
2172 { .hw_value = 9, .center_freq = 5045},
2173 { .hw_value = 36, .center_freq = 5180},
2174 { .hw_value = 46, .center_freq = 5230},
2175 { .hw_value = 64, .center_freq = 5320},
2176 { .hw_value = 116, .center_freq = 5580},
2177 { .hw_value = 136, .center_freq = 5680},
2178 { .hw_value = 192, .center_freq = 4960},
2179 { .hw_value = 11, .center_freq = 5055},
2180 { .hw_value = 38, .center_freq = 5190},
2181 { .hw_value = 48, .center_freq = 5240},
2182 { .hw_value = 100, .center_freq = 5500},
2183 { .hw_value = 120, .center_freq = 5600},
2184 { .hw_value = 140, .center_freq = 5700},
2185 { .hw_value = 185, .center_freq = 4925},
2186 { .hw_value = 196, .center_freq = 4980},
2187 { .hw_value = 12, .center_freq = 5060},
2188 { .hw_value = 40, .center_freq = 5200},
2189 { .hw_value = 52, .center_freq = 5260},
2190 { .hw_value = 104, .center_freq = 5520},
2191 { .hw_value = 124, .center_freq = 5620},
2192 { .hw_value = 149, .center_freq = 5745},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002193 { .hw_value = 161, .center_freq = 5805},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002194 { .hw_value = 187, .center_freq = 4935},
2195 { .hw_value = 7, .center_freq = 5035},
2196 { .hw_value = 16, .center_freq = 5080},
2197 { .hw_value = 42, .center_freq = 5210},
2198 { .hw_value = 56, .center_freq = 5280},
2199 { .hw_value = 108, .center_freq = 5540},
2200 { .hw_value = 128, .center_freq = 5640},
2201 { .hw_value = 153, .center_freq = 5765},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002202 { .hw_value = 165, .center_freq = 5825},
2203};
2204
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002205/* mapping to indexes for wl1271_rates_5ghz */
Tobias Klausera0ea9492010-05-20 10:38:11 +02002206static const u8 wl1271_rate_to_idx_5ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002207 /* MCS rates are used only with 11n */
2208 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS7 */
2209 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS6 */
2210 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS5 */
2211 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS4 */
2212 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS3 */
2213 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS2 */
2214 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS1 */
2215 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS0 */
2216
2217 7, /* CONF_HW_RXTX_RATE_54 */
2218 6, /* CONF_HW_RXTX_RATE_48 */
2219 5, /* CONF_HW_RXTX_RATE_36 */
2220 4, /* CONF_HW_RXTX_RATE_24 */
2221
2222 /* TI-specific rate */
2223 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
2224
2225 3, /* CONF_HW_RXTX_RATE_18 */
2226 2, /* CONF_HW_RXTX_RATE_12 */
2227 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_11 */
2228 1, /* CONF_HW_RXTX_RATE_9 */
2229 0, /* CONF_HW_RXTX_RATE_6 */
2230 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_5_5 */
2231 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_2 */
2232 CONF_HW_RXTX_RATE_UNSUPPORTED /* CONF_HW_RXTX_RATE_1 */
2233};
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002234
2235static struct ieee80211_supported_band wl1271_band_5ghz = {
2236 .channels = wl1271_channels_5ghz,
2237 .n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
2238 .bitrates = wl1271_rates_5ghz,
2239 .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
2240};
2241
Tobias Klausera0ea9492010-05-20 10:38:11 +02002242static const u8 *wl1271_band_rate_to_idx[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002243 [IEEE80211_BAND_2GHZ] = wl1271_rate_to_idx_2ghz,
2244 [IEEE80211_BAND_5GHZ] = wl1271_rate_to_idx_5ghz
2245};
2246
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002247static const struct ieee80211_ops wl1271_ops = {
2248 .start = wl1271_op_start,
2249 .stop = wl1271_op_stop,
2250 .add_interface = wl1271_op_add_interface,
2251 .remove_interface = wl1271_op_remove_interface,
2252 .config = wl1271_op_config,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002253 .prepare_multicast = wl1271_op_prepare_multicast,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002254 .configure_filter = wl1271_op_configure_filter,
2255 .tx = wl1271_op_tx,
2256 .set_key = wl1271_op_set_key,
2257 .hw_scan = wl1271_op_hw_scan,
2258 .bss_info_changed = wl1271_op_bss_info_changed,
2259 .set_rts_threshold = wl1271_op_set_rts_threshold,
Kalle Valoc6999d82010-02-18 13:25:41 +02002260 .conf_tx = wl1271_op_conf_tx,
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03002261 .get_tsf = wl1271_op_get_tsf,
John W. Linvilleece550d2010-07-28 16:41:06 -04002262 .get_survey = wl1271_op_get_survey,
Kalle Valoc8c90872010-02-18 13:25:53 +02002263 CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002264};
2265
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002266
2267u8 wl1271_rate_to_idx(struct wl1271 *wl, int rate)
2268{
2269 u8 idx;
2270
2271 BUG_ON(wl->band >= sizeof(wl1271_band_rate_to_idx)/sizeof(u8 *));
2272
2273 if (unlikely(rate >= CONF_HW_RXTX_RATE_MAX)) {
2274 wl1271_error("Illegal RX rate from HW: %d", rate);
2275 return 0;
2276 }
2277
2278 idx = wl1271_band_rate_to_idx[wl->band][rate];
2279 if (unlikely(idx == CONF_HW_RXTX_RATE_UNSUPPORTED)) {
2280 wl1271_error("Unsupported RX rate from HW: %d", rate);
2281 return 0;
2282 }
2283
2284 return idx;
2285}
2286
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002287static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
2288 struct device_attribute *attr,
2289 char *buf)
2290{
2291 struct wl1271 *wl = dev_get_drvdata(dev);
2292 ssize_t len;
2293
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02002294 len = PAGE_SIZE;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002295
2296 mutex_lock(&wl->mutex);
2297 len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
2298 wl->sg_enabled);
2299 mutex_unlock(&wl->mutex);
2300
2301 return len;
2302
2303}
2304
2305static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
2306 struct device_attribute *attr,
2307 const char *buf, size_t count)
2308{
2309 struct wl1271 *wl = dev_get_drvdata(dev);
2310 unsigned long res;
2311 int ret;
2312
2313 ret = strict_strtoul(buf, 10, &res);
2314
2315 if (ret < 0) {
2316 wl1271_warning("incorrect value written to bt_coex_mode");
2317 return count;
2318 }
2319
2320 mutex_lock(&wl->mutex);
2321
2322 res = !!res;
2323
2324 if (res == wl->sg_enabled)
2325 goto out;
2326
2327 wl->sg_enabled = res;
2328
2329 if (wl->state == WL1271_STATE_OFF)
2330 goto out;
2331
2332 ret = wl1271_ps_elp_wakeup(wl, false);
2333 if (ret < 0)
2334 goto out;
2335
2336 wl1271_acx_sg_enable(wl, wl->sg_enabled);
2337 wl1271_ps_elp_sleep(wl);
2338
2339 out:
2340 mutex_unlock(&wl->mutex);
2341 return count;
2342}
2343
2344static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
2345 wl1271_sysfs_show_bt_coex_state,
2346 wl1271_sysfs_store_bt_coex_state);
2347
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002348static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
2349 struct device_attribute *attr,
2350 char *buf)
2351{
2352 struct wl1271 *wl = dev_get_drvdata(dev);
2353 ssize_t len;
2354
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02002355 len = PAGE_SIZE;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002356
2357 mutex_lock(&wl->mutex);
2358 if (wl->hw_pg_ver >= 0)
2359 len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
2360 else
2361 len = snprintf(buf, len, "n/a\n");
2362 mutex_unlock(&wl->mutex);
2363
2364 return len;
2365}
2366
2367static DEVICE_ATTR(hw_pg_ver, S_IRUGO | S_IWUSR,
2368 wl1271_sysfs_show_hw_pg_ver, NULL);
2369
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002370int wl1271_register_hw(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002371{
2372 int ret;
2373
2374 if (wl->mac80211_registered)
2375 return 0;
2376
2377 SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
2378
2379 ret = ieee80211_register_hw(wl->hw);
2380 if (ret < 0) {
2381 wl1271_error("unable to register mac80211 hw: %d", ret);
2382 return ret;
2383 }
2384
2385 wl->mac80211_registered = true;
2386
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03002387 register_netdevice_notifier(&wl1271_dev_notifier);
2388
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002389 wl1271_notice("loaded");
2390
2391 return 0;
2392}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002393EXPORT_SYMBOL_GPL(wl1271_register_hw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002394
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002395void wl1271_unregister_hw(struct wl1271 *wl)
2396{
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03002397 unregister_netdevice_notifier(&wl1271_dev_notifier);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002398 ieee80211_unregister_hw(wl->hw);
2399 wl->mac80211_registered = false;
2400
2401}
2402EXPORT_SYMBOL_GPL(wl1271_unregister_hw);
2403
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002404int wl1271_init_ieee80211(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002405{
Juuso Oikarinen1e2b7972009-10-08 21:56:20 +03002406 /* The tx descriptor buffer and the TKIP space. */
2407 wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
2408 sizeof(struct wl1271_tx_hw_descr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002409
2410 /* unit us */
2411 /* FIXME: find a proper value */
2412 wl->hw->channel_change_time = 10000;
Juuso Oikarinen50c500a2010-04-01 11:38:22 +03002413 wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002414
2415 wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
Juuso Oikarinen03442a32009-11-23 23:22:14 +02002416 IEEE80211_HW_BEACON_FILTER |
Juuso Oikarinen0a343322010-02-22 08:38:41 +02002417 IEEE80211_HW_SUPPORTS_PS |
Kalle Valo4695dc92010-03-18 12:26:38 +02002418 IEEE80211_HW_SUPPORTS_UAPSD |
Juuso Oikarinena9af0922010-03-26 12:53:30 +02002419 IEEE80211_HW_HAS_RATE_CONTROL |
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03002420 IEEE80211_HW_CONNECTION_MONITOR |
2421 IEEE80211_HW_SUPPORTS_CQM_RSSI;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002422
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02002423 wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
2424 BIT(NL80211_IFTYPE_ADHOC);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002425 wl->hw->wiphy->max_scan_ssids = 1;
2426 wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz;
Juuso Oikarinen11eb5422010-08-24 06:28:03 +03002427 wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz;
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002428
Kalle Valo12bd8942010-03-18 12:26:33 +02002429 wl->hw->queues = 4;
Juuso Oikarinen31627dc2010-03-26 12:53:12 +02002430 wl->hw->max_rates = 1;
Kalle Valo12bd8942010-03-18 12:26:33 +02002431
Teemu Paasikivi8197b712010-02-22 08:38:23 +02002432 SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002433
2434 return 0;
2435}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002436EXPORT_SYMBOL_GPL(wl1271_init_ieee80211);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002437
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002438#define WL1271_DEFAULT_CHANNEL 0
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002439
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002440struct ieee80211_hw *wl1271_alloc_hw(void)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002441{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002442 struct ieee80211_hw *hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002443 struct platform_device *plat_dev = NULL;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002444 struct wl1271 *wl;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002445 int i, ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002446
2447 hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
2448 if (!hw) {
2449 wl1271_error("could not alloc ieee80211_hw");
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002450 ret = -ENOMEM;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002451 goto err_hw_alloc;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002452 }
2453
Julia Lawall929ebd32010-05-15 23:16:39 +02002454 plat_dev = kmemdup(&wl1271_device, sizeof(wl1271_device), GFP_KERNEL);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002455 if (!plat_dev) {
2456 wl1271_error("could not allocate platform_device");
2457 ret = -ENOMEM;
2458 goto err_plat_alloc;
2459 }
2460
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002461 wl = hw->priv;
2462 memset(wl, 0, sizeof(*wl));
2463
Juuso Oikarinen01c09162009-10-13 12:47:55 +03002464 INIT_LIST_HEAD(&wl->list);
2465
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002466 wl->hw = hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002467 wl->plat_dev = plat_dev;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002468
2469 skb_queue_head_init(&wl->tx_queue);
2470
Juuso Oikarinen37b70a82009-10-08 21:56:21 +03002471 INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03002472 INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002473 wl->channel = WL1271_DEFAULT_CHANNEL;
Juuso Oikarinen60e84c22010-03-26 12:53:25 +02002474 wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002475 wl->default_key = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002476 wl->rx_counter = 0;
2477 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
2478 wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02002479 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002480 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
Juuso Oikarinena6fe2312009-12-11 15:40:58 +02002481 wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002482 wl->basic_rate = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02002483 wl->rate_set = CONF_TX_RATE_MASK_BASIC;
2484 wl->sta_rate_set = 0;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03002485 wl->band = IEEE80211_BAND_2GHZ;
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03002486 wl->vif = NULL;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02002487 wl->flags = 0;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002488 wl->sg_enabled = true;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002489 wl->hw_pg_ver = -1;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002490
Juuso Oikarinenbe7078c2009-10-08 21:56:26 +03002491 for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002492 wl->tx_frames[i] = NULL;
2493
2494 spin_lock_init(&wl->wl_lock);
2495
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002496 wl->state = WL1271_STATE_OFF;
2497 mutex_init(&wl->mutex);
2498
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002499 /* Apply default driver configuration. */
2500 wl1271_conf_init(wl);
2501
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002502 wl1271_debugfs_init(wl);
2503
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002504 /* Register platform device */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002505 ret = platform_device_register(wl->plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002506 if (ret) {
2507 wl1271_error("couldn't register platform device");
2508 goto err_hw;
2509 }
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002510 dev_set_drvdata(&wl->plat_dev->dev, wl);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002511
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002512 /* Create sysfs file to control bt coex state */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002513 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002514 if (ret < 0) {
2515 wl1271_error("failed to create sysfs file bt_coex_state");
2516 goto err_platform;
2517 }
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002518
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002519 /* Create sysfs file to get HW PG version */
2520 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
2521 if (ret < 0) {
2522 wl1271_error("failed to create sysfs file hw_pg_ver");
2523 goto err_bt_coex_state;
2524 }
2525
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002526 return hw;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002527
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002528err_bt_coex_state:
2529 device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
2530
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002531err_platform:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002532 platform_device_unregister(wl->plat_dev);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002533
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002534err_hw:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002535 wl1271_debugfs_exit(wl);
2536 kfree(plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002537
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002538err_plat_alloc:
2539 ieee80211_free_hw(hw);
2540
2541err_hw_alloc:
2542
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002543 return ERR_PTR(ret);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002544}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002545EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002546
2547int wl1271_free_hw(struct wl1271 *wl)
2548{
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002549 platform_device_unregister(wl->plat_dev);
2550 kfree(wl->plat_dev);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002551
2552 wl1271_debugfs_exit(wl);
2553
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002554 vfree(wl->fw);
2555 wl->fw = NULL;
2556 kfree(wl->nvs);
2557 wl->nvs = NULL;
2558
2559 kfree(wl->fw_status);
2560 kfree(wl->tx_res_if);
2561
2562 ieee80211_free_hw(wl->hw);
2563
2564 return 0;
2565}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002566EXPORT_SYMBOL_GPL(wl1271_free_hw);
2567
2568MODULE_LICENSE("GPL");
2569MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
2570MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");