blob: b2432dab4b51a1a4d5b8ec9252d7f0f0db022c3f [file] [log] [blame]
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001/*
2 * This file is part of wl1271
3 *
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02004 * Copyright (C) 2008-2010 Nokia Corporation
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03005 *
6 * Contact: Luciano Coelho <luciano.coelho@nokia.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 *
22 */
23
24#include <linux/module.h>
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030025#include <linux/firmware.h>
26#include <linux/delay.h>
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030027#include <linux/spi/spi.h>
28#include <linux/crc32.h>
29#include <linux/etherdevice.h>
Juuso Oikarinen1fba4972009-10-08 21:56:32 +030030#include <linux/vmalloc.h>
Juuso Oikarinena1dd8182010-03-18 12:26:31 +020031#include <linux/platform_device.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090032#include <linux/slab.h>
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030033
Shahar Levi00d20102010-11-08 11:20:10 +000034#include "wl12xx.h"
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030035#include "wl12xx_80211.h"
Shahar Levi00d20102010-11-08 11:20:10 +000036#include "reg.h"
37#include "io.h"
38#include "event.h"
39#include "tx.h"
40#include "rx.h"
41#include "ps.h"
42#include "init.h"
43#include "debugfs.h"
44#include "cmd.h"
45#include "boot.h"
46#include "testmode.h"
47#include "scan.h"
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030048
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +020049#define WL1271_BOOT_RETRIES 3
50
Juuso Oikarinen8a080482009-10-13 12:47:44 +030051static struct conf_drv_settings default_conf = {
52 .sg = {
Juuso Oikarinen1b00f542010-03-18 12:26:30 +020053 .params = {
54 [CONF_SG_BT_PER_THRESHOLD] = 7500,
55 [CONF_SG_HV3_MAX_OVERRIDE] = 0,
56 [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400,
57 [CONF_SG_BT_LOAD_RATIO] = 50,
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +030058 [CONF_SG_AUTO_PS_MODE] = 1,
Juuso Oikarinen1b00f542010-03-18 12:26:30 +020059 [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
60 [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
61 [CONF_SG_ANTENNA_CONFIGURATION] = 0,
62 [CONF_SG_BEACON_MISS_PERCENT] = 60,
63 [CONF_SG_RATE_ADAPT_THRESH] = 12,
64 [CONF_SG_RATE_ADAPT_SNR] = 0,
65 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR] = 10,
66 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR] = 30,
67 [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR] = 8,
68 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR] = 20,
69 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR] = 50,
70 /* Note: with UPSD, this should be 4 */
71 [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR] = 8,
72 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR] = 7,
73 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR] = 25,
74 [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR] = 20,
75 /* Note: with UPDS, this should be 15 */
76 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR] = 8,
77 /* Note: with UPDS, this should be 50 */
78 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR] = 40,
79 /* Note: with UPDS, this should be 10 */
80 [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR] = 20,
81 [CONF_SG_RXT] = 1200,
82 [CONF_SG_TXT] = 1000,
83 [CONF_SG_ADAPTIVE_RXT_TXT] = 1,
84 [CONF_SG_PS_POLL_TIMEOUT] = 10,
85 [CONF_SG_UPSD_TIMEOUT] = 10,
86 [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR] = 7,
87 [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR] = 15,
88 [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR] = 15,
89 [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR] = 8,
90 [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR] = 20,
91 [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR] = 15,
92 [CONF_SG_WLAN_ACTIVE_BT_ACL_MIN_BR] = 20,
93 [CONF_SG_WLAN_ACTIVE_BT_ACL_MAX_BR] = 50,
94 [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_BR] = 10,
95 [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
96 [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP] = 800,
97 [CONF_SG_PASSIVE_SCAN_A2DP_BT_TIME] = 75,
98 [CONF_SG_PASSIVE_SCAN_A2DP_WLAN_TIME] = 15,
99 [CONF_SG_HV3_MAX_SERVED] = 6,
100 [CONF_SG_DHCP_TIME] = 5000,
101 [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
102 },
103 .state = CONF_SG_PROTECTIVE,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300104 },
105 .rx = {
106 .rx_msdu_life_time = 512000,
107 .packet_detection_threshold = 0,
108 .ps_poll_timeout = 15,
109 .upsd_timeout = 15,
110 .rts_threshold = 2347,
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200111 .rx_cca_threshold = 0,
112 .irq_blk_threshold = 0xFFFF,
113 .irq_pkt_threshold = 0,
114 .irq_timeout = 600,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300115 .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY,
116 },
117 .tx = {
118 .tx_energy_detection = 0,
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 Oikarinen644a4862010-10-05 13:11:56 +0200245 .rf = {
246 .tx_per_channel_power_compensation_2 = {
247 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
248 },
249 .tx_per_channel_power_compensation_5 = {
250 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
251 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
252 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
253 },
254 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300255};
256
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200257static void __wl1271_op_remove_interface(struct wl1271 *wl);
258
259
Juuso Oikarinena1dd8182010-03-18 12:26:31 +0200260static void wl1271_device_release(struct device *dev)
261{
262
263}
264
265static struct platform_device wl1271_device = {
266 .name = "wl1271",
267 .id = -1,
268
269 /* device model insists to have a release function */
270 .dev = {
271 .release = wl1271_device_release,
272 },
273};
274
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300275static LIST_HEAD(wl_list);
276
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300277static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
278 void *arg)
279{
280 struct net_device *dev = arg;
281 struct wireless_dev *wdev;
282 struct wiphy *wiphy;
283 struct ieee80211_hw *hw;
284 struct wl1271 *wl;
285 struct wl1271 *wl_temp;
286 int ret = 0;
287
288 /* Check that this notification is for us. */
289 if (what != NETDEV_CHANGE)
290 return NOTIFY_DONE;
291
292 wdev = dev->ieee80211_ptr;
293 if (wdev == NULL)
294 return NOTIFY_DONE;
295
296 wiphy = wdev->wiphy;
297 if (wiphy == NULL)
298 return NOTIFY_DONE;
299
300 hw = wiphy_priv(wiphy);
301 if (hw == NULL)
302 return NOTIFY_DONE;
303
304 wl_temp = hw->priv;
305 list_for_each_entry(wl, &wl_list, list) {
306 if (wl == wl_temp)
307 break;
308 }
309 if (wl != wl_temp)
310 return NOTIFY_DONE;
311
312 mutex_lock(&wl->mutex);
313
314 if (wl->state == WL1271_STATE_OFF)
315 goto out;
316
317 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
318 goto out;
319
320 ret = wl1271_ps_elp_wakeup(wl, false);
321 if (ret < 0)
322 goto out;
323
324 if ((dev->operstate == IF_OPER_UP) &&
325 !test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags)) {
326 wl1271_cmd_set_sta_state(wl);
327 wl1271_info("Association completed.");
328 }
329
330 wl1271_ps_elp_sleep(wl);
331
332out:
333 mutex_unlock(&wl->mutex);
334
335 return NOTIFY_OK;
336}
337
Juuso Oikarinenb7417d92010-11-10 11:27:19 +0100338static int wl1271_reg_notify(struct wiphy *wiphy,
Luciano Coelho573c67c2010-11-26 13:44:59 +0200339 struct regulatory_request *request)
340{
341 struct wl1271 *wl = wiphy_to_ieee80211_hw(wiphy)->priv;
Juuso Oikarinenb7417d92010-11-10 11:27:19 +0100342 struct ieee80211_supported_band *band;
343 struct ieee80211_channel *ch;
344 int i;
345
346 band = wiphy->bands[IEEE80211_BAND_5GHZ];
347 for (i = 0; i < band->n_channels; i++) {
348 ch = &band->channels[i];
349 if (ch->flags & IEEE80211_CHAN_DISABLED)
350 continue;
351
Luciano Coelho573c67c2010-11-26 13:44:59 +0200352 if (!wl->enable_11a) {
353 ch->flags |= IEEE80211_CHAN_DISABLED;
354 continue;
355 }
356
Juuso Oikarinenb7417d92010-11-10 11:27:19 +0100357 if (ch->flags & IEEE80211_CHAN_RADAR)
358 ch->flags |= IEEE80211_CHAN_NO_IBSS |
359 IEEE80211_CHAN_PASSIVE_SCAN;
360
361 }
362
363 return 0;
364}
365
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300366static void wl1271_conf_init(struct wl1271 *wl)
367{
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300368
369 /*
370 * This function applies the default configuration to the driver. This
371 * function is invoked upon driver load (spi probe.)
372 *
373 * The configuration is stored in a run-time structure in order to
374 * facilitate for run-time adjustment of any of the parameters. Making
375 * changes to the configuration structure will apply the new values on
376 * the next interface up (wl1271_op_start.)
377 */
378
379 /* apply driver default configuration */
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300380 memcpy(&wl->conf, &default_conf, sizeof(default_conf));
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300381}
382
383
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300384static int wl1271_plt_init(struct wl1271 *wl)
385{
Luciano Coelho12419cc2010-02-18 13:25:44 +0200386 struct conf_tx_ac_category *conf_ac;
387 struct conf_tx_tid *conf_tid;
388 int ret, i;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300389
Luciano Coelho98b5dd52009-11-23 23:22:17 +0200390 ret = wl1271_cmd_general_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200391 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200392 return ret;
393
Luciano Coelho98b5dd52009-11-23 23:22:17 +0200394 ret = wl1271_cmd_radio_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200395 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200396 return ret;
397
Juuso Oikarinen644a4862010-10-05 13:11:56 +0200398 ret = wl1271_cmd_ext_radio_parms(wl);
399 if (ret < 0)
400 return ret;
401
Luciano Coelho12419cc2010-02-18 13:25:44 +0200402 ret = wl1271_init_templates_config(wl);
403 if (ret < 0)
404 return ret;
405
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300406 ret = wl1271_acx_init_mem_config(wl);
407 if (ret < 0)
408 return ret;
409
Luciano Coelho12419cc2010-02-18 13:25:44 +0200410 /* PHY layer config */
411 ret = wl1271_init_phy_config(wl);
412 if (ret < 0)
413 goto out_free_memmap;
414
415 ret = wl1271_acx_dco_itrim_params(wl);
416 if (ret < 0)
417 goto out_free_memmap;
418
419 /* Initialize connection monitoring thresholds */
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +0200420 ret = wl1271_acx_conn_monit_params(wl, false);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200421 if (ret < 0)
422 goto out_free_memmap;
423
424 /* Bluetooth WLAN coexistence */
425 ret = wl1271_init_pta(wl);
426 if (ret < 0)
427 goto out_free_memmap;
428
429 /* Energy detection */
430 ret = wl1271_init_energy_detection(wl);
431 if (ret < 0)
432 goto out_free_memmap;
433
434 /* Default fragmentation threshold */
Arik Nemtsov68d069c2010-11-08 10:51:07 +0100435 ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200436 if (ret < 0)
437 goto out_free_memmap;
438
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200439 /* Default TID/AC configuration */
440 BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200441 for (i = 0; i < wl->conf.tx.tid_conf_count; i++) {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200442 conf_ac = &wl->conf.tx.ac_conf[i];
443 ret = wl1271_acx_ac_cfg(wl, conf_ac->ac, conf_ac->cw_min,
444 conf_ac->cw_max, conf_ac->aifsn,
445 conf_ac->tx_op_limit);
446 if (ret < 0)
447 goto out_free_memmap;
448
Luciano Coelho12419cc2010-02-18 13:25:44 +0200449 conf_tid = &wl->conf.tx.tid_conf[i];
450 ret = wl1271_acx_tid_cfg(wl, conf_tid->queue_id,
451 conf_tid->channel_type,
452 conf_tid->tsid,
453 conf_tid->ps_scheme,
454 conf_tid->ack_policy,
455 conf_tid->apsd_conf[0],
456 conf_tid->apsd_conf[1]);
457 if (ret < 0)
458 goto out_free_memmap;
459 }
460
Luciano Coelho12419cc2010-02-18 13:25:44 +0200461 /* Enable data path */
Luciano Coelho94210892009-12-11 15:40:55 +0200462 ret = wl1271_cmd_data_path(wl, 1);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300463 if (ret < 0)
Luciano Coelho12419cc2010-02-18 13:25:44 +0200464 goto out_free_memmap;
465
466 /* Configure for CAM power saving (ie. always active) */
467 ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
468 if (ret < 0)
469 goto out_free_memmap;
470
471 /* configure PM */
472 ret = wl1271_acx_pm_config(wl);
473 if (ret < 0)
474 goto out_free_memmap;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300475
476 return 0;
Luciano Coelho12419cc2010-02-18 13:25:44 +0200477
478 out_free_memmap:
479 kfree(wl->target_mem_map);
480 wl->target_mem_map = NULL;
481
482 return ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300483}
484
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300485static void wl1271_fw_status(struct wl1271 *wl,
486 struct wl1271_fw_status *status)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300487{
Juuso Oikarinenac5e1e32010-02-22 08:38:38 +0200488 struct timespec ts;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300489 u32 total = 0;
490 int i;
491
Teemu Paasikivi09a9c2b2010-02-22 08:38:28 +0200492 wl1271_raw_read(wl, FW_STATUS_ADDR, status, sizeof(*status), false);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300493
494 wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
495 "drv_rx_counter = %d, tx_results_counter = %d)",
496 status->intr,
497 status->fw_rx_counter,
498 status->drv_rx_counter,
499 status->tx_results_counter);
500
501 /* update number of available TX blocks */
502 for (i = 0; i < NUM_TX_QUEUES; i++) {
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300503 u32 cnt = le32_to_cpu(status->tx_released_blks[i]) -
504 wl->tx_blocks_freed[i];
505
506 wl->tx_blocks_freed[i] =
507 le32_to_cpu(status->tx_released_blks[i]);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300508 wl->tx_blocks_available += cnt;
509 total += cnt;
510 }
511
Ido Yariva5225502010-10-12 14:49:10 +0200512 /* if more blocks are available now, tx work can be scheduled */
513 if (total)
514 clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300515
516 /* update the host-chipset time offset */
Juuso Oikarinenac5e1e32010-02-22 08:38:38 +0200517 getnstimeofday(&ts);
518 wl->time_offset = (timespec_to_ns(&ts) >> 10) -
519 (s64)le32_to_cpu(status->fw_localtime);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300520}
521
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200522#define WL1271_IRQ_MAX_LOOPS 10
523
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300524static void wl1271_irq_work(struct work_struct *work)
525{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300526 int ret;
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300527 u32 intr;
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200528 int loopcount = WL1271_IRQ_MAX_LOOPS;
529 unsigned long flags;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300530 struct wl1271 *wl =
531 container_of(work, struct wl1271, irq_work);
532
533 mutex_lock(&wl->mutex);
534
535 wl1271_debug(DEBUG_IRQ, "IRQ work");
536
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200537 if (unlikely(wl->state == WL1271_STATE_OFF))
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300538 goto out;
539
540 ret = wl1271_ps_elp_wakeup(wl, true);
541 if (ret < 0)
542 goto out;
543
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200544 spin_lock_irqsave(&wl->wl_lock, flags);
545 while (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags) && loopcount) {
546 clear_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags);
547 spin_unlock_irqrestore(&wl->wl_lock, flags);
548 loopcount--;
549
550 wl1271_fw_status(wl, wl->fw_status);
551 intr = le32_to_cpu(wl->fw_status->intr);
552 if (!intr) {
553 wl1271_debug(DEBUG_IRQ, "Zero interrupt received.");
Dan Carpentercdd08642010-05-08 18:25:17 +0200554 spin_lock_irqsave(&wl->wl_lock, flags);
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200555 continue;
556 }
557
558 intr &= WL1271_INTR_MASK;
559
Eliad Pellerccc83b02010-10-27 14:09:57 +0200560 if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) {
561 wl1271_error("watchdog interrupt received! "
562 "starting recovery.");
563 ieee80211_queue_work(wl->hw, &wl->recovery_work);
564
565 /* restarting the chip. ignore any other interrupt. */
566 goto out;
567 }
568
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200569 if (intr & WL1271_ACX_INTR_DATA) {
570 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
571
572 /* check for tx results */
573 if (wl->fw_status->tx_results_counter !=
574 (wl->tx_results_count & 0xff))
575 wl1271_tx_complete(wl);
576
Ido Yariva5225502010-10-12 14:49:10 +0200577 /* Check if any tx blocks were freed */
578 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
579 !skb_queue_empty(&wl->tx_queue)) {
580 /*
581 * In order to avoid starvation of the TX path,
582 * call the work function directly.
583 */
584 wl1271_tx_work_locked(wl);
585 }
586
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200587 wl1271_rx(wl, wl->fw_status);
588 }
589
590 if (intr & WL1271_ACX_INTR_EVENT_A) {
591 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
592 wl1271_event_handle(wl, 0);
593 }
594
595 if (intr & WL1271_ACX_INTR_EVENT_B) {
596 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
597 wl1271_event_handle(wl, 1);
598 }
599
600 if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
601 wl1271_debug(DEBUG_IRQ,
602 "WL1271_ACX_INTR_INIT_COMPLETE");
603
604 if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
605 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
606
607 spin_lock_irqsave(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300608 }
609
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200610 if (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags))
611 ieee80211_queue_work(wl->hw, &wl->irq_work);
612 else
613 clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
614 spin_unlock_irqrestore(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300615
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300616 wl1271_ps_elp_sleep(wl);
617
618out:
619 mutex_unlock(&wl->mutex);
620}
621
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300622static int wl1271_fetch_firmware(struct wl1271 *wl)
623{
624 const struct firmware *fw;
625 int ret;
626
Teemu Paasikivi8197b712010-02-22 08:38:23 +0200627 ret = request_firmware(&fw, WL1271_FW_NAME, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300628
629 if (ret < 0) {
630 wl1271_error("could not get firmware: %d", ret);
631 return ret;
632 }
633
634 if (fw->size % 4) {
635 wl1271_error("firmware size is not multiple of 32 bits: %zu",
636 fw->size);
637 ret = -EILSEQ;
638 goto out;
639 }
640
641 wl->fw_len = fw->size;
Juuso Oikarinen1fba4972009-10-08 21:56:32 +0300642 wl->fw = vmalloc(wl->fw_len);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300643
644 if (!wl->fw) {
645 wl1271_error("could not allocate memory for the firmware");
646 ret = -ENOMEM;
647 goto out;
648 }
649
650 memcpy(wl->fw, fw->data, wl->fw_len);
651
652 ret = 0;
653
654out:
655 release_firmware(fw);
656
657 return ret;
658}
659
660static int wl1271_fetch_nvs(struct wl1271 *wl)
661{
662 const struct firmware *fw;
663 int ret;
664
Teemu Paasikivi8197b712010-02-22 08:38:23 +0200665 ret = request_firmware(&fw, WL1271_NVS_NAME, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300666
667 if (ret < 0) {
668 wl1271_error("could not get nvs file: %d", ret);
669 return ret;
670 }
671
Julia Lawall929ebd32010-05-15 23:16:39 +0200672 wl->nvs = kmemdup(fw->data, sizeof(struct wl1271_nvs_file), GFP_KERNEL);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300673
674 if (!wl->nvs) {
675 wl1271_error("could not allocate memory for the nvs file");
676 ret = -ENOMEM;
677 goto out;
678 }
679
Juuso Oikarinen02fabb02010-08-19 04:41:15 +0200680 wl->nvs_len = fw->size;
681
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300682out:
683 release_firmware(fw);
684
685 return ret;
686}
687
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200688static void wl1271_recovery_work(struct work_struct *work)
689{
690 struct wl1271 *wl =
691 container_of(work, struct wl1271, recovery_work);
692
693 mutex_lock(&wl->mutex);
694
695 if (wl->state != WL1271_STATE_ON)
696 goto out;
697
698 wl1271_info("Hardware recovery in progress.");
699
Juuso Oikarinend25611d2010-09-30 10:43:27 +0200700 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
701 ieee80211_connection_loss(wl->vif);
702
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200703 /* reboot the chipset */
704 __wl1271_op_remove_interface(wl);
705 ieee80211_restart_hw(wl->hw);
706
707out:
708 mutex_unlock(&wl->mutex);
709}
710
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300711static void wl1271_fw_wakeup(struct wl1271 *wl)
712{
713 u32 elp_reg;
714
715 elp_reg = ELPCTRL_WAKE_UP;
Juuso Oikarinen74621412009-10-12 15:08:54 +0300716 wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300717}
718
719static int wl1271_setup(struct wl1271 *wl)
720{
721 wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL);
722 if (!wl->fw_status)
723 return -ENOMEM;
724
725 wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
726 if (!wl->tx_res_if) {
727 kfree(wl->fw_status);
728 return -ENOMEM;
729 }
730
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300731 return 0;
732}
733
734static int wl1271_chip_wakeup(struct wl1271 *wl)
735{
Juuso Oikarinen451de972009-10-12 15:08:46 +0300736 struct wl1271_partition_set partition;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300737 int ret = 0;
738
Juuso Oikarinen01ac17e2009-12-11 15:41:02 +0200739 msleep(WL1271_PRE_POWER_ON_SLEEP);
Ohad Ben-Cohen2cc78ff2010-09-16 01:22:04 +0200740 ret = wl1271_power_on(wl);
741 if (ret < 0)
742 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300743 msleep(WL1271_POWER_ON_SLEEP);
Teemu Paasikivi9b280722010-02-18 13:25:56 +0200744 wl1271_io_reset(wl);
745 wl1271_io_init(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300746
747 /* We don't need a real memory partition here, because we only want
748 * to use the registers at this point. */
Juuso Oikarinen451de972009-10-12 15:08:46 +0300749 memset(&partition, 0, sizeof(partition));
750 partition.reg.start = REGISTERS_BASE;
751 partition.reg.size = REGISTERS_DOWN_SIZE;
752 wl1271_set_partition(wl, &partition);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300753
754 /* ELP module wake up */
755 wl1271_fw_wakeup(wl);
756
757 /* whal_FwCtrl_BootSm() */
758
759 /* 0. read chip id from CHIP_ID */
Teemu Paasikivi7b048c52010-02-18 13:25:55 +0200760 wl->chip.id = wl1271_read32(wl, CHIP_ID_B);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300761
762 /* 1. check if chip id is valid */
763
764 switch (wl->chip.id) {
765 case CHIP_ID_1271_PG10:
766 wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
767 wl->chip.id);
768
769 ret = wl1271_setup(wl);
770 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200771 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300772 break;
773 case CHIP_ID_1271_PG20:
774 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
775 wl->chip.id);
776
777 ret = wl1271_setup(wl);
778 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200779 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300780 break;
781 default:
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200782 wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300783 ret = -ENODEV;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200784 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300785 }
786
787 if (wl->fw == NULL) {
788 ret = wl1271_fetch_firmware(wl);
789 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200790 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300791 }
792
793 /* No NVS from netlink, try to get it from the filesystem */
794 if (wl->nvs == NULL) {
795 ret = wl1271_fetch_nvs(wl);
796 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200797 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300798 }
799
800out:
801 return ret;
802}
803
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300804int wl1271_plt_start(struct wl1271 *wl)
805{
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200806 int retries = WL1271_BOOT_RETRIES;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300807 int ret;
808
809 mutex_lock(&wl->mutex);
810
811 wl1271_notice("power up");
812
813 if (wl->state != WL1271_STATE_OFF) {
814 wl1271_error("cannot go into PLT state because not "
815 "in off state: %d", wl->state);
816 ret = -EBUSY;
817 goto out;
818 }
819
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200820 while (retries) {
821 retries--;
822 ret = wl1271_chip_wakeup(wl);
823 if (ret < 0)
824 goto power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300825
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200826 ret = wl1271_boot(wl);
827 if (ret < 0)
828 goto power_off;
829
830 ret = wl1271_plt_init(wl);
831 if (ret < 0)
832 goto irq_disable;
833
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200834 wl->state = WL1271_STATE_PLT;
835 wl1271_notice("firmware booted in PLT mode (%s)",
836 wl->chip.fw_ver);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300837 goto out;
838
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200839irq_disable:
840 wl1271_disable_interrupts(wl);
841 mutex_unlock(&wl->mutex);
842 /* Unlocking the mutex in the middle of handling is
843 inherently unsafe. In this case we deem it safe to do,
844 because we need to let any possibly pending IRQ out of
845 the system (and while we are WL1271_STATE_OFF the IRQ
846 work function will not do anything.) Also, any other
847 possible concurrent operations will fail due to the
848 current state, hence the wl1271 struct should be safe. */
849 cancel_work_sync(&wl->irq_work);
850 mutex_lock(&wl->mutex);
851power_off:
852 wl1271_power_off(wl);
853 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300854
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200855 wl1271_error("firmware boot in PLT mode failed despite %d retries",
856 WL1271_BOOT_RETRIES);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300857out:
858 mutex_unlock(&wl->mutex);
859
860 return ret;
861}
862
863int wl1271_plt_stop(struct wl1271 *wl)
864{
865 int ret = 0;
866
867 mutex_lock(&wl->mutex);
868
869 wl1271_notice("power down");
870
871 if (wl->state != WL1271_STATE_PLT) {
872 wl1271_error("cannot power down because not in PLT "
873 "state: %d", wl->state);
874 ret = -EBUSY;
875 goto out;
876 }
877
878 wl1271_disable_interrupts(wl);
879 wl1271_power_off(wl);
880
881 wl->state = WL1271_STATE_OFF;
Luciano Coelhobd5ea182009-10-13 12:47:58 +0300882 wl->rx_counter = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300883
884out:
885 mutex_unlock(&wl->mutex);
886
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +0200887 cancel_work_sync(&wl->irq_work);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200888 cancel_work_sync(&wl->recovery_work);
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +0200889
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300890 return ret;
891}
892
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300893static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
894{
895 struct wl1271 *wl = hw->priv;
Juuso Oikarinen830fb672009-12-11 15:41:06 +0200896 struct ieee80211_conf *conf = &hw->conf;
897 struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
898 struct ieee80211_sta *sta = txinfo->control.sta;
899 unsigned long flags;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300900
Shahar Levi18357852010-10-13 16:09:41 +0200901 /*
902 * peek into the rates configured in the STA entry.
903 * The rates set after connection stage, The first block only BG sets:
904 * the compare is for bit 0-16 of sta_rate_set. The second block add
905 * HT rates in case of HT supported.
906 */
Juuso Oikarinen830fb672009-12-11 15:41:06 +0200907 spin_lock_irqsave(&wl->wl_lock, flags);
Shahar Levi18357852010-10-13 16:09:41 +0200908 if (sta &&
909 (sta->supp_rates[conf->channel->band] !=
910 (wl->sta_rate_set & HW_BG_RATES_MASK))) {
Juuso Oikarinen830fb672009-12-11 15:41:06 +0200911 wl->sta_rate_set = sta->supp_rates[conf->channel->band];
912 set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags);
913 }
Shahar Levi18357852010-10-13 16:09:41 +0200914
Shahar Levi00d20102010-11-08 11:20:10 +0000915#ifdef CONFIG_WL12XX_HT
Shahar Levi18357852010-10-13 16:09:41 +0200916 if (sta &&
917 sta->ht_cap.ht_supported &&
918 ((wl->sta_rate_set >> HW_HT_RATES_OFFSET) !=
919 sta->ht_cap.mcs.rx_mask[0])) {
920 /* Clean MCS bits before setting them */
921 wl->sta_rate_set &= HW_BG_RATES_MASK;
922 wl->sta_rate_set |=
923 (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET);
924 set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags);
925 }
926#endif
Juuso Oikarinen830fb672009-12-11 15:41:06 +0200927 spin_unlock_irqrestore(&wl->wl_lock, flags);
928
929 /* queue the packet */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300930 skb_queue_tail(&wl->tx_queue, skb);
931
932 /*
933 * The chip specific setup must run before the first TX packet -
934 * before that, the tx_work will not be initialized!
935 */
936
Ido Yariva5225502010-10-12 14:49:10 +0200937 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
938 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300939
940 /*
941 * The workqueue is slow to process the tx_queue and we need stop
942 * the queue here, otherwise the queue will get too long.
943 */
Juuso Oikarinen06f7bc72010-02-22 08:38:33 +0200944 if (skb_queue_len(&wl->tx_queue) >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
945 wl1271_debug(DEBUG_TX, "op_tx: stopping queues");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300946
Juuso Oikarinen06f7bc72010-02-22 08:38:33 +0200947 spin_lock_irqsave(&wl->wl_lock, flags);
948 ieee80211_stop_queues(wl->hw);
Juuso Oikarinen71449f82009-12-11 15:41:07 +0200949 set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
Juuso Oikarinen06f7bc72010-02-22 08:38:33 +0200950 spin_unlock_irqrestore(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300951 }
952
953 return NETDEV_TX_OK;
954}
955
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300956static struct notifier_block wl1271_dev_notifier = {
957 .notifier_call = wl1271_dev_notify,
958};
959
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300960static int wl1271_op_start(struct ieee80211_hw *hw)
961{
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200962 wl1271_debug(DEBUG_MAC80211, "mac80211 start");
963
964 /*
965 * We have to delay the booting of the hardware because
966 * we need to know the local MAC address before downloading and
967 * initializing the firmware. The MAC address cannot be changed
968 * after boot, and without the proper MAC address, the firmware
969 * will not function properly.
970 *
971 * The MAC address is first known when the corresponding interface
972 * is added. That is where we will initialize the hardware.
973 */
974
975 return 0;
976}
977
978static void wl1271_op_stop(struct ieee80211_hw *hw)
979{
980 wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
981}
982
983static int wl1271_op_add_interface(struct ieee80211_hw *hw,
984 struct ieee80211_vif *vif)
985{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300986 struct wl1271 *wl = hw->priv;
John W. Linvilleac01e942010-07-28 17:09:41 -0400987 struct wiphy *wiphy = hw->wiphy;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200988 int retries = WL1271_BOOT_RETRIES;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300989 int ret = 0;
Eliad Peller71125ab2010-10-28 21:46:43 +0200990 bool booted = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300991
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200992 wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
993 vif->type, vif->addr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300994
995 mutex_lock(&wl->mutex);
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200996 if (wl->vif) {
Eliad Peller71125ab2010-10-28 21:46:43 +0200997 wl1271_debug(DEBUG_MAC80211,
998 "multiple vifs are not supported yet");
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +0200999 ret = -EBUSY;
1000 goto out;
1001 }
1002
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001003 switch (vif->type) {
1004 case NL80211_IFTYPE_STATION:
1005 wl->bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001006 wl->set_bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001007 break;
1008 case NL80211_IFTYPE_ADHOC:
1009 wl->bss_type = BSS_TYPE_IBSS;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001010 wl->set_bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001011 break;
1012 default:
1013 ret = -EOPNOTSUPP;
1014 goto out;
1015 }
1016
1017 memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001018
1019 if (wl->state != WL1271_STATE_OFF) {
1020 wl1271_error("cannot start because not in off state: %d",
1021 wl->state);
1022 ret = -EBUSY;
1023 goto out;
1024 }
1025
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001026 while (retries) {
1027 retries--;
1028 ret = wl1271_chip_wakeup(wl);
1029 if (ret < 0)
1030 goto power_off;
1031
1032 ret = wl1271_boot(wl);
1033 if (ret < 0)
1034 goto power_off;
1035
1036 ret = wl1271_hw_init(wl);
1037 if (ret < 0)
1038 goto irq_disable;
1039
Eliad Peller71125ab2010-10-28 21:46:43 +02001040 booted = true;
1041 break;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001042
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001043irq_disable:
1044 wl1271_disable_interrupts(wl);
1045 mutex_unlock(&wl->mutex);
1046 /* Unlocking the mutex in the middle of handling is
1047 inherently unsafe. In this case we deem it safe to do,
1048 because we need to let any possibly pending IRQ out of
1049 the system (and while we are WL1271_STATE_OFF the IRQ
1050 work function will not do anything.) Also, any other
1051 possible concurrent operations will fail due to the
1052 current state, hence the wl1271 struct should be safe. */
1053 cancel_work_sync(&wl->irq_work);
1054 mutex_lock(&wl->mutex);
1055power_off:
1056 wl1271_power_off(wl);
1057 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001058
Eliad Peller71125ab2010-10-28 21:46:43 +02001059 if (!booted) {
1060 wl1271_error("firmware boot failed despite %d retries",
1061 WL1271_BOOT_RETRIES);
1062 goto out;
1063 }
1064
1065 wl->vif = vif;
1066 wl->state = WL1271_STATE_ON;
1067 wl1271_info("firmware booted (%s)", wl->chip.fw_ver);
1068
1069 /* update hw/fw version info in wiphy struct */
1070 wiphy->hw_version = wl->chip.id;
1071 strncpy(wiphy->fw_version, wl->chip.fw_ver,
1072 sizeof(wiphy->fw_version));
1073
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +03001074out:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001075 mutex_unlock(&wl->mutex);
1076
Juuso Oikarineneb887df2010-07-08 17:49:58 +03001077 if (!ret)
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001078 list_add(&wl->list, &wl_list);
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001079
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001080 return ret;
1081}
1082
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001083static void __wl1271_op_remove_interface(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001084{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001085 int i;
1086
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001087 wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001088
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001089 wl1271_info("down");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001090
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001091 list_del(&wl->list);
1092
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001093 WARN_ON(wl->state != WL1271_STATE_ON);
1094
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001095 /* enable dyn ps just in case (if left on due to fw crash etc) */
Juuso Oikarinen9a547bf2010-07-08 17:50:04 +03001096 if (wl->bss_type == BSS_TYPE_STA_BSS)
Juuso Oikarinenf532be62010-07-08 17:50:05 +03001097 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001098
Luciano Coelho08688d62010-07-08 17:50:07 +03001099 if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
Luciano Coelho08688d62010-07-08 17:50:07 +03001100 wl->scan.state = WL1271_SCAN_STATE_IDLE;
1101 kfree(wl->scan.scanned_ch);
1102 wl->scan.scanned_ch = NULL;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02001103 wl->scan.req = NULL;
Juuso Oikarinen76a029f2010-07-29 04:54:45 +03001104 ieee80211_scan_completed(wl->hw, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001105 }
1106
1107 wl->state = WL1271_STATE_OFF;
1108
1109 wl1271_disable_interrupts(wl);
1110
1111 mutex_unlock(&wl->mutex);
1112
Juuso Oikarinen78abd322010-09-21 06:23:32 +02001113 cancel_delayed_work_sync(&wl->scan_complete_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001114 cancel_work_sync(&wl->irq_work);
1115 cancel_work_sync(&wl->tx_work);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001116 cancel_delayed_work_sync(&wl->pspoll_work);
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +02001117 cancel_delayed_work_sync(&wl->elp_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001118
1119 mutex_lock(&wl->mutex);
1120
1121 /* let's notify MAC80211 about the remaining pending TX frames */
Juuso Oikarinen781608c2010-05-24 11:18:17 +03001122 wl1271_tx_reset(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001123 wl1271_power_off(wl);
1124
1125 memset(wl->bssid, 0, ETH_ALEN);
1126 memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1);
1127 wl->ssid_len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001128 wl->bss_type = MAX_BSS_TYPE;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001129 wl->set_bss_type = MAX_BSS_TYPE;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001130 wl->band = IEEE80211_BAND_2GHZ;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001131
1132 wl->rx_counter = 0;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02001133 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001134 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
1135 wl->tx_blocks_available = 0;
1136 wl->tx_results_count = 0;
1137 wl->tx_packets_count = 0;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001138 wl->tx_security_last_seq = 0;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001139 wl->tx_security_seq = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001140 wl->time_offset = 0;
1141 wl->session_counter = 0;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001142 wl->rate_set = CONF_TX_RATE_MASK_BASIC;
1143 wl->sta_rate_set = 0;
1144 wl->flags = 0;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001145 wl->vif = NULL;
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001146 wl->filters = 0;
Luciano Coelhod6e19d12009-10-12 15:08:43 +03001147
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001148 for (i = 0; i < NUM_TX_QUEUES; i++)
1149 wl->tx_blocks_freed[i] = 0;
1150
1151 wl1271_debugfs_reset(wl);
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03001152
1153 kfree(wl->fw_status);
1154 wl->fw_status = NULL;
1155 kfree(wl->tx_res_if);
1156 wl->tx_res_if = NULL;
1157 kfree(wl->target_mem_map);
1158 wl->target_mem_map = NULL;
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001159}
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03001160
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001161static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
1162 struct ieee80211_vif *vif)
1163{
1164 struct wl1271 *wl = hw->priv;
1165
1166 mutex_lock(&wl->mutex);
Juuso Oikarinen67353292010-11-18 15:19:02 +02001167 /*
1168 * wl->vif can be null here if someone shuts down the interface
1169 * just when hardware recovery has been started.
1170 */
1171 if (wl->vif) {
1172 WARN_ON(wl->vif != vif);
1173 __wl1271_op_remove_interface(wl);
1174 }
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001175
Juuso Oikarinen67353292010-11-18 15:19:02 +02001176 mutex_unlock(&wl->mutex);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001177 cancel_work_sync(&wl->recovery_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001178}
1179
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001180static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
1181{
1182 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
1183 wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
1184
1185 /* combine requested filters with current filter config */
1186 filters = wl->filters | filters;
1187
1188 wl1271_debug(DEBUG_FILTERS, "RX filters set: ");
1189
1190 if (filters & FIF_PROMISC_IN_BSS) {
1191 wl1271_debug(DEBUG_FILTERS, " - FIF_PROMISC_IN_BSS");
1192 wl->rx_config &= ~CFG_UNI_FILTER_EN;
1193 wl->rx_config |= CFG_BSSID_FILTER_EN;
1194 }
1195 if (filters & FIF_BCN_PRBRESP_PROMISC) {
1196 wl1271_debug(DEBUG_FILTERS, " - FIF_BCN_PRBRESP_PROMISC");
1197 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1198 wl->rx_config &= ~CFG_SSID_FILTER_EN;
1199 }
1200 if (filters & FIF_OTHER_BSS) {
1201 wl1271_debug(DEBUG_FILTERS, " - FIF_OTHER_BSS");
1202 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1203 }
1204 if (filters & FIF_CONTROL) {
1205 wl1271_debug(DEBUG_FILTERS, " - FIF_CONTROL");
1206 wl->rx_filter |= CFG_RX_CTL_EN;
1207 }
1208 if (filters & FIF_FCSFAIL) {
1209 wl1271_debug(DEBUG_FILTERS, " - FIF_FCSFAIL");
1210 wl->rx_filter |= CFG_RX_FCS_ERROR;
1211 }
1212}
1213
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001214static int wl1271_dummy_join(struct wl1271 *wl)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001215{
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001216 int ret = 0;
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001217 /* we need to use a dummy BSSID for now */
1218 static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
1219 0xad, 0xbe, 0xef };
1220
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001221 memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
1222
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001223 /* pass through frames from all BSS */
1224 wl1271_configure_filters(wl, FIF_OTHER_BSS);
1225
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001226 ret = wl1271_cmd_join(wl, wl->set_bss_type);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001227 if (ret < 0)
1228 goto out;
1229
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001230 set_bit(WL1271_FLAG_JOINED, &wl->flags);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001231
1232out:
1233 return ret;
1234}
1235
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001236static int wl1271_join(struct wl1271 *wl, bool set_assoc)
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001237{
1238 int ret;
1239
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001240 /*
1241 * One of the side effects of the JOIN command is that is clears
1242 * WPA/WPA2 keys from the chipset. Performing a JOIN while associated
1243 * to a WPA/WPA2 access point will therefore kill the data-path.
1244 * Currently there is no supported scenario for JOIN during
1245 * association - if it becomes a supported scenario, the WPA/WPA2 keys
1246 * must be handled somehow.
1247 *
1248 */
1249 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1250 wl1271_info("JOIN while associated.");
1251
1252 if (set_assoc)
1253 set_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
1254
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001255 ret = wl1271_cmd_join(wl, wl->set_bss_type);
1256 if (ret < 0)
1257 goto out;
1258
1259 set_bit(WL1271_FLAG_JOINED, &wl->flags);
1260
1261 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1262 goto out;
1263
1264 /*
1265 * The join command disable the keep-alive mode, shut down its process,
1266 * and also clear the template config, so we need to reset it all after
1267 * the join. The acx_aid starts the keep-alive process, and the order
1268 * of the commands below is relevant.
1269 */
1270 ret = wl1271_acx_keep_alive_mode(wl, true);
1271 if (ret < 0)
1272 goto out;
1273
1274 ret = wl1271_acx_aid(wl, wl->aid);
1275 if (ret < 0)
1276 goto out;
1277
1278 ret = wl1271_cmd_build_klv_null_data(wl);
1279 if (ret < 0)
1280 goto out;
1281
1282 ret = wl1271_acx_keep_alive_config(wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
1283 ACX_KEEP_ALIVE_TPL_VALID);
1284 if (ret < 0)
1285 goto out;
1286
1287out:
1288 return ret;
1289}
1290
1291static int wl1271_unjoin(struct wl1271 *wl)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001292{
1293 int ret;
1294
1295 /* to stop listening to a channel, we disconnect */
1296 ret = wl1271_cmd_disconnect(wl);
1297 if (ret < 0)
1298 goto out;
1299
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001300 clear_bit(WL1271_FLAG_JOINED, &wl->flags);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001301 memset(wl->bssid, 0, ETH_ALEN);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001302
1303 /* stop filterting packets based on bssid */
1304 wl1271_configure_filters(wl, FIF_OTHER_BSS);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001305
1306out:
1307 return ret;
1308}
1309
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001310static void wl1271_set_band_rate(struct wl1271 *wl)
1311{
1312 if (wl->band == IEEE80211_BAND_2GHZ)
1313 wl->basic_rate_set = wl->conf.tx.basic_rate;
1314 else
1315 wl->basic_rate_set = wl->conf.tx.basic_rate_5;
1316}
1317
1318static u32 wl1271_min_rate_get(struct wl1271 *wl)
1319{
1320 int i;
1321 u32 rate = 0;
1322
1323 if (!wl->basic_rate_set) {
1324 WARN_ON(1);
1325 wl->basic_rate_set = wl->conf.tx.basic_rate;
1326 }
1327
1328 for (i = 0; !rate; i++) {
1329 if ((wl->basic_rate_set >> i) & 0x1)
1330 rate = 1 << i;
1331 }
1332
1333 return rate;
1334}
1335
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03001336static int wl1271_handle_idle(struct wl1271 *wl, bool idle)
1337{
1338 int ret;
1339
1340 if (idle) {
1341 if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
1342 ret = wl1271_unjoin(wl);
1343 if (ret < 0)
1344 goto out;
1345 }
1346 wl->rate_set = wl1271_min_rate_get(wl);
1347 wl->sta_rate_set = 0;
1348 ret = wl1271_acx_rate_policies(wl);
1349 if (ret < 0)
1350 goto out;
1351 ret = wl1271_acx_keep_alive_config(
1352 wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
1353 ACX_KEEP_ALIVE_TPL_INVALID);
1354 if (ret < 0)
1355 goto out;
1356 set_bit(WL1271_FLAG_IDLE, &wl->flags);
1357 } else {
1358 /* increment the session counter */
1359 wl->session_counter++;
1360 if (wl->session_counter >= SESSION_COUNTER_MAX)
1361 wl->session_counter = 0;
1362 ret = wl1271_dummy_join(wl);
1363 if (ret < 0)
1364 goto out;
1365 clear_bit(WL1271_FLAG_IDLE, &wl->flags);
1366 }
1367
1368out:
1369 return ret;
1370}
1371
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001372static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
1373{
1374 struct wl1271 *wl = hw->priv;
1375 struct ieee80211_conf *conf = &hw->conf;
1376 int channel, ret = 0;
1377
1378 channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
1379
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001380 wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s",
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001381 channel,
1382 conf->flags & IEEE80211_CONF_PS ? "on" : "off",
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001383 conf->power_level,
1384 conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001385
Juuso Oikarinen781608c2010-05-24 11:18:17 +03001386 /*
1387 * mac80211 will go to idle nearly immediately after transmitting some
1388 * frames, such as the deauth. To make sure those frames reach the air,
1389 * wait here until the TX queue is fully flushed.
1390 */
1391 if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
1392 (conf->flags & IEEE80211_CONF_IDLE))
1393 wl1271_tx_flush(wl);
1394
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001395 mutex_lock(&wl->mutex);
1396
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02001397 if (unlikely(wl->state == WL1271_STATE_OFF)) {
1398 ret = -EAGAIN;
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001399 goto out;
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02001400 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001401
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001402 ret = wl1271_ps_elp_wakeup(wl, false);
1403 if (ret < 0)
1404 goto out;
1405
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001406 /* if the channel changes while joined, join again */
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001407 if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
1408 ((wl->band != conf->channel->band) ||
1409 (wl->channel != channel))) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001410 wl->band = conf->channel->band;
1411 wl->channel = channel;
1412
1413 /*
1414 * FIXME: the mac80211 should really provide a fixed rate
1415 * to use here. for now, just use the smallest possible rate
1416 * for the band as a fixed rate for association frames and
1417 * other control messages.
1418 */
1419 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1420 wl1271_set_band_rate(wl);
1421
1422 wl->basic_rate = wl1271_min_rate_get(wl);
1423 ret = wl1271_acx_rate_policies(wl);
1424 if (ret < 0)
1425 wl1271_warning("rate policy for update channel "
1426 "failed %d", ret);
1427
1428 if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001429 ret = wl1271_join(wl, false);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001430 if (ret < 0)
1431 wl1271_warning("cmd join to update channel "
1432 "failed %d", ret);
1433 }
1434 }
1435
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001436 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03001437 ret = wl1271_handle_idle(wl, conf->flags & IEEE80211_CONF_IDLE);
1438 if (ret < 0)
1439 wl1271_warning("idle mode change failed %d", ret);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001440 }
1441
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001442 /*
1443 * if mac80211 changes the PSM mode, make sure the mode is not
1444 * incorrectly changed after the pspoll failure active window.
1445 */
1446 if (changed & IEEE80211_CONF_CHANGE_PS)
1447 clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
1448
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001449 if (conf->flags & IEEE80211_CONF_PS &&
1450 !test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
1451 set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001452
1453 /*
1454 * We enter PSM only if we're already associated.
1455 * If we're not, we'll enter it when joining an SSID,
1456 * through the bss_info_changed() hook.
1457 */
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001458 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02001459 wl1271_debug(DEBUG_PSM, "psm enabled");
Juuso Oikarinend8c42c02010-02-18 13:25:36 +02001460 ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02001461 wl->basic_rate, true);
Juuso Oikarinenaf5e0842009-12-11 15:41:00 +02001462 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001463 } else if (!(conf->flags & IEEE80211_CONF_PS) &&
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001464 test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02001465 wl1271_debug(DEBUG_PSM, "psm disabled");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001466
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001467 clear_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001468
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001469 if (test_bit(WL1271_FLAG_PSM, &wl->flags))
Juuso Oikarinend8c42c02010-02-18 13:25:36 +02001470 ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02001471 wl->basic_rate, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001472 }
1473
1474 if (conf->power_level != wl->power_level) {
1475 ret = wl1271_acx_tx_power(wl, conf->power_level);
1476 if (ret < 0)
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02001477 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001478
1479 wl->power_level = conf->power_level;
1480 }
1481
1482out_sleep:
1483 wl1271_ps_elp_sleep(wl);
1484
1485out:
1486 mutex_unlock(&wl->mutex);
1487
1488 return ret;
1489}
1490
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001491struct wl1271_filter_params {
1492 bool enabled;
1493 int mc_list_length;
1494 u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
1495};
1496
Jiri Pirko22bedad2010-04-01 21:22:57 +00001497static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw,
1498 struct netdev_hw_addr_list *mc_list)
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001499{
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001500 struct wl1271_filter_params *fp;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001501 struct netdev_hw_addr *ha;
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001502 struct wl1271 *wl = hw->priv;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001503
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001504 if (unlikely(wl->state == WL1271_STATE_OFF))
1505 return 0;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001506
Juuso Oikarinen74441132009-10-13 12:47:53 +03001507 fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001508 if (!fp) {
1509 wl1271_error("Out of memory setting filters.");
1510 return 0;
1511 }
1512
1513 /* update multicast filtering parameters */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001514 fp->mc_list_length = 0;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001515 if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) {
1516 fp->enabled = false;
1517 } else {
1518 fp->enabled = true;
1519 netdev_hw_addr_list_for_each(ha, mc_list) {
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001520 memcpy(fp->mc_list[fp->mc_list_length],
Jiri Pirko22bedad2010-04-01 21:22:57 +00001521 ha->addr, ETH_ALEN);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001522 fp->mc_list_length++;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001523 }
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001524 }
1525
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001526 return (u64)(unsigned long)fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001527}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001528
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001529#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
1530 FIF_ALLMULTI | \
1531 FIF_FCSFAIL | \
1532 FIF_BCN_PRBRESP_PROMISC | \
1533 FIF_CONTROL | \
1534 FIF_OTHER_BSS)
1535
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001536static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
1537 unsigned int changed,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001538 unsigned int *total, u64 multicast)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001539{
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001540 struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001541 struct wl1271 *wl = hw->priv;
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001542 int ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001543
1544 wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter");
1545
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001546 mutex_lock(&wl->mutex);
1547
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001548 *total &= WL1271_SUPPORTED_FILTERS;
1549 changed &= WL1271_SUPPORTED_FILTERS;
1550
1551 if (unlikely(wl->state == WL1271_STATE_OFF))
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001552 goto out;
1553
1554 ret = wl1271_ps_elp_wakeup(wl, false);
1555 if (ret < 0)
1556 goto out;
1557
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001558
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001559 if (*total & FIF_ALLMULTI)
1560 ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
1561 else if (fp)
1562 ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
1563 fp->mc_list,
1564 fp->mc_list_length);
1565 if (ret < 0)
1566 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001567
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001568 /* determine, whether supported filter values have changed */
1569 if (changed == 0)
1570 goto out_sleep;
1571
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001572 /* configure filters */
1573 wl->filters = *total;
1574 wl1271_configure_filters(wl, 0);
1575
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001576 /* apply configured filters */
1577 ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
1578 if (ret < 0)
1579 goto out_sleep;
1580
1581out_sleep:
1582 wl1271_ps_elp_sleep(wl);
1583
1584out:
1585 mutex_unlock(&wl->mutex);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001586 kfree(fp);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001587}
1588
1589static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
1590 struct ieee80211_vif *vif,
1591 struct ieee80211_sta *sta,
1592 struct ieee80211_key_conf *key_conf)
1593{
1594 struct wl1271 *wl = hw->priv;
1595 const u8 *addr;
1596 int ret;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001597 u32 tx_seq_32 = 0;
1598 u16 tx_seq_16 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001599 u8 key_type;
1600
1601 static const u8 bcast_addr[ETH_ALEN] =
1602 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1603
1604 wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
1605
1606 addr = sta ? sta->addr : bcast_addr;
1607
1608 wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd);
1609 wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN);
1610 wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
Johannes Berg97359d12010-08-10 09:46:38 +02001611 key_conf->cipher, key_conf->keyidx,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001612 key_conf->keylen, key_conf->flags);
1613 wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
1614
1615 if (is_zero_ether_addr(addr)) {
1616 /* We dont support TX only encryption */
1617 ret = -EOPNOTSUPP;
1618 goto out;
1619 }
1620
1621 mutex_lock(&wl->mutex);
1622
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02001623 if (unlikely(wl->state == WL1271_STATE_OFF)) {
1624 ret = -EAGAIN;
1625 goto out_unlock;
1626 }
1627
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001628 ret = wl1271_ps_elp_wakeup(wl, false);
1629 if (ret < 0)
1630 goto out_unlock;
1631
Johannes Berg97359d12010-08-10 09:46:38 +02001632 switch (key_conf->cipher) {
1633 case WLAN_CIPHER_SUITE_WEP40:
1634 case WLAN_CIPHER_SUITE_WEP104:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001635 key_type = KEY_WEP;
1636
1637 key_conf->hw_key_idx = key_conf->keyidx;
1638 break;
Johannes Berg97359d12010-08-10 09:46:38 +02001639 case WLAN_CIPHER_SUITE_TKIP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001640 key_type = KEY_TKIP;
1641
1642 key_conf->hw_key_idx = key_conf->keyidx;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001643 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1644 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001645 break;
Johannes Berg97359d12010-08-10 09:46:38 +02001646 case WLAN_CIPHER_SUITE_CCMP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001647 key_type = KEY_AES;
1648
1649 key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001650 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1651 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001652 break;
Juuso Oikarinen7a557242010-09-27 12:42:07 +02001653 case WL1271_CIPHER_SUITE_GEM:
1654 key_type = KEY_GEM;
1655 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1656 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
1657 break;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001658 default:
Johannes Berg97359d12010-08-10 09:46:38 +02001659 wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001660
1661 ret = -EOPNOTSUPP;
1662 goto out_sleep;
1663 }
1664
1665 switch (cmd) {
1666 case SET_KEY:
1667 ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE,
1668 key_conf->keyidx, key_type,
1669 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001670 addr, tx_seq_32, tx_seq_16);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001671 if (ret < 0) {
1672 wl1271_error("Could not add or replace key");
1673 goto out_sleep;
1674 }
Juuso Oikarinenee444cf2010-02-18 13:25:50 +02001675
1676 /* the default WEP key needs to be configured at least once */
1677 if (key_type == KEY_WEP) {
1678 ret = wl1271_cmd_set_default_wep_key(wl,
1679 wl->default_key);
1680 if (ret < 0)
1681 goto out_sleep;
1682 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001683 break;
1684
1685 case DISABLE_KEY:
Juuso Oikarinenfddc7dd2010-02-18 13:25:49 +02001686 /* The wl1271 does not allow to remove unicast keys - they
1687 will be cleared automatically on next CMD_JOIN. Ignore the
1688 request silently, as we dont want the mac80211 to emit
1689 an error message. */
1690 if (!is_broadcast_ether_addr(addr))
1691 break;
1692
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001693 ret = wl1271_cmd_set_key(wl, KEY_REMOVE,
1694 key_conf->keyidx, key_type,
1695 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001696 addr, 0, 0);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001697 if (ret < 0) {
1698 wl1271_error("Could not remove key");
1699 goto out_sleep;
1700 }
1701 break;
1702
1703 default:
1704 wl1271_error("Unsupported key cmd 0x%x", cmd);
1705 ret = -EOPNOTSUPP;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001706 break;
1707 }
1708
1709out_sleep:
1710 wl1271_ps_elp_sleep(wl);
1711
1712out_unlock:
1713 mutex_unlock(&wl->mutex);
1714
1715out:
1716 return ret;
1717}
1718
1719static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
Johannes Berga060bbf2010-04-27 11:59:34 +02001720 struct ieee80211_vif *vif,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001721 struct cfg80211_scan_request *req)
1722{
1723 struct wl1271 *wl = hw->priv;
1724 int ret;
1725 u8 *ssid = NULL;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001726 size_t len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001727
1728 wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
1729
1730 if (req->n_ssids) {
1731 ssid = req->ssids[0].ssid;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001732 len = req->ssids[0].ssid_len;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001733 }
1734
1735 mutex_lock(&wl->mutex);
1736
Juuso Oikarinenb739a422010-10-26 13:24:38 +02001737 if (wl->state == WL1271_STATE_OFF) {
1738 /*
1739 * We cannot return -EBUSY here because cfg80211 will expect
1740 * a call to ieee80211_scan_completed if we do - in this case
1741 * there won't be any call.
1742 */
1743 ret = -EAGAIN;
1744 goto out;
1745 }
1746
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001747 ret = wl1271_ps_elp_wakeup(wl, false);
1748 if (ret < 0)
1749 goto out;
1750
Luciano Coelho5924f892010-08-04 03:46:22 +03001751 ret = wl1271_scan(hw->priv, ssid, len, req);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001752
1753 wl1271_ps_elp_sleep(wl);
1754
1755out:
1756 mutex_unlock(&wl->mutex);
1757
1758 return ret;
1759}
1760
Arik Nemtsov68d069c2010-11-08 10:51:07 +01001761static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
1762{
1763 struct wl1271 *wl = hw->priv;
1764 int ret = 0;
1765
1766 mutex_lock(&wl->mutex);
1767
1768 if (unlikely(wl->state == WL1271_STATE_OFF)) {
1769 ret = -EAGAIN;
1770 goto out;
1771 }
1772
1773 ret = wl1271_ps_elp_wakeup(wl, false);
1774 if (ret < 0)
1775 goto out;
1776
1777 ret = wl1271_acx_frag_threshold(wl, (u16)value);
1778 if (ret < 0)
1779 wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret);
1780
1781 wl1271_ps_elp_sleep(wl);
1782
1783out:
1784 mutex_unlock(&wl->mutex);
1785
1786 return ret;
1787}
1788
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001789static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1790{
1791 struct wl1271 *wl = hw->priv;
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03001792 int ret = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001793
1794 mutex_lock(&wl->mutex);
1795
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02001796 if (unlikely(wl->state == WL1271_STATE_OFF)) {
1797 ret = -EAGAIN;
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03001798 goto out;
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02001799 }
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03001800
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001801 ret = wl1271_ps_elp_wakeup(wl, false);
1802 if (ret < 0)
1803 goto out;
1804
1805 ret = wl1271_acx_rts_threshold(wl, (u16) value);
1806 if (ret < 0)
1807 wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret);
1808
1809 wl1271_ps_elp_sleep(wl);
1810
1811out:
1812 mutex_unlock(&wl->mutex);
1813
1814 return ret;
1815}
1816
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02001817static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb,
1818 int offset)
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001819{
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02001820 u8 *ptr = skb->data + offset;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001821
1822 /* find the location of the ssid in the beacon */
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02001823 while (ptr < skb->data + skb->len) {
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001824 if (ptr[0] == WLAN_EID_SSID) {
1825 wl->ssid_len = ptr[1];
1826 memcpy(wl->ssid, ptr+2, wl->ssid_len);
1827 return;
1828 }
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02001829 ptr += (ptr[1] + 2);
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001830 }
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02001831 wl1271_error("No SSID in IEs!\n");
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001832}
1833
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001834static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
1835 struct ieee80211_vif *vif,
1836 struct ieee80211_bss_conf *bss_conf,
1837 u32 changed)
1838{
1839 enum wl1271_cmd_ps_mode mode;
1840 struct wl1271 *wl = hw->priv;
Shahar Levi18357852010-10-13 16:09:41 +02001841 struct ieee80211_sta *sta = ieee80211_find_sta(vif, bss_conf->bssid);
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001842 bool do_join = false;
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001843 bool set_assoc = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001844 int ret;
1845
1846 wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed");
1847
1848 mutex_lock(&wl->mutex);
1849
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02001850 if (unlikely(wl->state == WL1271_STATE_OFF))
1851 goto out;
1852
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001853 ret = wl1271_ps_elp_wakeup(wl, false);
1854 if (ret < 0)
1855 goto out;
1856
Eliad Peller9ee82d52010-09-19 18:55:09 +02001857 if ((changed & BSS_CHANGED_BEACON_INT) &&
Juuso Oikarinen60e84c22010-03-26 12:53:25 +02001858 (wl->bss_type == BSS_TYPE_IBSS)) {
1859 wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon interval updated: %d",
1860 bss_conf->beacon_int);
1861
1862 wl->beacon_int = bss_conf->beacon_int;
1863 do_join = true;
1864 }
1865
Eliad Peller9ee82d52010-09-19 18:55:09 +02001866 if ((changed & BSS_CHANGED_BEACON) &&
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001867 (wl->bss_type == BSS_TYPE_IBSS)) {
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001868 struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
1869
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001870 wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon updated");
1871
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001872 if (beacon) {
1873 struct ieee80211_hdr *hdr;
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02001874 int ieoffset = offsetof(struct ieee80211_mgmt,
1875 u.beacon.variable);
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001876
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02001877 wl1271_ssid_set(wl, beacon, ieoffset);
1878
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001879 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON,
1880 beacon->data,
Juuso Oikarinen606c1482010-04-01 11:38:21 +03001881 beacon->len, 0,
1882 wl1271_min_rate_get(wl));
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001883
1884 if (ret < 0) {
1885 dev_kfree_skb(beacon);
1886 goto out_sleep;
1887 }
1888
1889 hdr = (struct ieee80211_hdr *) beacon->data;
1890 hdr->frame_control = cpu_to_le16(
1891 IEEE80211_FTYPE_MGMT |
1892 IEEE80211_STYPE_PROBE_RESP);
1893
1894 ret = wl1271_cmd_template_set(wl,
1895 CMD_TEMPL_PROBE_RESPONSE,
1896 beacon->data,
Juuso Oikarinen606c1482010-04-01 11:38:21 +03001897 beacon->len, 0,
1898 wl1271_min_rate_get(wl));
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001899 dev_kfree_skb(beacon);
1900 if (ret < 0)
1901 goto out_sleep;
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001902
1903 /* Need to update the SSID (for filtering etc) */
1904 do_join = true;
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001905 }
1906 }
1907
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001908 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1909 (wl->bss_type == BSS_TYPE_IBSS)) {
1910 wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
1911 bss_conf->enable_beacon ? "enabled" : "disabled");
1912
1913 if (bss_conf->enable_beacon)
1914 wl->set_bss_type = BSS_TYPE_IBSS;
1915 else
1916 wl->set_bss_type = BSS_TYPE_STA_BSS;
1917 do_join = true;
1918 }
1919
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03001920 if (changed & BSS_CHANGED_CQM) {
1921 bool enable = false;
1922 if (bss_conf->cqm_rssi_thold)
1923 enable = true;
1924 ret = wl1271_acx_rssi_snr_trigger(wl, enable,
1925 bss_conf->cqm_rssi_thold,
1926 bss_conf->cqm_rssi_hyst);
1927 if (ret < 0)
1928 goto out;
1929 wl->rssi_thold = bss_conf->cqm_rssi_thold;
1930 }
1931
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001932 if ((changed & BSS_CHANGED_BSSID) &&
1933 /*
1934 * Now we know the correct bssid, so we send a new join command
1935 * and enable the BSSID filter
1936 */
1937 memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001938 memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
Juuso Oikarinena0cb7be2010-03-18 12:26:44 +02001939
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001940 ret = wl1271_cmd_build_null_data(wl);
Juuso Oikarinena0cb7be2010-03-18 12:26:44 +02001941 if (ret < 0)
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001942 goto out_sleep;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001943
Saravanan Dhanabal141418c2010-04-28 09:50:00 +03001944 ret = wl1271_build_qos_null_data(wl);
1945 if (ret < 0)
1946 goto out_sleep;
1947
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001948 /* filter out all packets not from this BSSID */
1949 wl1271_configure_filters(wl, 0);
1950
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02001951 /* Need to update the BSSID (for filtering etc) */
1952 do_join = true;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02001953 }
1954
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001955 if (changed & BSS_CHANGED_ASSOC) {
1956 if (bss_conf->assoc) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001957 u32 rates;
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02001958 int ieoffset;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001959 wl->aid = bss_conf->aid;
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001960 set_assoc = true;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001961
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001962 wl->ps_poll_failures = 0;
1963
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001964 /*
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001965 * use basic rates from AP, and determine lowest rate
1966 * to use with control frames.
1967 */
1968 rates = bss_conf->basic_rates;
1969 wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
1970 rates);
1971 wl->basic_rate = wl1271_min_rate_get(wl);
1972 ret = wl1271_acx_rate_policies(wl);
1973 if (ret < 0)
1974 goto out_sleep;
1975
1976 /*
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001977 * with wl1271, we don't need to update the
1978 * beacon_int and dtim_period, because the firmware
1979 * updates it by itself when the first beacon is
1980 * received after a join.
1981 */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001982 ret = wl1271_cmd_build_ps_poll(wl, wl->aid);
1983 if (ret < 0)
1984 goto out_sleep;
1985
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02001986 /*
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02001987 * Get a template for hardware connection maintenance
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02001988 */
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02001989 dev_kfree_skb(wl->probereq);
1990 wl->probereq = wl1271_cmd_build_ap_probe_req(wl, NULL);
1991 ieoffset = offsetof(struct ieee80211_mgmt,
1992 u.probe_req.variable);
1993 wl1271_ssid_set(wl, wl->probereq, ieoffset);
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02001994
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02001995 /* enable the connection monitoring feature */
1996 ret = wl1271_acx_conn_monit_params(wl, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001997 if (ret < 0)
1998 goto out_sleep;
1999
2000 /* If we want to go in PSM but we're not there yet */
Juuso Oikarinen71449f82009-12-11 15:41:07 +02002001 if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) &&
2002 !test_bit(WL1271_FLAG_PSM, &wl->flags)) {
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002003 mode = STATION_POWER_SAVE_MODE;
Juuso Oikarinen65cddbf12010-08-24 06:28:03 +03002004 ret = wl1271_ps_set_mode(wl, mode,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02002005 wl->basic_rate,
Juuso Oikarinen65cddbf12010-08-24 06:28:03 +03002006 true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002007 if (ret < 0)
2008 goto out_sleep;
2009 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03002010 } else {
2011 /* use defaults when not associated */
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03002012 clear_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags);
Juuso Oikarinen830fb672009-12-11 15:41:06 +02002013 clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
Juuso Oikarinend94cd292009-10-08 21:56:25 +03002014 wl->aid = 0;
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02002015
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002016 /* free probe-request template */
2017 dev_kfree_skb(wl->probereq);
2018 wl->probereq = NULL;
2019
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03002020 /* re-enable dynamic ps - just in case */
Juuso Oikarinenf532be62010-07-08 17:50:05 +03002021 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03002022
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002023 /* revert back to minimum rates for the current band */
2024 wl1271_set_band_rate(wl);
2025 wl->basic_rate = wl1271_min_rate_get(wl);
2026 ret = wl1271_acx_rate_policies(wl);
2027 if (ret < 0)
2028 goto out_sleep;
2029
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02002030 /* disable connection monitor features */
2031 ret = wl1271_acx_conn_monit_params(wl, false);
Juuso Oikarinenc1899552010-03-26 12:53:32 +02002032
2033 /* Disable the keep-alive feature */
2034 ret = wl1271_acx_keep_alive_mode(wl, false);
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02002035 if (ret < 0)
2036 goto out_sleep;
Juuso Oikarinenb84a7d32010-11-22 12:59:08 +02002037
2038 /* restore the bssid filter and go to dummy bssid */
2039 wl1271_unjoin(wl);
2040 wl1271_dummy_join(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002041 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03002042
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002043 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03002044
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002045 if (changed & BSS_CHANGED_ERP_SLOT) {
2046 if (bss_conf->use_short_slot)
2047 ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
2048 else
2049 ret = wl1271_acx_slot(wl, SLOT_TIME_LONG);
2050 if (ret < 0) {
2051 wl1271_warning("Set slot time failed %d", ret);
2052 goto out_sleep;
2053 }
2054 }
2055
2056 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
2057 if (bss_conf->use_short_preamble)
2058 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT);
2059 else
2060 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG);
2061 }
2062
2063 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
2064 if (bss_conf->use_cts_prot)
2065 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE);
2066 else
2067 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE);
2068 if (ret < 0) {
2069 wl1271_warning("Set ctsprotect failed %d", ret);
2070 goto out_sleep;
2071 }
2072 }
2073
Shahar Levi18357852010-10-13 16:09:41 +02002074 /*
2075 * Takes care of: New association with HT enable,
2076 * HT information change in beacon.
2077 */
2078 if (sta &&
2079 (changed & BSS_CHANGED_HT) &&
2080 (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
2081 ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true);
2082 if (ret < 0) {
2083 wl1271_warning("Set ht cap true failed %d", ret);
2084 goto out_sleep;
2085 }
2086 ret = wl1271_acx_set_ht_information(wl,
2087 bss_conf->ht_operation_mode);
2088 if (ret < 0) {
2089 wl1271_warning("Set ht information failed %d", ret);
2090 goto out_sleep;
2091 }
2092 }
2093 /*
2094 * Takes care of: New association without HT,
2095 * Disassociation.
2096 */
2097 else if (sta && (changed & BSS_CHANGED_ASSOC)) {
2098 ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, false);
2099 if (ret < 0) {
2100 wl1271_warning("Set ht cap false failed %d", ret);
2101 goto out_sleep;
2102 }
2103 }
2104
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03002105 if (changed & BSS_CHANGED_ARP_FILTER) {
2106 __be32 addr = bss_conf->arp_addr_list[0];
2107 WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
2108
2109 if (bss_conf->arp_addr_cnt == 1 && bss_conf->arp_filter_enabled)
2110 ret = wl1271_acx_arp_ip_filter(wl, true, addr);
2111 else
2112 ret = wl1271_acx_arp_ip_filter(wl, false, addr);
2113
2114 if (ret < 0)
2115 goto out_sleep;
2116 }
2117
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02002118 if (do_join) {
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002119 ret = wl1271_join(wl, set_assoc);
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02002120 if (ret < 0) {
2121 wl1271_warning("cmd join failed %d", ret);
2122 goto out_sleep;
2123 }
Juuso Oikarinenc1899552010-03-26 12:53:32 +02002124 }
2125
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002126out_sleep:
2127 wl1271_ps_elp_sleep(wl);
2128
2129out:
2130 mutex_unlock(&wl->mutex);
2131}
2132
Kalle Valoc6999d82010-02-18 13:25:41 +02002133static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
2134 const struct ieee80211_tx_queue_params *params)
2135{
2136 struct wl1271 *wl = hw->priv;
Kalle Valo4695dc92010-03-18 12:26:38 +02002137 u8 ps_scheme;
Kalle Valoc6999d82010-02-18 13:25:41 +02002138 int ret;
2139
2140 mutex_lock(&wl->mutex);
2141
2142 wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
2143
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002144 if (unlikely(wl->state == WL1271_STATE_OFF)) {
2145 ret = -EAGAIN;
2146 goto out;
2147 }
2148
Kalle Valoc82c1dd2010-02-18 13:25:47 +02002149 ret = wl1271_ps_elp_wakeup(wl, false);
2150 if (ret < 0)
2151 goto out;
2152
Juuso Oikarinenb43316d2010-03-18 12:26:26 +02002153 /* the txop is confed in units of 32us by the mac80211, we need us */
Kalle Valoc6999d82010-02-18 13:25:41 +02002154 ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
2155 params->cw_min, params->cw_max,
Juuso Oikarinenb43316d2010-03-18 12:26:26 +02002156 params->aifs, params->txop << 5);
Kalle Valoc6999d82010-02-18 13:25:41 +02002157 if (ret < 0)
Kalle Valoc82c1dd2010-02-18 13:25:47 +02002158 goto out_sleep;
Kalle Valoc6999d82010-02-18 13:25:41 +02002159
Kalle Valo4695dc92010-03-18 12:26:38 +02002160 if (params->uapsd)
2161 ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
2162 else
2163 ps_scheme = CONF_PS_SCHEME_LEGACY;
2164
Kalle Valoc6999d82010-02-18 13:25:41 +02002165 ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
2166 CONF_CHANNEL_TYPE_EDCF,
2167 wl1271_tx_get_queue(queue),
Kalle Valo4695dc92010-03-18 12:26:38 +02002168 ps_scheme, CONF_ACK_POLICY_LEGACY, 0, 0);
Kalle Valoc6999d82010-02-18 13:25:41 +02002169 if (ret < 0)
Kalle Valoc82c1dd2010-02-18 13:25:47 +02002170 goto out_sleep;
2171
2172out_sleep:
2173 wl1271_ps_elp_sleep(wl);
Kalle Valoc6999d82010-02-18 13:25:41 +02002174
2175out:
2176 mutex_unlock(&wl->mutex);
2177
2178 return ret;
2179}
2180
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03002181static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw)
2182{
2183
2184 struct wl1271 *wl = hw->priv;
2185 u64 mactime = ULLONG_MAX;
2186 int ret;
2187
2188 wl1271_debug(DEBUG_MAC80211, "mac80211 get tsf");
2189
2190 mutex_lock(&wl->mutex);
2191
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002192 if (unlikely(wl->state == WL1271_STATE_OFF))
2193 goto out;
2194
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03002195 ret = wl1271_ps_elp_wakeup(wl, false);
2196 if (ret < 0)
2197 goto out;
2198
2199 ret = wl1271_acx_tsf_info(wl, &mactime);
2200 if (ret < 0)
2201 goto out_sleep;
2202
2203out_sleep:
2204 wl1271_ps_elp_sleep(wl);
2205
2206out:
2207 mutex_unlock(&wl->mutex);
2208 return mactime;
2209}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002210
John W. Linvilleece550d2010-07-28 16:41:06 -04002211static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
2212 struct survey_info *survey)
2213{
2214 struct wl1271 *wl = hw->priv;
2215 struct ieee80211_conf *conf = &hw->conf;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02002216
John W. Linvilleece550d2010-07-28 16:41:06 -04002217 if (idx != 0)
2218 return -ENOENT;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02002219
John W. Linvilleece550d2010-07-28 16:41:06 -04002220 survey->channel = conf->channel;
2221 survey->filled = SURVEY_INFO_NOISE_DBM;
2222 survey->noise = wl->noise;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02002223
John W. Linvilleece550d2010-07-28 16:41:06 -04002224 return 0;
2225}
2226
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002227/* can't be const, mac80211 writes to this */
2228static struct ieee80211_rate wl1271_rates[] = {
2229 { .bitrate = 10,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002230 .hw_value = CONF_HW_BIT_RATE_1MBPS,
2231 .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002232 { .bitrate = 20,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002233 .hw_value = CONF_HW_BIT_RATE_2MBPS,
2234 .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002235 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2236 { .bitrate = 55,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002237 .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
2238 .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002239 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2240 { .bitrate = 110,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002241 .hw_value = CONF_HW_BIT_RATE_11MBPS,
2242 .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002243 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2244 { .bitrate = 60,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002245 .hw_value = CONF_HW_BIT_RATE_6MBPS,
2246 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002247 { .bitrate = 90,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002248 .hw_value = CONF_HW_BIT_RATE_9MBPS,
2249 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002250 { .bitrate = 120,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002251 .hw_value = CONF_HW_BIT_RATE_12MBPS,
2252 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002253 { .bitrate = 180,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002254 .hw_value = CONF_HW_BIT_RATE_18MBPS,
2255 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002256 { .bitrate = 240,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002257 .hw_value = CONF_HW_BIT_RATE_24MBPS,
2258 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002259 { .bitrate = 360,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002260 .hw_value = CONF_HW_BIT_RATE_36MBPS,
2261 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002262 { .bitrate = 480,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002263 .hw_value = CONF_HW_BIT_RATE_48MBPS,
2264 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002265 { .bitrate = 540,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002266 .hw_value = CONF_HW_BIT_RATE_54MBPS,
2267 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002268};
2269
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002270/* can't be const, mac80211 writes to this */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002271static struct ieee80211_channel wl1271_channels[] = {
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02002272 { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002273 { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002274 { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
2275 { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
2276 { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002277 { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002278 { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
2279 { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
2280 { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002281 { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002282 { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
2283 { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
2284 { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002285};
2286
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002287/* mapping to indexes for wl1271_rates */
Tobias Klausera0ea9492010-05-20 10:38:11 +02002288static const u8 wl1271_rate_to_idx_2ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002289 /* MCS rates are used only with 11n */
Shahar Levi18357852010-10-13 16:09:41 +02002290 7, /* CONF_HW_RXTX_RATE_MCS7 */
2291 6, /* CONF_HW_RXTX_RATE_MCS6 */
2292 5, /* CONF_HW_RXTX_RATE_MCS5 */
2293 4, /* CONF_HW_RXTX_RATE_MCS4 */
2294 3, /* CONF_HW_RXTX_RATE_MCS3 */
2295 2, /* CONF_HW_RXTX_RATE_MCS2 */
2296 1, /* CONF_HW_RXTX_RATE_MCS1 */
2297 0, /* CONF_HW_RXTX_RATE_MCS0 */
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002298
2299 11, /* CONF_HW_RXTX_RATE_54 */
2300 10, /* CONF_HW_RXTX_RATE_48 */
2301 9, /* CONF_HW_RXTX_RATE_36 */
2302 8, /* CONF_HW_RXTX_RATE_24 */
2303
2304 /* TI-specific rate */
2305 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
2306
2307 7, /* CONF_HW_RXTX_RATE_18 */
2308 6, /* CONF_HW_RXTX_RATE_12 */
2309 3, /* CONF_HW_RXTX_RATE_11 */
2310 5, /* CONF_HW_RXTX_RATE_9 */
2311 4, /* CONF_HW_RXTX_RATE_6 */
2312 2, /* CONF_HW_RXTX_RATE_5_5 */
2313 1, /* CONF_HW_RXTX_RATE_2 */
2314 0 /* CONF_HW_RXTX_RATE_1 */
2315};
2316
Shahar Levie8b03a22010-10-13 16:09:39 +02002317/* 11n STA capabilities */
2318#define HW_RX_HIGHEST_RATE 72
2319
Shahar Levi00d20102010-11-08 11:20:10 +00002320#ifdef CONFIG_WL12XX_HT
2321#define WL12XX_HT_CAP { \
Shahar Levie8b03a22010-10-13 16:09:39 +02002322 .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20, \
2323 .ht_supported = true, \
2324 .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, \
2325 .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \
2326 .mcs = { \
2327 .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \
2328 .rx_highest = cpu_to_le16(HW_RX_HIGHEST_RATE), \
2329 .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \
2330 }, \
2331}
Shahar Levi18357852010-10-13 16:09:41 +02002332#else
Shahar Levi00d20102010-11-08 11:20:10 +00002333#define WL12XX_HT_CAP { \
Shahar Levi18357852010-10-13 16:09:41 +02002334 .ht_supported = false, \
2335}
2336#endif
Shahar Levie8b03a22010-10-13 16:09:39 +02002337
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002338/* can't be const, mac80211 writes to this */
2339static struct ieee80211_supported_band wl1271_band_2ghz = {
2340 .channels = wl1271_channels,
2341 .n_channels = ARRAY_SIZE(wl1271_channels),
2342 .bitrates = wl1271_rates,
2343 .n_bitrates = ARRAY_SIZE(wl1271_rates),
Shahar Levi00d20102010-11-08 11:20:10 +00002344 .ht_cap = WL12XX_HT_CAP,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002345};
2346
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002347/* 5 GHz data rates for WL1273 */
2348static struct ieee80211_rate wl1271_rates_5ghz[] = {
2349 { .bitrate = 60,
2350 .hw_value = CONF_HW_BIT_RATE_6MBPS,
2351 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
2352 { .bitrate = 90,
2353 .hw_value = CONF_HW_BIT_RATE_9MBPS,
2354 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
2355 { .bitrate = 120,
2356 .hw_value = CONF_HW_BIT_RATE_12MBPS,
2357 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
2358 { .bitrate = 180,
2359 .hw_value = CONF_HW_BIT_RATE_18MBPS,
2360 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
2361 { .bitrate = 240,
2362 .hw_value = CONF_HW_BIT_RATE_24MBPS,
2363 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
2364 { .bitrate = 360,
2365 .hw_value = CONF_HW_BIT_RATE_36MBPS,
2366 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
2367 { .bitrate = 480,
2368 .hw_value = CONF_HW_BIT_RATE_48MBPS,
2369 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
2370 { .bitrate = 540,
2371 .hw_value = CONF_HW_BIT_RATE_54MBPS,
2372 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
2373};
2374
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002375/* 5 GHz band channels for WL1273 */
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002376static struct ieee80211_channel wl1271_channels_5ghz[] = {
2377 { .hw_value = 183, .center_freq = 4915},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002378 { .hw_value = 184, .center_freq = 4920},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002379 { .hw_value = 185, .center_freq = 4925},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002380 { .hw_value = 187, .center_freq = 4935},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002381 { .hw_value = 188, .center_freq = 4940},
2382 { .hw_value = 189, .center_freq = 4945},
2383 { .hw_value = 192, .center_freq = 4960},
2384 { .hw_value = 196, .center_freq = 4980},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002385 { .hw_value = 7, .center_freq = 5035},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002386 { .hw_value = 8, .center_freq = 5040},
2387 { .hw_value = 9, .center_freq = 5045},
2388 { .hw_value = 11, .center_freq = 5055},
2389 { .hw_value = 12, .center_freq = 5060},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002390 { .hw_value = 16, .center_freq = 5080},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002391 { .hw_value = 34, .center_freq = 5170},
2392 { .hw_value = 36, .center_freq = 5180},
2393 { .hw_value = 38, .center_freq = 5190},
2394 { .hw_value = 40, .center_freq = 5200},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002395 { .hw_value = 42, .center_freq = 5210},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002396 { .hw_value = 44, .center_freq = 5220},
2397 { .hw_value = 46, .center_freq = 5230},
2398 { .hw_value = 48, .center_freq = 5240},
2399 { .hw_value = 52, .center_freq = 5260},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002400 { .hw_value = 56, .center_freq = 5280},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002401 { .hw_value = 60, .center_freq = 5300},
2402 { .hw_value = 64, .center_freq = 5320},
2403 { .hw_value = 100, .center_freq = 5500},
2404 { .hw_value = 104, .center_freq = 5520},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002405 { .hw_value = 108, .center_freq = 5540},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002406 { .hw_value = 112, .center_freq = 5560},
2407 { .hw_value = 116, .center_freq = 5580},
2408 { .hw_value = 120, .center_freq = 5600},
2409 { .hw_value = 124, .center_freq = 5620},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002410 { .hw_value = 128, .center_freq = 5640},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002411 { .hw_value = 132, .center_freq = 5660},
2412 { .hw_value = 136, .center_freq = 5680},
2413 { .hw_value = 140, .center_freq = 5700},
2414 { .hw_value = 149, .center_freq = 5745},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002415 { .hw_value = 153, .center_freq = 5765},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002416 { .hw_value = 157, .center_freq = 5785},
2417 { .hw_value = 161, .center_freq = 5805},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002418 { .hw_value = 165, .center_freq = 5825},
2419};
2420
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002421/* mapping to indexes for wl1271_rates_5ghz */
Tobias Klausera0ea9492010-05-20 10:38:11 +02002422static const u8 wl1271_rate_to_idx_5ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002423 /* MCS rates are used only with 11n */
Shahar Levi18357852010-10-13 16:09:41 +02002424 7, /* CONF_HW_RXTX_RATE_MCS7 */
2425 6, /* CONF_HW_RXTX_RATE_MCS6 */
2426 5, /* CONF_HW_RXTX_RATE_MCS5 */
2427 4, /* CONF_HW_RXTX_RATE_MCS4 */
2428 3, /* CONF_HW_RXTX_RATE_MCS3 */
2429 2, /* CONF_HW_RXTX_RATE_MCS2 */
2430 1, /* CONF_HW_RXTX_RATE_MCS1 */
2431 0, /* CONF_HW_RXTX_RATE_MCS0 */
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002432
2433 7, /* CONF_HW_RXTX_RATE_54 */
2434 6, /* CONF_HW_RXTX_RATE_48 */
2435 5, /* CONF_HW_RXTX_RATE_36 */
2436 4, /* CONF_HW_RXTX_RATE_24 */
2437
2438 /* TI-specific rate */
2439 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
2440
2441 3, /* CONF_HW_RXTX_RATE_18 */
2442 2, /* CONF_HW_RXTX_RATE_12 */
2443 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_11 */
2444 1, /* CONF_HW_RXTX_RATE_9 */
2445 0, /* CONF_HW_RXTX_RATE_6 */
2446 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_5_5 */
2447 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_2 */
2448 CONF_HW_RXTX_RATE_UNSUPPORTED /* CONF_HW_RXTX_RATE_1 */
2449};
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002450
2451static struct ieee80211_supported_band wl1271_band_5ghz = {
2452 .channels = wl1271_channels_5ghz,
2453 .n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
2454 .bitrates = wl1271_rates_5ghz,
2455 .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
Shahar Levi00d20102010-11-08 11:20:10 +00002456 .ht_cap = WL12XX_HT_CAP,
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002457};
2458
Tobias Klausera0ea9492010-05-20 10:38:11 +02002459static const u8 *wl1271_band_rate_to_idx[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002460 [IEEE80211_BAND_2GHZ] = wl1271_rate_to_idx_2ghz,
2461 [IEEE80211_BAND_5GHZ] = wl1271_rate_to_idx_5ghz
2462};
2463
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002464static const struct ieee80211_ops wl1271_ops = {
2465 .start = wl1271_op_start,
2466 .stop = wl1271_op_stop,
2467 .add_interface = wl1271_op_add_interface,
2468 .remove_interface = wl1271_op_remove_interface,
2469 .config = wl1271_op_config,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002470 .prepare_multicast = wl1271_op_prepare_multicast,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002471 .configure_filter = wl1271_op_configure_filter,
2472 .tx = wl1271_op_tx,
2473 .set_key = wl1271_op_set_key,
2474 .hw_scan = wl1271_op_hw_scan,
2475 .bss_info_changed = wl1271_op_bss_info_changed,
Arik Nemtsov68d069c2010-11-08 10:51:07 +01002476 .set_frag_threshold = wl1271_op_set_frag_threshold,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002477 .set_rts_threshold = wl1271_op_set_rts_threshold,
Kalle Valoc6999d82010-02-18 13:25:41 +02002478 .conf_tx = wl1271_op_conf_tx,
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03002479 .get_tsf = wl1271_op_get_tsf,
John W. Linvilleece550d2010-07-28 16:41:06 -04002480 .get_survey = wl1271_op_get_survey,
Kalle Valoc8c90872010-02-18 13:25:53 +02002481 CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002482};
2483
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002484
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02002485u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band)
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002486{
2487 u8 idx;
2488
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02002489 BUG_ON(band >= sizeof(wl1271_band_rate_to_idx)/sizeof(u8 *));
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002490
2491 if (unlikely(rate >= CONF_HW_RXTX_RATE_MAX)) {
2492 wl1271_error("Illegal RX rate from HW: %d", rate);
2493 return 0;
2494 }
2495
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02002496 idx = wl1271_band_rate_to_idx[band][rate];
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002497 if (unlikely(idx == CONF_HW_RXTX_RATE_UNSUPPORTED)) {
2498 wl1271_error("Unsupported RX rate from HW: %d", rate);
2499 return 0;
2500 }
2501
2502 return idx;
2503}
2504
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002505static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
2506 struct device_attribute *attr,
2507 char *buf)
2508{
2509 struct wl1271 *wl = dev_get_drvdata(dev);
2510 ssize_t len;
2511
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02002512 len = PAGE_SIZE;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002513
2514 mutex_lock(&wl->mutex);
2515 len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
2516 wl->sg_enabled);
2517 mutex_unlock(&wl->mutex);
2518
2519 return len;
2520
2521}
2522
2523static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
2524 struct device_attribute *attr,
2525 const char *buf, size_t count)
2526{
2527 struct wl1271 *wl = dev_get_drvdata(dev);
2528 unsigned long res;
2529 int ret;
2530
2531 ret = strict_strtoul(buf, 10, &res);
2532
2533 if (ret < 0) {
2534 wl1271_warning("incorrect value written to bt_coex_mode");
2535 return count;
2536 }
2537
2538 mutex_lock(&wl->mutex);
2539
2540 res = !!res;
2541
2542 if (res == wl->sg_enabled)
2543 goto out;
2544
2545 wl->sg_enabled = res;
2546
2547 if (wl->state == WL1271_STATE_OFF)
2548 goto out;
2549
2550 ret = wl1271_ps_elp_wakeup(wl, false);
2551 if (ret < 0)
2552 goto out;
2553
2554 wl1271_acx_sg_enable(wl, wl->sg_enabled);
2555 wl1271_ps_elp_sleep(wl);
2556
2557 out:
2558 mutex_unlock(&wl->mutex);
2559 return count;
2560}
2561
2562static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
2563 wl1271_sysfs_show_bt_coex_state,
2564 wl1271_sysfs_store_bt_coex_state);
2565
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002566static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
2567 struct device_attribute *attr,
2568 char *buf)
2569{
2570 struct wl1271 *wl = dev_get_drvdata(dev);
2571 ssize_t len;
2572
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02002573 len = PAGE_SIZE;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002574
2575 mutex_lock(&wl->mutex);
2576 if (wl->hw_pg_ver >= 0)
2577 len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
2578 else
2579 len = snprintf(buf, len, "n/a\n");
2580 mutex_unlock(&wl->mutex);
2581
2582 return len;
2583}
2584
2585static DEVICE_ATTR(hw_pg_ver, S_IRUGO | S_IWUSR,
2586 wl1271_sysfs_show_hw_pg_ver, NULL);
2587
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002588int wl1271_register_hw(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002589{
2590 int ret;
2591
2592 if (wl->mac80211_registered)
2593 return 0;
2594
2595 SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
2596
2597 ret = ieee80211_register_hw(wl->hw);
2598 if (ret < 0) {
2599 wl1271_error("unable to register mac80211 hw: %d", ret);
2600 return ret;
2601 }
2602
2603 wl->mac80211_registered = true;
2604
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03002605 register_netdevice_notifier(&wl1271_dev_notifier);
2606
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002607 wl1271_notice("loaded");
2608
2609 return 0;
2610}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002611EXPORT_SYMBOL_GPL(wl1271_register_hw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002612
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002613void wl1271_unregister_hw(struct wl1271 *wl)
2614{
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03002615 unregister_netdevice_notifier(&wl1271_dev_notifier);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002616 ieee80211_unregister_hw(wl->hw);
2617 wl->mac80211_registered = false;
2618
2619}
2620EXPORT_SYMBOL_GPL(wl1271_unregister_hw);
2621
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002622int wl1271_init_ieee80211(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002623{
Juuso Oikarinen7a557242010-09-27 12:42:07 +02002624 static const u32 cipher_suites[] = {
2625 WLAN_CIPHER_SUITE_WEP40,
2626 WLAN_CIPHER_SUITE_WEP104,
2627 WLAN_CIPHER_SUITE_TKIP,
2628 WLAN_CIPHER_SUITE_CCMP,
2629 WL1271_CIPHER_SUITE_GEM,
2630 };
2631
Juuso Oikarinen1e2b7972009-10-08 21:56:20 +03002632 /* The tx descriptor buffer and the TKIP space. */
2633 wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
2634 sizeof(struct wl1271_tx_hw_descr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002635
2636 /* unit us */
2637 /* FIXME: find a proper value */
2638 wl->hw->channel_change_time = 10000;
Juuso Oikarinen50c500a2010-04-01 11:38:22 +03002639 wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002640
2641 wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
Juuso Oikarinen03442a32009-11-23 23:22:14 +02002642 IEEE80211_HW_BEACON_FILTER |
Juuso Oikarinen0a343322010-02-22 08:38:41 +02002643 IEEE80211_HW_SUPPORTS_PS |
Kalle Valo4695dc92010-03-18 12:26:38 +02002644 IEEE80211_HW_SUPPORTS_UAPSD |
Juuso Oikarinena9af0922010-03-26 12:53:30 +02002645 IEEE80211_HW_HAS_RATE_CONTROL |
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03002646 IEEE80211_HW_CONNECTION_MONITOR |
2647 IEEE80211_HW_SUPPORTS_CQM_RSSI;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002648
Juuso Oikarinen7a557242010-09-27 12:42:07 +02002649 wl->hw->wiphy->cipher_suites = cipher_suites;
2650 wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
2651
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02002652 wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
2653 BIT(NL80211_IFTYPE_ADHOC);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002654 wl->hw->wiphy->max_scan_ssids = 1;
2655 wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz;
Juuso Oikarinen11eb5422010-08-24 06:28:03 +03002656 wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz;
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002657
Kalle Valo12bd8942010-03-18 12:26:33 +02002658 wl->hw->queues = 4;
Juuso Oikarinen31627dc2010-03-26 12:53:12 +02002659 wl->hw->max_rates = 1;
Kalle Valo12bd8942010-03-18 12:26:33 +02002660
Juuso Oikarinenb7417d92010-11-10 11:27:19 +01002661 wl->hw->wiphy->reg_notifier = wl1271_reg_notify;
2662
Teemu Paasikivi8197b712010-02-22 08:38:23 +02002663 SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002664
2665 return 0;
2666}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002667EXPORT_SYMBOL_GPL(wl1271_init_ieee80211);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002668
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002669#define WL1271_DEFAULT_CHANNEL 0
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002670
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002671struct ieee80211_hw *wl1271_alloc_hw(void)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002672{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002673 struct ieee80211_hw *hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002674 struct platform_device *plat_dev = NULL;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002675 struct wl1271 *wl;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002676 int i, ret;
Ido Yariv1f37cbc2010-09-30 13:28:27 +02002677 unsigned int order;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002678
2679 hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
2680 if (!hw) {
2681 wl1271_error("could not alloc ieee80211_hw");
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002682 ret = -ENOMEM;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002683 goto err_hw_alloc;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002684 }
2685
Julia Lawall929ebd32010-05-15 23:16:39 +02002686 plat_dev = kmemdup(&wl1271_device, sizeof(wl1271_device), GFP_KERNEL);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002687 if (!plat_dev) {
2688 wl1271_error("could not allocate platform_device");
2689 ret = -ENOMEM;
2690 goto err_plat_alloc;
2691 }
2692
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002693 wl = hw->priv;
2694 memset(wl, 0, sizeof(*wl));
2695
Juuso Oikarinen01c09162009-10-13 12:47:55 +03002696 INIT_LIST_HEAD(&wl->list);
2697
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002698 wl->hw = hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002699 wl->plat_dev = plat_dev;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002700
2701 skb_queue_head_init(&wl->tx_queue);
2702
Juuso Oikarinen37b70a82009-10-08 21:56:21 +03002703 INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03002704 INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work);
Juuso Oikarinen117b38d2010-09-30 10:43:28 +02002705 INIT_WORK(&wl->irq_work, wl1271_irq_work);
2706 INIT_WORK(&wl->tx_work, wl1271_tx_work);
2707 INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
2708 INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002709 wl->channel = WL1271_DEFAULT_CHANNEL;
Juuso Oikarinen60e84c22010-03-26 12:53:25 +02002710 wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002711 wl->default_key = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002712 wl->rx_counter = 0;
2713 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
2714 wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02002715 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002716 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
Juuso Oikarinena6fe2312009-12-11 15:40:58 +02002717 wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002718 wl->basic_rate = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02002719 wl->rate_set = CONF_TX_RATE_MASK_BASIC;
2720 wl->sta_rate_set = 0;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03002721 wl->band = IEEE80211_BAND_2GHZ;
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03002722 wl->vif = NULL;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02002723 wl->flags = 0;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002724 wl->sg_enabled = true;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002725 wl->hw_pg_ver = -1;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002726
Ido Yariv25eeb9e2010-10-12 16:20:06 +02002727 memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
Juuso Oikarinenbe7078c2009-10-08 21:56:26 +03002728 for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002729 wl->tx_frames[i] = NULL;
2730
2731 spin_lock_init(&wl->wl_lock);
2732
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002733 wl->state = WL1271_STATE_OFF;
2734 mutex_init(&wl->mutex);
2735
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002736 /* Apply default driver configuration. */
2737 wl1271_conf_init(wl);
2738
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02002739 wl1271_debugfs_init(wl);
2740
Ido Yariv1f37cbc2010-09-30 13:28:27 +02002741 order = get_order(WL1271_AGGR_BUFFER_SIZE);
2742 wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
2743 if (!wl->aggr_buf) {
2744 ret = -ENOMEM;
2745 goto err_hw;
2746 }
2747
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002748 /* Register platform device */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002749 ret = platform_device_register(wl->plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002750 if (ret) {
2751 wl1271_error("couldn't register platform device");
Ido Yariv1f37cbc2010-09-30 13:28:27 +02002752 goto err_aggr;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002753 }
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002754 dev_set_drvdata(&wl->plat_dev->dev, wl);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002755
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002756 /* Create sysfs file to control bt coex state */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002757 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002758 if (ret < 0) {
2759 wl1271_error("failed to create sysfs file bt_coex_state");
2760 goto err_platform;
2761 }
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002762
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002763 /* Create sysfs file to get HW PG version */
2764 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
2765 if (ret < 0) {
2766 wl1271_error("failed to create sysfs file hw_pg_ver");
2767 goto err_bt_coex_state;
2768 }
2769
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002770 return hw;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002771
Juuso Oikarinend717fd62010-05-07 11:38:58 +03002772err_bt_coex_state:
2773 device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
2774
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002775err_platform:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002776 platform_device_unregister(wl->plat_dev);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02002777
Ido Yariv1f37cbc2010-09-30 13:28:27 +02002778err_aggr:
2779 free_pages((unsigned long)wl->aggr_buf, order);
2780
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002781err_hw:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002782 wl1271_debugfs_exit(wl);
2783 kfree(plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002784
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002785err_plat_alloc:
2786 ieee80211_free_hw(hw);
2787
2788err_hw_alloc:
2789
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02002790 return ERR_PTR(ret);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002791}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002792EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002793
2794int wl1271_free_hw(struct wl1271 *wl)
2795{
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002796 platform_device_unregister(wl->plat_dev);
Ido Yariv1f37cbc2010-09-30 13:28:27 +02002797 free_pages((unsigned long)wl->aggr_buf,
2798 get_order(WL1271_AGGR_BUFFER_SIZE));
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02002799 kfree(wl->plat_dev);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002800
2801 wl1271_debugfs_exit(wl);
2802
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02002803 vfree(wl->fw);
2804 wl->fw = NULL;
2805 kfree(wl->nvs);
2806 wl->nvs = NULL;
2807
2808 kfree(wl->fw_status);
2809 kfree(wl->tx_res_if);
2810
2811 ieee80211_free_hw(wl->hw);
2812
2813 return 0;
2814}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02002815EXPORT_SYMBOL_GPL(wl1271_free_hw);
2816
2817MODULE_LICENSE("GPL");
2818MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
2819MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");