blob: d826b6684e39b1f5a9ef0866a1c64169fc5ed4ad [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
Juuso Oikarinend25611d2010-09-30 10:43:27 +0200639 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
640 ieee80211_connection_loss(wl->vif);
641
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200642 /* reboot the chipset */
643 __wl1271_op_remove_interface(wl);
644 ieee80211_restart_hw(wl->hw);
645
646out:
647 mutex_unlock(&wl->mutex);
648}
649
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300650static void wl1271_fw_wakeup(struct wl1271 *wl)
651{
652 u32 elp_reg;
653
654 elp_reg = ELPCTRL_WAKE_UP;
Juuso Oikarinen74621412009-10-12 15:08:54 +0300655 wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300656}
657
658static int wl1271_setup(struct wl1271 *wl)
659{
660 wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL);
661 if (!wl->fw_status)
662 return -ENOMEM;
663
664 wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
665 if (!wl->tx_res_if) {
666 kfree(wl->fw_status);
667 return -ENOMEM;
668 }
669
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300670 return 0;
671}
672
673static int wl1271_chip_wakeup(struct wl1271 *wl)
674{
Juuso Oikarinen451de972009-10-12 15:08:46 +0300675 struct wl1271_partition_set partition;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300676 int ret = 0;
677
Juuso Oikarinen01ac17e2009-12-11 15:41:02 +0200678 msleep(WL1271_PRE_POWER_ON_SLEEP);
Ohad Ben-Cohen2cc78ff2010-09-16 01:22:04 +0200679 ret = wl1271_power_on(wl);
680 if (ret < 0)
681 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300682 msleep(WL1271_POWER_ON_SLEEP);
Teemu Paasikivi9b280722010-02-18 13:25:56 +0200683 wl1271_io_reset(wl);
684 wl1271_io_init(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300685
686 /* We don't need a real memory partition here, because we only want
687 * to use the registers at this point. */
Juuso Oikarinen451de972009-10-12 15:08:46 +0300688 memset(&partition, 0, sizeof(partition));
689 partition.reg.start = REGISTERS_BASE;
690 partition.reg.size = REGISTERS_DOWN_SIZE;
691 wl1271_set_partition(wl, &partition);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300692
693 /* ELP module wake up */
694 wl1271_fw_wakeup(wl);
695
696 /* whal_FwCtrl_BootSm() */
697
698 /* 0. read chip id from CHIP_ID */
Teemu Paasikivi7b048c52010-02-18 13:25:55 +0200699 wl->chip.id = wl1271_read32(wl, CHIP_ID_B);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300700
701 /* 1. check if chip id is valid */
702
703 switch (wl->chip.id) {
704 case CHIP_ID_1271_PG10:
705 wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
706 wl->chip.id);
707
708 ret = wl1271_setup(wl);
709 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200710 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300711 break;
712 case CHIP_ID_1271_PG20:
713 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
714 wl->chip.id);
715
716 ret = wl1271_setup(wl);
717 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200718 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300719 break;
720 default:
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200721 wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300722 ret = -ENODEV;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200723 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300724 }
725
726 if (wl->fw == NULL) {
727 ret = wl1271_fetch_firmware(wl);
728 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200729 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300730 }
731
732 /* No NVS from netlink, try to get it from the filesystem */
733 if (wl->nvs == NULL) {
734 ret = wl1271_fetch_nvs(wl);
735 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200736 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300737 }
738
739out:
740 return ret;
741}
742
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300743int wl1271_plt_start(struct wl1271 *wl)
744{
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200745 int retries = WL1271_BOOT_RETRIES;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300746 int ret;
747
748 mutex_lock(&wl->mutex);
749
750 wl1271_notice("power up");
751
752 if (wl->state != WL1271_STATE_OFF) {
753 wl1271_error("cannot go into PLT state because not "
754 "in off state: %d", wl->state);
755 ret = -EBUSY;
756 goto out;
757 }
758
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200759 while (retries) {
760 retries--;
761 ret = wl1271_chip_wakeup(wl);
762 if (ret < 0)
763 goto power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300764
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200765 ret = wl1271_boot(wl);
766 if (ret < 0)
767 goto power_off;
768
769 ret = wl1271_plt_init(wl);
770 if (ret < 0)
771 goto irq_disable;
772
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200773 wl->state = WL1271_STATE_PLT;
774 wl1271_notice("firmware booted in PLT mode (%s)",
775 wl->chip.fw_ver);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300776 goto out;
777
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200778irq_disable:
779 wl1271_disable_interrupts(wl);
780 mutex_unlock(&wl->mutex);
781 /* Unlocking the mutex in the middle of handling is
782 inherently unsafe. In this case we deem it safe to do,
783 because we need to let any possibly pending IRQ out of
784 the system (and while we are WL1271_STATE_OFF the IRQ
785 work function will not do anything.) Also, any other
786 possible concurrent operations will fail due to the
787 current state, hence the wl1271 struct should be safe. */
788 cancel_work_sync(&wl->irq_work);
789 mutex_lock(&wl->mutex);
790power_off:
791 wl1271_power_off(wl);
792 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300793
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200794 wl1271_error("firmware boot in PLT mode failed despite %d retries",
795 WL1271_BOOT_RETRIES);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300796out:
797 mutex_unlock(&wl->mutex);
798
799 return ret;
800}
801
802int wl1271_plt_stop(struct wl1271 *wl)
803{
804 int ret = 0;
805
806 mutex_lock(&wl->mutex);
807
808 wl1271_notice("power down");
809
810 if (wl->state != WL1271_STATE_PLT) {
811 wl1271_error("cannot power down because not in PLT "
812 "state: %d", wl->state);
813 ret = -EBUSY;
814 goto out;
815 }
816
817 wl1271_disable_interrupts(wl);
818 wl1271_power_off(wl);
819
820 wl->state = WL1271_STATE_OFF;
Luciano Coelhobd5ea182009-10-13 12:47:58 +0300821 wl->rx_counter = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300822
823out:
824 mutex_unlock(&wl->mutex);
825
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +0200826 cancel_work_sync(&wl->irq_work);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200827 cancel_work_sync(&wl->recovery_work);
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +0200828
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300829 return ret;
830}
831
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300832static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
833{
834 struct wl1271 *wl = hw->priv;
Juuso Oikarinen830fb672009-12-11 15:41:06 +0200835 struct ieee80211_conf *conf = &hw->conf;
836 struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
837 struct ieee80211_sta *sta = txinfo->control.sta;
838 unsigned long flags;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300839
Juuso Oikarinen830fb672009-12-11 15:41:06 +0200840 /* peek into the rates configured in the STA entry */
841 spin_lock_irqsave(&wl->wl_lock, flags);
842 if (sta && sta->supp_rates[conf->channel->band] != wl->sta_rate_set) {
843 wl->sta_rate_set = sta->supp_rates[conf->channel->band];
844 set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags);
845 }
846 spin_unlock_irqrestore(&wl->wl_lock, flags);
847
848 /* queue the packet */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300849 skb_queue_tail(&wl->tx_queue, skb);
850
851 /*
852 * The chip specific setup must run before the first TX packet -
853 * before that, the tx_work will not be initialized!
854 */
855
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300856 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300857
858 /*
859 * The workqueue is slow to process the tx_queue and we need stop
860 * the queue here, otherwise the queue will get too long.
861 */
Juuso Oikarinen06f7bc72010-02-22 08:38:33 +0200862 if (skb_queue_len(&wl->tx_queue) >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
863 wl1271_debug(DEBUG_TX, "op_tx: stopping queues");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300864
Juuso Oikarinen06f7bc72010-02-22 08:38:33 +0200865 spin_lock_irqsave(&wl->wl_lock, flags);
866 ieee80211_stop_queues(wl->hw);
Juuso Oikarinen71449f82009-12-11 15:41:07 +0200867 set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
Juuso Oikarinen06f7bc72010-02-22 08:38:33 +0200868 spin_unlock_irqrestore(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300869 }
870
871 return NETDEV_TX_OK;
872}
873
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300874static struct notifier_block wl1271_dev_notifier = {
875 .notifier_call = wl1271_dev_notify,
876};
877
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300878static int wl1271_op_start(struct ieee80211_hw *hw)
879{
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200880 wl1271_debug(DEBUG_MAC80211, "mac80211 start");
881
882 /*
883 * We have to delay the booting of the hardware because
884 * we need to know the local MAC address before downloading and
885 * initializing the firmware. The MAC address cannot be changed
886 * after boot, and without the proper MAC address, the firmware
887 * will not function properly.
888 *
889 * The MAC address is first known when the corresponding interface
890 * is added. That is where we will initialize the hardware.
891 */
892
893 return 0;
894}
895
896static void wl1271_op_stop(struct ieee80211_hw *hw)
897{
898 wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
899}
900
901static int wl1271_op_add_interface(struct ieee80211_hw *hw,
902 struct ieee80211_vif *vif)
903{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300904 struct wl1271 *wl = hw->priv;
John W. Linvilleac01e942010-07-28 17:09:41 -0400905 struct wiphy *wiphy = hw->wiphy;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200906 int retries = WL1271_BOOT_RETRIES;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300907 int ret = 0;
908
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200909 wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
910 vif->type, vif->addr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300911
912 mutex_lock(&wl->mutex);
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200913 if (wl->vif) {
914 ret = -EBUSY;
915 goto out;
916 }
917
918 wl->vif = vif;
919
920 switch (vif->type) {
921 case NL80211_IFTYPE_STATION:
922 wl->bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +0200923 wl->set_bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200924 break;
925 case NL80211_IFTYPE_ADHOC:
926 wl->bss_type = BSS_TYPE_IBSS;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +0200927 wl->set_bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200928 break;
929 default:
930 ret = -EOPNOTSUPP;
931 goto out;
932 }
933
934 memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300935
936 if (wl->state != WL1271_STATE_OFF) {
937 wl1271_error("cannot start because not in off state: %d",
938 wl->state);
939 ret = -EBUSY;
940 goto out;
941 }
942
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200943 while (retries) {
944 retries--;
945 ret = wl1271_chip_wakeup(wl);
946 if (ret < 0)
947 goto power_off;
948
949 ret = wl1271_boot(wl);
950 if (ret < 0)
951 goto power_off;
952
953 ret = wl1271_hw_init(wl);
954 if (ret < 0)
955 goto irq_disable;
956
957 wl->state = WL1271_STATE_ON;
958 wl1271_info("firmware booted (%s)", wl->chip.fw_ver);
John W. Linvilleac01e942010-07-28 17:09:41 -0400959
960 /* update hw/fw version info in wiphy struct */
961 wiphy->hw_version = wl->chip.id;
962 strncpy(wiphy->fw_version, wl->chip.fw_ver,
963 sizeof(wiphy->fw_version));
964
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300965 goto out;
966
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200967irq_disable:
968 wl1271_disable_interrupts(wl);
969 mutex_unlock(&wl->mutex);
970 /* Unlocking the mutex in the middle of handling is
971 inherently unsafe. In this case we deem it safe to do,
972 because we need to let any possibly pending IRQ out of
973 the system (and while we are WL1271_STATE_OFF the IRQ
974 work function will not do anything.) Also, any other
975 possible concurrent operations will fail due to the
976 current state, hence the wl1271 struct should be safe. */
977 cancel_work_sync(&wl->irq_work);
978 mutex_lock(&wl->mutex);
979power_off:
980 wl1271_power_off(wl);
981 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300982
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200983 wl1271_error("firmware boot failed despite %d retries",
984 WL1271_BOOT_RETRIES);
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300985out:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300986 mutex_unlock(&wl->mutex);
987
Juuso Oikarineneb887df2010-07-08 17:49:58 +0300988 if (!ret)
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300989 list_add(&wl->list, &wl_list);
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300990
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300991 return ret;
992}
993
Juuso Oikarinen52a2a372010-09-21 06:23:30 +0200994static void __wl1271_op_remove_interface(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300995{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300996 int i;
997
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200998 wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300999
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001000 wl1271_info("down");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001001
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001002 list_del(&wl->list);
1003
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001004 WARN_ON(wl->state != WL1271_STATE_ON);
1005
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001006 /* enable dyn ps just in case (if left on due to fw crash etc) */
Juuso Oikarinen9a547bf2010-07-08 17:50:04 +03001007 if (wl->bss_type == BSS_TYPE_STA_BSS)
Juuso Oikarinenf532be62010-07-08 17:50:05 +03001008 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001009
Luciano Coelho08688d62010-07-08 17:50:07 +03001010 if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
Luciano Coelho08688d62010-07-08 17:50:07 +03001011 wl->scan.state = WL1271_SCAN_STATE_IDLE;
1012 kfree(wl->scan.scanned_ch);
1013 wl->scan.scanned_ch = NULL;
Juuso Oikarinen76a029f2010-07-29 04:54:45 +03001014 ieee80211_scan_completed(wl->hw, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001015 }
1016
1017 wl->state = WL1271_STATE_OFF;
1018
1019 wl1271_disable_interrupts(wl);
1020
1021 mutex_unlock(&wl->mutex);
1022
Juuso Oikarinen78abd322010-09-21 06:23:32 +02001023 cancel_delayed_work_sync(&wl->scan_complete_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001024 cancel_work_sync(&wl->irq_work);
1025 cancel_work_sync(&wl->tx_work);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001026 cancel_delayed_work_sync(&wl->pspoll_work);
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +02001027 cancel_delayed_work_sync(&wl->elp_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001028
1029 mutex_lock(&wl->mutex);
1030
1031 /* let's notify MAC80211 about the remaining pending TX frames */
Juuso Oikarinen781608c2010-05-24 11:18:17 +03001032 wl1271_tx_reset(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001033 wl1271_power_off(wl);
1034
1035 memset(wl->bssid, 0, ETH_ALEN);
1036 memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1);
1037 wl->ssid_len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001038 wl->bss_type = MAX_BSS_TYPE;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001039 wl->set_bss_type = MAX_BSS_TYPE;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001040 wl->band = IEEE80211_BAND_2GHZ;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001041
1042 wl->rx_counter = 0;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02001043 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001044 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
1045 wl->tx_blocks_available = 0;
1046 wl->tx_results_count = 0;
1047 wl->tx_packets_count = 0;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001048 wl->tx_security_last_seq = 0;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001049 wl->tx_security_seq = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001050 wl->time_offset = 0;
1051 wl->session_counter = 0;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001052 wl->rate_set = CONF_TX_RATE_MASK_BASIC;
1053 wl->sta_rate_set = 0;
1054 wl->flags = 0;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001055 wl->vif = NULL;
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001056 wl->filters = 0;
Luciano Coelhod6e19d12009-10-12 15:08:43 +03001057
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001058 for (i = 0; i < NUM_TX_QUEUES; i++)
1059 wl->tx_blocks_freed[i] = 0;
1060
1061 wl1271_debugfs_reset(wl);
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03001062
1063 kfree(wl->fw_status);
1064 wl->fw_status = NULL;
1065 kfree(wl->tx_res_if);
1066 wl->tx_res_if = NULL;
1067 kfree(wl->target_mem_map);
1068 wl->target_mem_map = NULL;
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001069}
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03001070
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001071static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
1072 struct ieee80211_vif *vif)
1073{
1074 struct wl1271 *wl = hw->priv;
1075
1076 mutex_lock(&wl->mutex);
1077 WARN_ON(wl->vif != vif);
1078 __wl1271_op_remove_interface(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001079 mutex_unlock(&wl->mutex);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001080
1081 cancel_work_sync(&wl->recovery_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001082}
1083
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001084static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
1085{
1086 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
1087 wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
1088
1089 /* combine requested filters with current filter config */
1090 filters = wl->filters | filters;
1091
1092 wl1271_debug(DEBUG_FILTERS, "RX filters set: ");
1093
1094 if (filters & FIF_PROMISC_IN_BSS) {
1095 wl1271_debug(DEBUG_FILTERS, " - FIF_PROMISC_IN_BSS");
1096 wl->rx_config &= ~CFG_UNI_FILTER_EN;
1097 wl->rx_config |= CFG_BSSID_FILTER_EN;
1098 }
1099 if (filters & FIF_BCN_PRBRESP_PROMISC) {
1100 wl1271_debug(DEBUG_FILTERS, " - FIF_BCN_PRBRESP_PROMISC");
1101 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1102 wl->rx_config &= ~CFG_SSID_FILTER_EN;
1103 }
1104 if (filters & FIF_OTHER_BSS) {
1105 wl1271_debug(DEBUG_FILTERS, " - FIF_OTHER_BSS");
1106 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1107 }
1108 if (filters & FIF_CONTROL) {
1109 wl1271_debug(DEBUG_FILTERS, " - FIF_CONTROL");
1110 wl->rx_filter |= CFG_RX_CTL_EN;
1111 }
1112 if (filters & FIF_FCSFAIL) {
1113 wl1271_debug(DEBUG_FILTERS, " - FIF_FCSFAIL");
1114 wl->rx_filter |= CFG_RX_FCS_ERROR;
1115 }
1116}
1117
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001118static int wl1271_dummy_join(struct wl1271 *wl)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001119{
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001120 int ret = 0;
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001121 /* we need to use a dummy BSSID for now */
1122 static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
1123 0xad, 0xbe, 0xef };
1124
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001125 memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
1126
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001127 /* pass through frames from all BSS */
1128 wl1271_configure_filters(wl, FIF_OTHER_BSS);
1129
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001130 ret = wl1271_cmd_join(wl, wl->set_bss_type);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001131 if (ret < 0)
1132 goto out;
1133
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001134 set_bit(WL1271_FLAG_JOINED, &wl->flags);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001135
1136out:
1137 return ret;
1138}
1139
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001140static int wl1271_join(struct wl1271 *wl, bool set_assoc)
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001141{
1142 int ret;
1143
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001144 /*
1145 * One of the side effects of the JOIN command is that is clears
1146 * WPA/WPA2 keys from the chipset. Performing a JOIN while associated
1147 * to a WPA/WPA2 access point will therefore kill the data-path.
1148 * Currently there is no supported scenario for JOIN during
1149 * association - if it becomes a supported scenario, the WPA/WPA2 keys
1150 * must be handled somehow.
1151 *
1152 */
1153 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1154 wl1271_info("JOIN while associated.");
1155
1156 if (set_assoc)
1157 set_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
1158
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001159 ret = wl1271_cmd_join(wl, wl->set_bss_type);
1160 if (ret < 0)
1161 goto out;
1162
1163 set_bit(WL1271_FLAG_JOINED, &wl->flags);
1164
1165 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1166 goto out;
1167
1168 /*
1169 * The join command disable the keep-alive mode, shut down its process,
1170 * and also clear the template config, so we need to reset it all after
1171 * the join. The acx_aid starts the keep-alive process, and the order
1172 * of the commands below is relevant.
1173 */
1174 ret = wl1271_acx_keep_alive_mode(wl, true);
1175 if (ret < 0)
1176 goto out;
1177
1178 ret = wl1271_acx_aid(wl, wl->aid);
1179 if (ret < 0)
1180 goto out;
1181
1182 ret = wl1271_cmd_build_klv_null_data(wl);
1183 if (ret < 0)
1184 goto out;
1185
1186 ret = wl1271_acx_keep_alive_config(wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
1187 ACX_KEEP_ALIVE_TPL_VALID);
1188 if (ret < 0)
1189 goto out;
1190
1191out:
1192 return ret;
1193}
1194
1195static int wl1271_unjoin(struct wl1271 *wl)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001196{
1197 int ret;
1198
1199 /* to stop listening to a channel, we disconnect */
1200 ret = wl1271_cmd_disconnect(wl);
1201 if (ret < 0)
1202 goto out;
1203
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001204 clear_bit(WL1271_FLAG_JOINED, &wl->flags);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001205 memset(wl->bssid, 0, ETH_ALEN);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001206
1207 /* stop filterting packets based on bssid */
1208 wl1271_configure_filters(wl, FIF_OTHER_BSS);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001209
1210out:
1211 return ret;
1212}
1213
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001214static void wl1271_set_band_rate(struct wl1271 *wl)
1215{
1216 if (wl->band == IEEE80211_BAND_2GHZ)
1217 wl->basic_rate_set = wl->conf.tx.basic_rate;
1218 else
1219 wl->basic_rate_set = wl->conf.tx.basic_rate_5;
1220}
1221
1222static u32 wl1271_min_rate_get(struct wl1271 *wl)
1223{
1224 int i;
1225 u32 rate = 0;
1226
1227 if (!wl->basic_rate_set) {
1228 WARN_ON(1);
1229 wl->basic_rate_set = wl->conf.tx.basic_rate;
1230 }
1231
1232 for (i = 0; !rate; i++) {
1233 if ((wl->basic_rate_set >> i) & 0x1)
1234 rate = 1 << i;
1235 }
1236
1237 return rate;
1238}
1239
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03001240static int wl1271_handle_idle(struct wl1271 *wl, bool idle)
1241{
1242 int ret;
1243
1244 if (idle) {
1245 if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
1246 ret = wl1271_unjoin(wl);
1247 if (ret < 0)
1248 goto out;
1249 }
1250 wl->rate_set = wl1271_min_rate_get(wl);
1251 wl->sta_rate_set = 0;
1252 ret = wl1271_acx_rate_policies(wl);
1253 if (ret < 0)
1254 goto out;
1255 ret = wl1271_acx_keep_alive_config(
1256 wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
1257 ACX_KEEP_ALIVE_TPL_INVALID);
1258 if (ret < 0)
1259 goto out;
1260 set_bit(WL1271_FLAG_IDLE, &wl->flags);
1261 } else {
1262 /* increment the session counter */
1263 wl->session_counter++;
1264 if (wl->session_counter >= SESSION_COUNTER_MAX)
1265 wl->session_counter = 0;
1266 ret = wl1271_dummy_join(wl);
1267 if (ret < 0)
1268 goto out;
1269 clear_bit(WL1271_FLAG_IDLE, &wl->flags);
1270 }
1271
1272out:
1273 return ret;
1274}
1275
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001276static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
1277{
1278 struct wl1271 *wl = hw->priv;
1279 struct ieee80211_conf *conf = &hw->conf;
1280 int channel, ret = 0;
1281
1282 channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
1283
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001284 wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s",
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001285 channel,
1286 conf->flags & IEEE80211_CONF_PS ? "on" : "off",
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001287 conf->power_level,
1288 conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001289
Juuso Oikarinen781608c2010-05-24 11:18:17 +03001290 /*
1291 * mac80211 will go to idle nearly immediately after transmitting some
1292 * frames, such as the deauth. To make sure those frames reach the air,
1293 * wait here until the TX queue is fully flushed.
1294 */
1295 if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
1296 (conf->flags & IEEE80211_CONF_IDLE))
1297 wl1271_tx_flush(wl);
1298
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001299 mutex_lock(&wl->mutex);
1300
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001301 if (unlikely(wl->state == WL1271_STATE_OFF))
1302 goto out;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001303
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001304 ret = wl1271_ps_elp_wakeup(wl, false);
1305 if (ret < 0)
1306 goto out;
1307
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001308 /* if the channel changes while joined, join again */
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001309 if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
1310 ((wl->band != conf->channel->band) ||
1311 (wl->channel != channel))) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001312 wl->band = conf->channel->band;
1313 wl->channel = channel;
1314
1315 /*
1316 * FIXME: the mac80211 should really provide a fixed rate
1317 * to use here. for now, just use the smallest possible rate
1318 * for the band as a fixed rate for association frames and
1319 * other control messages.
1320 */
1321 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1322 wl1271_set_band_rate(wl);
1323
1324 wl->basic_rate = wl1271_min_rate_get(wl);
1325 ret = wl1271_acx_rate_policies(wl);
1326 if (ret < 0)
1327 wl1271_warning("rate policy for update channel "
1328 "failed %d", ret);
1329
1330 if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001331 ret = wl1271_join(wl, false);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001332 if (ret < 0)
1333 wl1271_warning("cmd join to update channel "
1334 "failed %d", ret);
1335 }
1336 }
1337
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001338 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03001339 ret = wl1271_handle_idle(wl, conf->flags & IEEE80211_CONF_IDLE);
1340 if (ret < 0)
1341 wl1271_warning("idle mode change failed %d", ret);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001342 }
1343
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001344 /*
1345 * if mac80211 changes the PSM mode, make sure the mode is not
1346 * incorrectly changed after the pspoll failure active window.
1347 */
1348 if (changed & IEEE80211_CONF_CHANGE_PS)
1349 clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
1350
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001351 if (conf->flags & IEEE80211_CONF_PS &&
1352 !test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
1353 set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001354
1355 /*
1356 * We enter PSM only if we're already associated.
1357 * If we're not, we'll enter it when joining an SSID,
1358 * through the bss_info_changed() hook.
1359 */
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001360 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02001361 wl1271_debug(DEBUG_PSM, "psm enabled");
Juuso Oikarinend8c42c02010-02-18 13:25:36 +02001362 ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02001363 wl->basic_rate, true);
Juuso Oikarinenaf5e0842009-12-11 15:41:00 +02001364 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001365 } else if (!(conf->flags & IEEE80211_CONF_PS) &&
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001366 test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02001367 wl1271_debug(DEBUG_PSM, "psm disabled");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001368
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001369 clear_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001370
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001371 if (test_bit(WL1271_FLAG_PSM, &wl->flags))
Juuso Oikarinend8c42c02010-02-18 13:25:36 +02001372 ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02001373 wl->basic_rate, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001374 }
1375
1376 if (conf->power_level != wl->power_level) {
1377 ret = wl1271_acx_tx_power(wl, conf->power_level);
1378 if (ret < 0)
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02001379 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001380
1381 wl->power_level = conf->power_level;
1382 }
1383
1384out_sleep:
1385 wl1271_ps_elp_sleep(wl);
1386
1387out:
1388 mutex_unlock(&wl->mutex);
1389
1390 return ret;
1391}
1392
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001393struct wl1271_filter_params {
1394 bool enabled;
1395 int mc_list_length;
1396 u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
1397};
1398
Jiri Pirko22bedad2010-04-01 21:22:57 +00001399static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw,
1400 struct netdev_hw_addr_list *mc_list)
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001401{
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001402 struct wl1271_filter_params *fp;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001403 struct netdev_hw_addr *ha;
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001404 struct wl1271 *wl = hw->priv;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001405
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001406 if (unlikely(wl->state == WL1271_STATE_OFF))
1407 return 0;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001408
Juuso Oikarinen74441132009-10-13 12:47:53 +03001409 fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001410 if (!fp) {
1411 wl1271_error("Out of memory setting filters.");
1412 return 0;
1413 }
1414
1415 /* update multicast filtering parameters */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001416 fp->mc_list_length = 0;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001417 if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) {
1418 fp->enabled = false;
1419 } else {
1420 fp->enabled = true;
1421 netdev_hw_addr_list_for_each(ha, mc_list) {
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001422 memcpy(fp->mc_list[fp->mc_list_length],
Jiri Pirko22bedad2010-04-01 21:22:57 +00001423 ha->addr, ETH_ALEN);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001424 fp->mc_list_length++;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001425 }
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001426 }
1427
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001428 return (u64)(unsigned long)fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001429}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001430
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001431#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
1432 FIF_ALLMULTI | \
1433 FIF_FCSFAIL | \
1434 FIF_BCN_PRBRESP_PROMISC | \
1435 FIF_CONTROL | \
1436 FIF_OTHER_BSS)
1437
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001438static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
1439 unsigned int changed,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001440 unsigned int *total, u64 multicast)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001441{
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001442 struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001443 struct wl1271 *wl = hw->priv;
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001444 int ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001445
1446 wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter");
1447
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001448 mutex_lock(&wl->mutex);
1449
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001450 *total &= WL1271_SUPPORTED_FILTERS;
1451 changed &= WL1271_SUPPORTED_FILTERS;
1452
1453 if (unlikely(wl->state == WL1271_STATE_OFF))
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001454 goto out;
1455
1456 ret = wl1271_ps_elp_wakeup(wl, false);
1457 if (ret < 0)
1458 goto out;
1459
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001460
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001461 if (*total & FIF_ALLMULTI)
1462 ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
1463 else if (fp)
1464 ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
1465 fp->mc_list,
1466 fp->mc_list_length);
1467 if (ret < 0)
1468 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001469
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001470 /* determine, whether supported filter values have changed */
1471 if (changed == 0)
1472 goto out_sleep;
1473
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001474 /* configure filters */
1475 wl->filters = *total;
1476 wl1271_configure_filters(wl, 0);
1477
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001478 /* apply configured filters */
1479 ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
1480 if (ret < 0)
1481 goto out_sleep;
1482
1483out_sleep:
1484 wl1271_ps_elp_sleep(wl);
1485
1486out:
1487 mutex_unlock(&wl->mutex);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001488 kfree(fp);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001489}
1490
1491static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
1492 struct ieee80211_vif *vif,
1493 struct ieee80211_sta *sta,
1494 struct ieee80211_key_conf *key_conf)
1495{
1496 struct wl1271 *wl = hw->priv;
1497 const u8 *addr;
1498 int ret;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001499 u32 tx_seq_32 = 0;
1500 u16 tx_seq_16 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001501 u8 key_type;
1502
1503 static const u8 bcast_addr[ETH_ALEN] =
1504 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1505
1506 wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
1507
1508 addr = sta ? sta->addr : bcast_addr;
1509
1510 wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd);
1511 wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN);
1512 wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
Johannes Berg97359d12010-08-10 09:46:38 +02001513 key_conf->cipher, key_conf->keyidx,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001514 key_conf->keylen, key_conf->flags);
1515 wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
1516
1517 if (is_zero_ether_addr(addr)) {
1518 /* We dont support TX only encryption */
1519 ret = -EOPNOTSUPP;
1520 goto out;
1521 }
1522
1523 mutex_lock(&wl->mutex);
1524
1525 ret = wl1271_ps_elp_wakeup(wl, false);
1526 if (ret < 0)
1527 goto out_unlock;
1528
Johannes Berg97359d12010-08-10 09:46:38 +02001529 switch (key_conf->cipher) {
1530 case WLAN_CIPHER_SUITE_WEP40:
1531 case WLAN_CIPHER_SUITE_WEP104:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001532 key_type = KEY_WEP;
1533
1534 key_conf->hw_key_idx = key_conf->keyidx;
1535 break;
Johannes Berg97359d12010-08-10 09:46:38 +02001536 case WLAN_CIPHER_SUITE_TKIP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001537 key_type = KEY_TKIP;
1538
1539 key_conf->hw_key_idx = key_conf->keyidx;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001540 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1541 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001542 break;
Johannes Berg97359d12010-08-10 09:46:38 +02001543 case WLAN_CIPHER_SUITE_CCMP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001544 key_type = KEY_AES;
1545
1546 key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001547 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1548 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001549 break;
Juuso Oikarinen7a557242010-09-27 12:42:07 +02001550 case WL1271_CIPHER_SUITE_GEM:
1551 key_type = KEY_GEM;
1552 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1553 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
1554 break;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001555 default:
Johannes Berg97359d12010-08-10 09:46:38 +02001556 wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001557
1558 ret = -EOPNOTSUPP;
1559 goto out_sleep;
1560 }
1561
1562 switch (cmd) {
1563 case SET_KEY:
1564 ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE,
1565 key_conf->keyidx, key_type,
1566 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001567 addr, tx_seq_32, tx_seq_16);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001568 if (ret < 0) {
1569 wl1271_error("Could not add or replace key");
1570 goto out_sleep;
1571 }
Juuso Oikarinenee444cf2010-02-18 13:25:50 +02001572
1573 /* the default WEP key needs to be configured at least once */
1574 if (key_type == KEY_WEP) {
1575 ret = wl1271_cmd_set_default_wep_key(wl,
1576 wl->default_key);
1577 if (ret < 0)
1578 goto out_sleep;
1579 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001580 break;
1581
1582 case DISABLE_KEY:
Juuso Oikarinenfddc7dd2010-02-18 13:25:49 +02001583 /* The wl1271 does not allow to remove unicast keys - they
1584 will be cleared automatically on next CMD_JOIN. Ignore the
1585 request silently, as we dont want the mac80211 to emit
1586 an error message. */
1587 if (!is_broadcast_ether_addr(addr))
1588 break;
1589
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001590 ret = wl1271_cmd_set_key(wl, KEY_REMOVE,
1591 key_conf->keyidx, key_type,
1592 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001593 addr, 0, 0);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001594 if (ret < 0) {
1595 wl1271_error("Could not remove key");
1596 goto out_sleep;
1597 }
1598 break;
1599
1600 default:
1601 wl1271_error("Unsupported key cmd 0x%x", cmd);
1602 ret = -EOPNOTSUPP;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001603 break;
1604 }
1605
1606out_sleep:
1607 wl1271_ps_elp_sleep(wl);
1608
1609out_unlock:
1610 mutex_unlock(&wl->mutex);
1611
1612out:
1613 return ret;
1614}
1615
1616static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
Johannes Berga060bbf2010-04-27 11:59:34 +02001617 struct ieee80211_vif *vif,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001618 struct cfg80211_scan_request *req)
1619{
1620 struct wl1271 *wl = hw->priv;
1621 int ret;
1622 u8 *ssid = NULL;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001623 size_t len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001624
1625 wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
1626
1627 if (req->n_ssids) {
1628 ssid = req->ssids[0].ssid;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001629 len = req->ssids[0].ssid_len;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001630 }
1631
1632 mutex_lock(&wl->mutex);
1633
1634 ret = wl1271_ps_elp_wakeup(wl, false);
1635 if (ret < 0)
1636 goto out;
1637
Luciano Coelho5924f892010-08-04 03:46:22 +03001638 ret = wl1271_scan(hw->priv, ssid, len, req);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001639
1640 wl1271_ps_elp_sleep(wl);
1641
1642out:
1643 mutex_unlock(&wl->mutex);
1644
1645 return ret;
1646}
1647
1648static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1649{
1650 struct wl1271 *wl = hw->priv;
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03001651 int ret = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001652
1653 mutex_lock(&wl->mutex);
1654
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03001655 if (unlikely(wl->state == WL1271_STATE_OFF))
1656 goto out;
1657
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001658 ret = wl1271_ps_elp_wakeup(wl, false);
1659 if (ret < 0)
1660 goto out;
1661
1662 ret = wl1271_acx_rts_threshold(wl, (u16) value);
1663 if (ret < 0)
1664 wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret);
1665
1666 wl1271_ps_elp_sleep(wl);
1667
1668out:
1669 mutex_unlock(&wl->mutex);
1670
1671 return ret;
1672}
1673
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001674static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *beacon)
1675{
1676 u8 *ptr = beacon->data +
1677 offsetof(struct ieee80211_mgmt, u.beacon.variable);
1678
1679 /* find the location of the ssid in the beacon */
1680 while (ptr < beacon->data + beacon->len) {
1681 if (ptr[0] == WLAN_EID_SSID) {
1682 wl->ssid_len = ptr[1];
1683 memcpy(wl->ssid, ptr+2, wl->ssid_len);
1684 return;
1685 }
1686 ptr += ptr[1];
1687 }
1688 wl1271_error("ad-hoc beacon template has no SSID!\n");
1689}
1690
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001691static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
1692 struct ieee80211_vif *vif,
1693 struct ieee80211_bss_conf *bss_conf,
1694 u32 changed)
1695{
1696 enum wl1271_cmd_ps_mode mode;
1697 struct wl1271 *wl = hw->priv;
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001698 bool do_join = false;
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001699 bool set_assoc = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001700 int ret;
1701
1702 wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed");
1703
1704 mutex_lock(&wl->mutex);
1705
1706 ret = wl1271_ps_elp_wakeup(wl, false);
1707 if (ret < 0)
1708 goto out;
1709
Eliad Peller9ee82d52010-09-19 18:55:09 +02001710 if ((changed & BSS_CHANGED_BEACON_INT) &&
Juuso Oikarinen60e84c22010-03-26 12:53:25 +02001711 (wl->bss_type == BSS_TYPE_IBSS)) {
1712 wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon interval updated: %d",
1713 bss_conf->beacon_int);
1714
1715 wl->beacon_int = bss_conf->beacon_int;
1716 do_join = true;
1717 }
1718
Eliad Peller9ee82d52010-09-19 18:55:09 +02001719 if ((changed & BSS_CHANGED_BEACON) &&
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001720 (wl->bss_type == BSS_TYPE_IBSS)) {
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001721 struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
1722
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001723 wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon updated");
1724
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001725 if (beacon) {
1726 struct ieee80211_hdr *hdr;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001727
1728 wl1271_ssid_set(wl, beacon);
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001729 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON,
1730 beacon->data,
Juuso Oikarinen606c1482010-04-01 11:38:21 +03001731 beacon->len, 0,
1732 wl1271_min_rate_get(wl));
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001733
1734 if (ret < 0) {
1735 dev_kfree_skb(beacon);
1736 goto out_sleep;
1737 }
1738
1739 hdr = (struct ieee80211_hdr *) beacon->data;
1740 hdr->frame_control = cpu_to_le16(
1741 IEEE80211_FTYPE_MGMT |
1742 IEEE80211_STYPE_PROBE_RESP);
1743
1744 ret = wl1271_cmd_template_set(wl,
1745 CMD_TEMPL_PROBE_RESPONSE,
1746 beacon->data,
Juuso Oikarinen606c1482010-04-01 11:38:21 +03001747 beacon->len, 0,
1748 wl1271_min_rate_get(wl));
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001749 dev_kfree_skb(beacon);
1750 if (ret < 0)
1751 goto out_sleep;
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001752
1753 /* Need to update the SSID (for filtering etc) */
1754 do_join = true;
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001755 }
1756 }
1757
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001758 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1759 (wl->bss_type == BSS_TYPE_IBSS)) {
1760 wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
1761 bss_conf->enable_beacon ? "enabled" : "disabled");
1762
1763 if (bss_conf->enable_beacon)
1764 wl->set_bss_type = BSS_TYPE_IBSS;
1765 else
1766 wl->set_bss_type = BSS_TYPE_STA_BSS;
1767 do_join = true;
1768 }
1769
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03001770 if (changed & BSS_CHANGED_CQM) {
1771 bool enable = false;
1772 if (bss_conf->cqm_rssi_thold)
1773 enable = true;
1774 ret = wl1271_acx_rssi_snr_trigger(wl, enable,
1775 bss_conf->cqm_rssi_thold,
1776 bss_conf->cqm_rssi_hyst);
1777 if (ret < 0)
1778 goto out;
1779 wl->rssi_thold = bss_conf->cqm_rssi_thold;
1780 }
1781
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001782 if ((changed & BSS_CHANGED_BSSID) &&
1783 /*
1784 * Now we know the correct bssid, so we send a new join command
1785 * and enable the BSSID filter
1786 */
1787 memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001788 memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
Juuso Oikarinena0cb7be2010-03-18 12:26:44 +02001789
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001790 ret = wl1271_cmd_build_null_data(wl);
Juuso Oikarinena0cb7be2010-03-18 12:26:44 +02001791 if (ret < 0)
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001792 goto out_sleep;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001793
Saravanan Dhanabal141418c2010-04-28 09:50:00 +03001794 ret = wl1271_build_qos_null_data(wl);
1795 if (ret < 0)
1796 goto out_sleep;
1797
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001798 /* filter out all packets not from this BSSID */
1799 wl1271_configure_filters(wl, 0);
1800
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001801 /* Need to update the BSSID (for filtering etc) */
1802 do_join = true;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001803 }
1804
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001805 if (changed & BSS_CHANGED_ASSOC) {
1806 if (bss_conf->assoc) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001807 u32 rates;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001808 wl->aid = bss_conf->aid;
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001809 set_assoc = true;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001810
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001811 wl->ps_poll_failures = 0;
1812
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001813 /*
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001814 * use basic rates from AP, and determine lowest rate
1815 * to use with control frames.
1816 */
1817 rates = bss_conf->basic_rates;
1818 wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
1819 rates);
1820 wl->basic_rate = wl1271_min_rate_get(wl);
1821 ret = wl1271_acx_rate_policies(wl);
1822 if (ret < 0)
1823 goto out_sleep;
1824
1825 /*
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001826 * with wl1271, we don't need to update the
1827 * beacon_int and dtim_period, because the firmware
1828 * updates it by itself when the first beacon is
1829 * received after a join.
1830 */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001831 ret = wl1271_cmd_build_ps_poll(wl, wl->aid);
1832 if (ret < 0)
1833 goto out_sleep;
1834
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02001835 /*
1836 * The SSID is intentionally set to NULL here - the
1837 * firmware will set the probe request with a
1838 * broadcast SSID regardless of what we set in the
1839 * template.
1840 */
1841 ret = wl1271_cmd_build_probe_req(wl, NULL, 0,
1842 NULL, 0, wl->band);
1843
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02001844 /* enable the connection monitoring feature */
1845 ret = wl1271_acx_conn_monit_params(wl, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001846 if (ret < 0)
1847 goto out_sleep;
1848
1849 /* If we want to go in PSM but we're not there yet */
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001850 if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) &&
1851 !test_bit(WL1271_FLAG_PSM, &wl->flags)) {
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001852 mode = STATION_POWER_SAVE_MODE;
Juuso Oikarinen65cddbf2010-08-24 06:28:03 +03001853 ret = wl1271_ps_set_mode(wl, mode,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02001854 wl->basic_rate,
Juuso Oikarinen65cddbf2010-08-24 06:28:03 +03001855 true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001856 if (ret < 0)
1857 goto out_sleep;
1858 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001859 } else {
1860 /* use defaults when not associated */
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03001861 clear_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags);
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001862 clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001863 wl->aid = 0;
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02001864
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001865 /* re-enable dynamic ps - just in case */
Juuso Oikarinenf532be62010-07-08 17:50:05 +03001866 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001867
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001868 /* revert back to minimum rates for the current band */
1869 wl1271_set_band_rate(wl);
1870 wl->basic_rate = wl1271_min_rate_get(wl);
1871 ret = wl1271_acx_rate_policies(wl);
1872 if (ret < 0)
1873 goto out_sleep;
1874
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02001875 /* disable connection monitor features */
1876 ret = wl1271_acx_conn_monit_params(wl, false);
Juuso Oikarinenc1899552010-03-26 12:53:32 +02001877
1878 /* Disable the keep-alive feature */
1879 ret = wl1271_acx_keep_alive_mode(wl, false);
1880
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02001881 if (ret < 0)
1882 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001883 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001884
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001885 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001886
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001887 if (changed & BSS_CHANGED_ERP_SLOT) {
1888 if (bss_conf->use_short_slot)
1889 ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
1890 else
1891 ret = wl1271_acx_slot(wl, SLOT_TIME_LONG);
1892 if (ret < 0) {
1893 wl1271_warning("Set slot time failed %d", ret);
1894 goto out_sleep;
1895 }
1896 }
1897
1898 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
1899 if (bss_conf->use_short_preamble)
1900 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT);
1901 else
1902 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG);
1903 }
1904
1905 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
1906 if (bss_conf->use_cts_prot)
1907 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE);
1908 else
1909 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE);
1910 if (ret < 0) {
1911 wl1271_warning("Set ctsprotect failed %d", ret);
1912 goto out_sleep;
1913 }
1914 }
1915
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03001916 if (changed & BSS_CHANGED_ARP_FILTER) {
1917 __be32 addr = bss_conf->arp_addr_list[0];
1918 WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
1919
1920 if (bss_conf->arp_addr_cnt == 1 && bss_conf->arp_filter_enabled)
1921 ret = wl1271_acx_arp_ip_filter(wl, true, addr);
1922 else
1923 ret = wl1271_acx_arp_ip_filter(wl, false, addr);
1924
1925 if (ret < 0)
1926 goto out_sleep;
1927 }
1928
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001929 if (do_join) {
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001930 ret = wl1271_join(wl, set_assoc);
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001931 if (ret < 0) {
1932 wl1271_warning("cmd join failed %d", ret);
1933 goto out_sleep;
1934 }
Juuso Oikarinenc1899552010-03-26 12:53:32 +02001935 }
1936
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001937out_sleep:
1938 wl1271_ps_elp_sleep(wl);
1939
1940out:
1941 mutex_unlock(&wl->mutex);
1942}
1943
Kalle Valoc6999d82010-02-18 13:25:41 +02001944static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
1945 const struct ieee80211_tx_queue_params *params)
1946{
1947 struct wl1271 *wl = hw->priv;
Kalle Valo4695dc92010-03-18 12:26:38 +02001948 u8 ps_scheme;
Kalle Valoc6999d82010-02-18 13:25:41 +02001949 int ret;
1950
1951 mutex_lock(&wl->mutex);
1952
1953 wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
1954
Kalle Valoc82c1dd2010-02-18 13:25:47 +02001955 ret = wl1271_ps_elp_wakeup(wl, false);
1956 if (ret < 0)
1957 goto out;
1958
Juuso Oikarinenb43316d2010-03-18 12:26:26 +02001959 /* the txop is confed in units of 32us by the mac80211, we need us */
Kalle Valoc6999d82010-02-18 13:25:41 +02001960 ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
1961 params->cw_min, params->cw_max,
Juuso Oikarinenb43316d2010-03-18 12:26:26 +02001962 params->aifs, params->txop << 5);
Kalle Valoc6999d82010-02-18 13:25:41 +02001963 if (ret < 0)
Kalle Valoc82c1dd2010-02-18 13:25:47 +02001964 goto out_sleep;
Kalle Valoc6999d82010-02-18 13:25:41 +02001965
Kalle Valo4695dc92010-03-18 12:26:38 +02001966 if (params->uapsd)
1967 ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
1968 else
1969 ps_scheme = CONF_PS_SCHEME_LEGACY;
1970
Kalle Valoc6999d82010-02-18 13:25:41 +02001971 ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
1972 CONF_CHANNEL_TYPE_EDCF,
1973 wl1271_tx_get_queue(queue),
Kalle Valo4695dc92010-03-18 12:26:38 +02001974 ps_scheme, CONF_ACK_POLICY_LEGACY, 0, 0);
Kalle Valoc6999d82010-02-18 13:25:41 +02001975 if (ret < 0)
Kalle Valoc82c1dd2010-02-18 13:25:47 +02001976 goto out_sleep;
1977
1978out_sleep:
1979 wl1271_ps_elp_sleep(wl);
Kalle Valoc6999d82010-02-18 13:25:41 +02001980
1981out:
1982 mutex_unlock(&wl->mutex);
1983
1984 return ret;
1985}
1986
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03001987static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw)
1988{
1989
1990 struct wl1271 *wl = hw->priv;
1991 u64 mactime = ULLONG_MAX;
1992 int ret;
1993
1994 wl1271_debug(DEBUG_MAC80211, "mac80211 get tsf");
1995
1996 mutex_lock(&wl->mutex);
1997
1998 ret = wl1271_ps_elp_wakeup(wl, false);
1999 if (ret < 0)
2000 goto out;
2001
2002 ret = wl1271_acx_tsf_info(wl, &mactime);
2003 if (ret < 0)
2004 goto out_sleep;
2005
2006out_sleep:
2007 wl1271_ps_elp_sleep(wl);
2008
2009out:
2010 mutex_unlock(&wl->mutex);
2011 return mactime;
2012}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002013
John W. Linvilleece550d2010-07-28 16:41:06 -04002014static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
2015 struct survey_info *survey)
2016{
2017 struct wl1271 *wl = hw->priv;
2018 struct ieee80211_conf *conf = &hw->conf;
2019
2020 if (idx != 0)
2021 return -ENOENT;
2022
2023 survey->channel = conf->channel;
2024 survey->filled = SURVEY_INFO_NOISE_DBM;
2025 survey->noise = wl->noise;
2026
2027 return 0;
2028}
2029
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002030/* can't be const, mac80211 writes to this */
2031static struct ieee80211_rate wl1271_rates[] = {
2032 { .bitrate = 10,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002033 .hw_value = CONF_HW_BIT_RATE_1MBPS,
2034 .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002035 { .bitrate = 20,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002036 .hw_value = CONF_HW_BIT_RATE_2MBPS,
2037 .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002038 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2039 { .bitrate = 55,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002040 .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
2041 .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002042 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2043 { .bitrate = 110,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002044 .hw_value = CONF_HW_BIT_RATE_11MBPS,
2045 .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002046 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2047 { .bitrate = 60,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002048 .hw_value = CONF_HW_BIT_RATE_6MBPS,
2049 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002050 { .bitrate = 90,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002051 .hw_value = CONF_HW_BIT_RATE_9MBPS,
2052 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002053 { .bitrate = 120,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002054 .hw_value = CONF_HW_BIT_RATE_12MBPS,
2055 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002056 { .bitrate = 180,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002057 .hw_value = CONF_HW_BIT_RATE_18MBPS,
2058 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002059 { .bitrate = 240,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002060 .hw_value = CONF_HW_BIT_RATE_24MBPS,
2061 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002062 { .bitrate = 360,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002063 .hw_value = CONF_HW_BIT_RATE_36MBPS,
2064 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002065 { .bitrate = 480,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002066 .hw_value = CONF_HW_BIT_RATE_48MBPS,
2067 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002068 { .bitrate = 540,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002069 .hw_value = CONF_HW_BIT_RATE_54MBPS,
2070 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002071};
2072
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002073/*
2074 * Can't be const, mac80211 writes to this. The order of the channels here
2075 * is designed to improve scanning.
2076 */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002077static struct ieee80211_channel wl1271_channels[] = {
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02002078 { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02002079 { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02002080 { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02002081 { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002082 { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
2083 { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
2084 { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
2085 { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
2086 { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
2087 { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
2088 { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
2089 { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
2090 { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002091};
2092
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002093/* mapping to indexes for wl1271_rates */
Tobias Klausera0ea9492010-05-20 10:38:11 +02002094static const u8 wl1271_rate_to_idx_2ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002095 /* MCS rates are used only with 11n */
2096 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS7 */
2097 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS6 */
2098 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS5 */
2099 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS4 */
2100 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS3 */
2101 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS2 */
2102 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS1 */
2103 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS0 */
2104
2105 11, /* CONF_HW_RXTX_RATE_54 */
2106 10, /* CONF_HW_RXTX_RATE_48 */
2107 9, /* CONF_HW_RXTX_RATE_36 */
2108 8, /* CONF_HW_RXTX_RATE_24 */
2109
2110 /* TI-specific rate */
2111 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
2112
2113 7, /* CONF_HW_RXTX_RATE_18 */
2114 6, /* CONF_HW_RXTX_RATE_12 */
2115 3, /* CONF_HW_RXTX_RATE_11 */
2116 5, /* CONF_HW_RXTX_RATE_9 */
2117 4, /* CONF_HW_RXTX_RATE_6 */
2118 2, /* CONF_HW_RXTX_RATE_5_5 */
2119 1, /* CONF_HW_RXTX_RATE_2 */
2120 0 /* CONF_HW_RXTX_RATE_1 */
2121};
2122
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002123/* can't be const, mac80211 writes to this */
2124static struct ieee80211_supported_band wl1271_band_2ghz = {
2125 .channels = wl1271_channels,
2126 .n_channels = ARRAY_SIZE(wl1271_channels),
2127 .bitrates = wl1271_rates,
2128 .n_bitrates = ARRAY_SIZE(wl1271_rates),
2129};
2130
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002131/* 5 GHz data rates for WL1273 */
2132static struct ieee80211_rate wl1271_rates_5ghz[] = {
2133 { .bitrate = 60,
2134 .hw_value = CONF_HW_BIT_RATE_6MBPS,
2135 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
2136 { .bitrate = 90,
2137 .hw_value = CONF_HW_BIT_RATE_9MBPS,
2138 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
2139 { .bitrate = 120,
2140 .hw_value = CONF_HW_BIT_RATE_12MBPS,
2141 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
2142 { .bitrate = 180,
2143 .hw_value = CONF_HW_BIT_RATE_18MBPS,
2144 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
2145 { .bitrate = 240,
2146 .hw_value = CONF_HW_BIT_RATE_24MBPS,
2147 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
2148 { .bitrate = 360,
2149 .hw_value = CONF_HW_BIT_RATE_36MBPS,
2150 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
2151 { .bitrate = 480,
2152 .hw_value = CONF_HW_BIT_RATE_48MBPS,
2153 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
2154 { .bitrate = 540,
2155 .hw_value = CONF_HW_BIT_RATE_54MBPS,
2156 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
2157};
2158
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002159/*
2160 * 5 GHz band channels for WL1273 - can't be const, mac80211 writes to this.
2161 * The order of the channels here is designed to improve scanning.
2162 */
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002163static struct ieee80211_channel wl1271_channels_5ghz[] = {
2164 { .hw_value = 183, .center_freq = 4915},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002165 { .hw_value = 188, .center_freq = 4940},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002166 { .hw_value = 8, .center_freq = 5040},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002167 { .hw_value = 34, .center_freq = 5170},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002168 { .hw_value = 44, .center_freq = 5220},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002169 { .hw_value = 60, .center_freq = 5300},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002170 { .hw_value = 112, .center_freq = 5560},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002171 { .hw_value = 132, .center_freq = 5660},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002172 { .hw_value = 157, .center_freq = 5785},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002173 { .hw_value = 184, .center_freq = 4920},
2174 { .hw_value = 189, .center_freq = 4945},
2175 { .hw_value = 9, .center_freq = 5045},
2176 { .hw_value = 36, .center_freq = 5180},
2177 { .hw_value = 46, .center_freq = 5230},
2178 { .hw_value = 64, .center_freq = 5320},
2179 { .hw_value = 116, .center_freq = 5580},
2180 { .hw_value = 136, .center_freq = 5680},
2181 { .hw_value = 192, .center_freq = 4960},
2182 { .hw_value = 11, .center_freq = 5055},
2183 { .hw_value = 38, .center_freq = 5190},
2184 { .hw_value = 48, .center_freq = 5240},
2185 { .hw_value = 100, .center_freq = 5500},
2186 { .hw_value = 120, .center_freq = 5600},
2187 { .hw_value = 140, .center_freq = 5700},
2188 { .hw_value = 185, .center_freq = 4925},
2189 { .hw_value = 196, .center_freq = 4980},
2190 { .hw_value = 12, .center_freq = 5060},
2191 { .hw_value = 40, .center_freq = 5200},
2192 { .hw_value = 52, .center_freq = 5260},
2193 { .hw_value = 104, .center_freq = 5520},
2194 { .hw_value = 124, .center_freq = 5620},
2195 { .hw_value = 149, .center_freq = 5745},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002196 { .hw_value = 161, .center_freq = 5805},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002197 { .hw_value = 187, .center_freq = 4935},
2198 { .hw_value = 7, .center_freq = 5035},
2199 { .hw_value = 16, .center_freq = 5080},
2200 { .hw_value = 42, .center_freq = 5210},
2201 { .hw_value = 56, .center_freq = 5280},
2202 { .hw_value = 108, .center_freq = 5540},
2203 { .hw_value = 128, .center_freq = 5640},
2204 { .hw_value = 153, .center_freq = 5765},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002205 { .hw_value = 165, .center_freq = 5825},
2206};
2207
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002208/* mapping to indexes for wl1271_rates_5ghz */
Tobias Klausera0ea9492010-05-20 10:38:11 +02002209static const u8 wl1271_rate_to_idx_5ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002210 /* MCS rates are used only with 11n */
2211 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS7 */
2212 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS6 */
2213 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS5 */
2214 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS4 */
2215 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS3 */
2216 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS2 */
2217 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS1 */
2218 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS0 */
2219
2220 7, /* CONF_HW_RXTX_RATE_54 */
2221 6, /* CONF_HW_RXTX_RATE_48 */
2222 5, /* CONF_HW_RXTX_RATE_36 */
2223 4, /* CONF_HW_RXTX_RATE_24 */
2224
2225 /* TI-specific rate */
2226 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
2227
2228 3, /* CONF_HW_RXTX_RATE_18 */
2229 2, /* CONF_HW_RXTX_RATE_12 */
2230 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_11 */
2231 1, /* CONF_HW_RXTX_RATE_9 */
2232 0, /* CONF_HW_RXTX_RATE_6 */
2233 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_5_5 */
2234 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_2 */
2235 CONF_HW_RXTX_RATE_UNSUPPORTED /* CONF_HW_RXTX_RATE_1 */
2236};
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002237
2238static struct ieee80211_supported_band wl1271_band_5ghz = {
2239 .channels = wl1271_channels_5ghz,
2240 .n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
2241 .bitrates = wl1271_rates_5ghz,
2242 .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
2243};
2244
Tobias Klausera0ea9492010-05-20 10:38:11 +02002245static const u8 *wl1271_band_rate_to_idx[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002246 [IEEE80211_BAND_2GHZ] = wl1271_rate_to_idx_2ghz,
2247 [IEEE80211_BAND_5GHZ] = wl1271_rate_to_idx_5ghz
2248};
2249
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002250static const struct ieee80211_ops wl1271_ops = {
2251 .start = wl1271_op_start,
2252 .stop = wl1271_op_stop,
2253 .add_interface = wl1271_op_add_interface,
2254 .remove_interface = wl1271_op_remove_interface,
2255 .config = wl1271_op_config,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002256 .prepare_multicast = wl1271_op_prepare_multicast,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002257 .configure_filter = wl1271_op_configure_filter,
2258 .tx = wl1271_op_tx,
2259 .set_key = wl1271_op_set_key,
2260 .hw_scan = wl1271_op_hw_scan,
2261 .bss_info_changed = wl1271_op_bss_info_changed,
2262 .set_rts_threshold = wl1271_op_set_rts_threshold,
Kalle Valoc6999d82010-02-18 13:25:41 +02002263 .conf_tx = wl1271_op_conf_tx,
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03002264 .get_tsf = wl1271_op_get_tsf,
John W. Linvilleece550d2010-07-28 16:41:06 -04002265 .get_survey = wl1271_op_get_survey,
Kalle Valoc8c90872010-02-18 13:25:53 +02002266 CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002267};
2268
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002269
2270u8 wl1271_rate_to_idx(struct wl1271 *wl, int rate)
2271{
2272 u8 idx;
2273
2274 BUG_ON(wl->band >= sizeof(wl1271_band_rate_to_idx)/sizeof(u8 *));
2275
2276 if (unlikely(rate >= CONF_HW_RXTX_RATE_MAX)) {
2277 wl1271_error("Illegal RX rate from HW: %d", rate);
2278 return 0;
2279 }
2280
2281 idx = wl1271_band_rate_to_idx[wl->band][rate];
2282 if (unlikely(idx == CONF_HW_RXTX_RATE_UNSUPPORTED)) {
2283 wl1271_error("Unsupported RX rate from HW: %d", rate);
2284 return 0;
2285 }
2286
2287 return idx;
2288}
2289
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002290static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
2291 struct device_attribute *attr,
2292 char *buf)
2293{
2294 struct wl1271 *wl = dev_get_drvdata(dev);
2295 ssize_t len;
2296
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02002297 len = PAGE_SIZE;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002298
2299 mutex_lock(&wl->mutex);
2300 len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
2301 wl->sg_enabled);
2302 mutex_unlock(&wl->mutex);
2303
2304 return len;
2305
2306}
2307
2308static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
2309 struct device_attribute *attr,
2310 const char *buf, size_t count)
2311{
2312 struct wl1271 *wl = dev_get_drvdata(dev);
2313 unsigned long res;
2314 int ret;
2315
2316 ret = strict_strtoul(buf, 10, &res);
2317
2318 if (ret < 0) {
2319 wl1271_warning("incorrect value written to bt_coex_mode");
2320 return count;
2321 }
2322
2323 mutex_lock(&wl->mutex);
2324
2325 res = !!res;
2326
2327 if (res == wl->sg_enabled)
2328 goto out;
2329
2330 wl->sg_enabled = res;
2331
2332 if (wl->state == WL1271_STATE_OFF)
2333 goto out;
2334
2335 ret = wl1271_ps_elp_wakeup(wl, false);
2336 if (ret < 0)
2337 goto out;
2338
2339 wl1271_acx_sg_enable(wl, wl->sg_enabled);
2340 wl1271_ps_elp_sleep(wl);
2341
2342 out:
2343 mutex_unlock(&wl->mutex);
2344 return count;
2345}
2346
2347static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
2348 wl1271_sysfs_show_bt_coex_state,
2349 wl1271_sysfs_store_bt_coex_state);
2350
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002351static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
2352 struct device_attribute *attr,
2353 char *buf)
2354{
2355 struct wl1271 *wl = dev_get_drvdata(dev);
2356 ssize_t len;
2357
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02002358 len = PAGE_SIZE;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002359
2360 mutex_lock(&wl->mutex);
2361 if (wl->hw_pg_ver >= 0)
2362 len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
2363 else
2364 len = snprintf(buf, len, "n/a\n");
2365 mutex_unlock(&wl->mutex);
2366
2367 return len;
2368}
2369
2370static DEVICE_ATTR(hw_pg_ver, S_IRUGO | S_IWUSR,
2371 wl1271_sysfs_show_hw_pg_ver, NULL);
2372
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002373int wl1271_register_hw(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002374{
2375 int ret;
2376
2377 if (wl->mac80211_registered)
2378 return 0;
2379
2380 SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
2381
2382 ret = ieee80211_register_hw(wl->hw);
2383 if (ret < 0) {
2384 wl1271_error("unable to register mac80211 hw: %d", ret);
2385 return ret;
2386 }
2387
2388 wl->mac80211_registered = true;
2389
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03002390 register_netdevice_notifier(&wl1271_dev_notifier);
2391
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002392 wl1271_notice("loaded");
2393
2394 return 0;
2395}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002396EXPORT_SYMBOL_GPL(wl1271_register_hw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002397
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002398void wl1271_unregister_hw(struct wl1271 *wl)
2399{
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03002400 unregister_netdevice_notifier(&wl1271_dev_notifier);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002401 ieee80211_unregister_hw(wl->hw);
2402 wl->mac80211_registered = false;
2403
2404}
2405EXPORT_SYMBOL_GPL(wl1271_unregister_hw);
2406
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002407int wl1271_init_ieee80211(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002408{
Juuso Oikarinen7a557242010-09-27 12:42:07 +02002409 static const u32 cipher_suites[] = {
2410 WLAN_CIPHER_SUITE_WEP40,
2411 WLAN_CIPHER_SUITE_WEP104,
2412 WLAN_CIPHER_SUITE_TKIP,
2413 WLAN_CIPHER_SUITE_CCMP,
2414 WL1271_CIPHER_SUITE_GEM,
2415 };
2416
Juuso Oikarinen1e2b7972009-10-08 21:56:20 +03002417 /* The tx descriptor buffer and the TKIP space. */
2418 wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
2419 sizeof(struct wl1271_tx_hw_descr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002420
2421 /* unit us */
2422 /* FIXME: find a proper value */
2423 wl->hw->channel_change_time = 10000;
Juuso Oikarinen50c500a2010-04-01 11:38:22 +03002424 wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002425
2426 wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
Juuso Oikarinen03442a32009-11-23 23:22:14 +02002427 IEEE80211_HW_BEACON_FILTER |
Juuso Oikarinen0a343322010-02-22 08:38:41 +02002428 IEEE80211_HW_SUPPORTS_PS |
Kalle Valo4695dc92010-03-18 12:26:38 +02002429 IEEE80211_HW_SUPPORTS_UAPSD |
Juuso Oikarinena9af0922010-03-26 12:53:30 +02002430 IEEE80211_HW_HAS_RATE_CONTROL |
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03002431 IEEE80211_HW_CONNECTION_MONITOR |
2432 IEEE80211_HW_SUPPORTS_CQM_RSSI;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002433
Juuso Oikarinen7a557242010-09-27 12:42:07 +02002434 wl->hw->wiphy->cipher_suites = cipher_suites;
2435 wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
2436
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02002437 wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
2438 BIT(NL80211_IFTYPE_ADHOC);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002439 wl->hw->wiphy->max_scan_ssids = 1;
2440 wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz;
Juuso Oikarinen11eb5422010-08-24 06:28:03 +03002441 wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz;
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002442
Kalle Valo12bd8942010-03-18 12:26:33 +02002443 wl->hw->queues = 4;
Juuso Oikarinen31627dc2010-03-26 12:53:12 +02002444 wl->hw->max_rates = 1;
Kalle Valo12bd8942010-03-18 12:26:33 +02002445
Teemu Paasikivi8197b712010-02-22 08:38:23 +02002446 SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002447
2448 return 0;
2449}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002450EXPORT_SYMBOL_GPL(wl1271_init_ieee80211);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002451
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002452#define WL1271_DEFAULT_CHANNEL 0
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002453
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002454struct ieee80211_hw *wl1271_alloc_hw(void)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002455{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002456 struct ieee80211_hw *hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002457 struct platform_device *plat_dev = NULL;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002458 struct wl1271 *wl;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002459 int i, ret;
Ido Yariv1f37cbc2010-09-30 13:28:27 +02002460 unsigned int order;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002461
2462 hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
2463 if (!hw) {
2464 wl1271_error("could not alloc ieee80211_hw");
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002465 ret = -ENOMEM;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002466 goto err_hw_alloc;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002467 }
2468
Julia Lawall929ebd32010-05-15 23:16:39 +02002469 plat_dev = kmemdup(&wl1271_device, sizeof(wl1271_device), GFP_KERNEL);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002470 if (!plat_dev) {
2471 wl1271_error("could not allocate platform_device");
2472 ret = -ENOMEM;
2473 goto err_plat_alloc;
2474 }
2475
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002476 wl = hw->priv;
2477 memset(wl, 0, sizeof(*wl));
2478
Juuso Oikarinen01c09162009-10-13 12:47:55 +03002479 INIT_LIST_HEAD(&wl->list);
2480
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002481 wl->hw = hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002482 wl->plat_dev = plat_dev;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002483
2484 skb_queue_head_init(&wl->tx_queue);
2485
Juuso Oikarinen37b70a82009-10-08 21:56:21 +03002486 INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03002487 INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work);
Juuso Oikarinen117b38d2010-09-30 10:43:28 +02002488 INIT_WORK(&wl->irq_work, wl1271_irq_work);
2489 INIT_WORK(&wl->tx_work, wl1271_tx_work);
2490 INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
2491 INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002492 wl->channel = WL1271_DEFAULT_CHANNEL;
Juuso Oikarinen60e84c22010-03-26 12:53:25 +02002493 wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002494 wl->default_key = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002495 wl->rx_counter = 0;
2496 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
2497 wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02002498 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002499 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
Juuso Oikarinena6fe2312009-12-11 15:40:58 +02002500 wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002501 wl->basic_rate = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02002502 wl->rate_set = CONF_TX_RATE_MASK_BASIC;
2503 wl->sta_rate_set = 0;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03002504 wl->band = IEEE80211_BAND_2GHZ;
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03002505 wl->vif = NULL;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02002506 wl->flags = 0;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002507 wl->sg_enabled = true;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002508 wl->hw_pg_ver = -1;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002509
Juuso Oikarinenbe7078c2009-10-08 21:56:26 +03002510 for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002511 wl->tx_frames[i] = NULL;
2512
2513 spin_lock_init(&wl->wl_lock);
2514
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002515 wl->state = WL1271_STATE_OFF;
2516 mutex_init(&wl->mutex);
2517
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002518 /* Apply default driver configuration. */
2519 wl1271_conf_init(wl);
2520
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002521 wl1271_debugfs_init(wl);
2522
Ido Yariv1f37cbc2010-09-30 13:28:27 +02002523 order = get_order(WL1271_AGGR_BUFFER_SIZE);
2524 wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
2525 if (!wl->aggr_buf) {
2526 ret = -ENOMEM;
2527 goto err_hw;
2528 }
2529
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002530 /* Register platform device */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002531 ret = platform_device_register(wl->plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002532 if (ret) {
2533 wl1271_error("couldn't register platform device");
Ido Yariv1f37cbc2010-09-30 13:28:27 +02002534 goto err_aggr;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002535 }
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002536 dev_set_drvdata(&wl->plat_dev->dev, wl);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002537
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002538 /* Create sysfs file to control bt coex state */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002539 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002540 if (ret < 0) {
2541 wl1271_error("failed to create sysfs file bt_coex_state");
2542 goto err_platform;
2543 }
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002544
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002545 /* Create sysfs file to get HW PG version */
2546 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
2547 if (ret < 0) {
2548 wl1271_error("failed to create sysfs file hw_pg_ver");
2549 goto err_bt_coex_state;
2550 }
2551
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002552 return hw;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002553
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002554err_bt_coex_state:
2555 device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
2556
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002557err_platform:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002558 platform_device_unregister(wl->plat_dev);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002559
Ido Yariv1f37cbc2010-09-30 13:28:27 +02002560err_aggr:
2561 free_pages((unsigned long)wl->aggr_buf, order);
2562
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002563err_hw:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002564 wl1271_debugfs_exit(wl);
2565 kfree(plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002566
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002567err_plat_alloc:
2568 ieee80211_free_hw(hw);
2569
2570err_hw_alloc:
2571
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002572 return ERR_PTR(ret);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002573}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002574EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002575
2576int wl1271_free_hw(struct wl1271 *wl)
2577{
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002578 platform_device_unregister(wl->plat_dev);
Ido Yariv1f37cbc2010-09-30 13:28:27 +02002579 free_pages((unsigned long)wl->aggr_buf,
2580 get_order(WL1271_AGGR_BUFFER_SIZE));
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002581 kfree(wl->plat_dev);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002582
2583 wl1271_debugfs_exit(wl);
2584
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002585 vfree(wl->fw);
2586 wl->fw = NULL;
2587 kfree(wl->nvs);
2588 wl->nvs = NULL;
2589
2590 kfree(wl->fw_status);
2591 kfree(wl->tx_res_if);
2592
2593 ieee80211_free_hw(wl->hw);
2594
2595 return 0;
2596}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002597EXPORT_SYMBOL_GPL(wl1271_free_hw);
2598
2599MODULE_LICENSE("GPL");
2600MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
2601MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");