blob: 3a4f6069be603400a9f61c8147b3a055de7af04e [file] [log] [blame]
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001/*
2 * This file is part of wl1271
3 *
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02004 * Copyright (C) 2008-2010 Nokia Corporation
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03005 *
6 * Contact: Luciano Coelho <luciano.coelho@nokia.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 *
22 */
23
24#include <linux/module.h>
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030025#include <linux/firmware.h>
26#include <linux/delay.h>
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030027#include <linux/spi/spi.h>
28#include <linux/crc32.h>
29#include <linux/etherdevice.h>
Juuso Oikarinen1fba4972009-10-08 21:56:32 +030030#include <linux/vmalloc.h>
Juuso Oikarinena1dd8182010-03-18 12:26:31 +020031#include <linux/platform_device.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090032#include <linux/slab.h>
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030033
Shahar Levi00d20102010-11-08 11:20:10 +000034#include "wl12xx.h"
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030035#include "wl12xx_80211.h"
Shahar Levi00d20102010-11-08 11:20:10 +000036#include "reg.h"
37#include "io.h"
38#include "event.h"
39#include "tx.h"
40#include "rx.h"
41#include "ps.h"
42#include "init.h"
43#include "debugfs.h"
44#include "cmd.h"
45#include "boot.h"
46#include "testmode.h"
47#include "scan.h"
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030048
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +020049#define WL1271_BOOT_RETRIES 3
50
Juuso Oikarinen8a080482009-10-13 12:47:44 +030051static struct conf_drv_settings default_conf = {
52 .sg = {
Juuso Oikarinen1b00f542010-03-18 12:26:30 +020053 .params = {
54 [CONF_SG_BT_PER_THRESHOLD] = 7500,
55 [CONF_SG_HV3_MAX_OVERRIDE] = 0,
56 [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400,
57 [CONF_SG_BT_LOAD_RATIO] = 50,
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +030058 [CONF_SG_AUTO_PS_MODE] = 1,
Juuso Oikarinen1b00f542010-03-18 12:26:30 +020059 [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
60 [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
61 [CONF_SG_ANTENNA_CONFIGURATION] = 0,
62 [CONF_SG_BEACON_MISS_PERCENT] = 60,
63 [CONF_SG_RATE_ADAPT_THRESH] = 12,
64 [CONF_SG_RATE_ADAPT_SNR] = 0,
65 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR] = 10,
66 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR] = 30,
67 [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR] = 8,
68 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR] = 20,
69 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR] = 50,
70 /* Note: with UPSD, this should be 4 */
71 [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR] = 8,
72 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR] = 7,
73 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR] = 25,
74 [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR] = 20,
75 /* Note: with UPDS, this should be 15 */
76 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR] = 8,
77 /* Note: with UPDS, this should be 50 */
78 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR] = 40,
79 /* Note: with UPDS, this should be 10 */
80 [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR] = 20,
81 [CONF_SG_RXT] = 1200,
82 [CONF_SG_TXT] = 1000,
83 [CONF_SG_ADAPTIVE_RXT_TXT] = 1,
84 [CONF_SG_PS_POLL_TIMEOUT] = 10,
85 [CONF_SG_UPSD_TIMEOUT] = 10,
86 [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR] = 7,
87 [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR] = 15,
88 [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR] = 15,
89 [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR] = 8,
90 [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR] = 20,
91 [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR] = 15,
92 [CONF_SG_WLAN_ACTIVE_BT_ACL_MIN_BR] = 20,
93 [CONF_SG_WLAN_ACTIVE_BT_ACL_MAX_BR] = 50,
94 [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_BR] = 10,
95 [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
96 [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP] = 800,
97 [CONF_SG_PASSIVE_SCAN_A2DP_BT_TIME] = 75,
98 [CONF_SG_PASSIVE_SCAN_A2DP_WLAN_TIME] = 15,
99 [CONF_SG_HV3_MAX_SERVED] = 6,
100 [CONF_SG_DHCP_TIME] = 5000,
101 [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
102 },
103 .state = CONF_SG_PROTECTIVE,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300104 },
105 .rx = {
106 .rx_msdu_life_time = 512000,
107 .packet_detection_threshold = 0,
108 .ps_poll_timeout = 15,
109 .upsd_timeout = 15,
110 .rts_threshold = 2347,
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200111 .rx_cca_threshold = 0,
112 .irq_blk_threshold = 0xFFFF,
113 .irq_pkt_threshold = 0,
114 .irq_timeout = 600,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300115 .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY,
116 },
117 .tx = {
118 .tx_energy_detection = 0,
Arik Nemtsov1e05a812010-10-16 17:44:51 +0200119 .sta_rc_conf = {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +0300120 .enabled_rates = 0,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300121 .short_retry_limit = 10,
122 .long_retry_limit = 10,
Arik Nemtsov1e05a812010-10-16 17:44:51 +0200123 .aflags = 0,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300124 },
125 .ac_conf_count = 4,
126 .ac_conf = {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200127 [CONF_TX_AC_BE] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300128 .ac = CONF_TX_AC_BE,
129 .cw_min = 15,
130 .cw_max = 63,
131 .aifsn = 3,
132 .tx_op_limit = 0,
133 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200134 [CONF_TX_AC_BK] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300135 .ac = CONF_TX_AC_BK,
136 .cw_min = 15,
137 .cw_max = 63,
138 .aifsn = 7,
139 .tx_op_limit = 0,
140 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200141 [CONF_TX_AC_VI] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300142 .ac = CONF_TX_AC_VI,
143 .cw_min = 15,
144 .cw_max = 63,
145 .aifsn = CONF_TX_AIFS_PIFS,
146 .tx_op_limit = 3008,
147 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200148 [CONF_TX_AC_VO] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300149 .ac = CONF_TX_AC_VO,
150 .cw_min = 15,
151 .cw_max = 63,
152 .aifsn = CONF_TX_AIFS_PIFS,
153 .tx_op_limit = 1504,
154 },
155 },
Arik Nemtsov1e05a812010-10-16 17:44:51 +0200156 .ap_rc_conf = {
157 [0] = {
158 .enabled_rates = CONF_TX_AP_ENABLED_RATES,
159 .short_retry_limit = 10,
160 .long_retry_limit = 10,
161 .aflags = 0,
162 },
163 [1] = {
164 .enabled_rates = CONF_TX_AP_ENABLED_RATES,
165 .short_retry_limit = 10,
166 .long_retry_limit = 10,
167 .aflags = 0,
168 },
169 [2] = {
170 .enabled_rates = CONF_TX_AP_ENABLED_RATES,
171 .short_retry_limit = 10,
172 .long_retry_limit = 10,
173 .aflags = 0,
174 },
175 [3] = {
176 .enabled_rates = CONF_TX_AP_ENABLED_RATES,
177 .short_retry_limit = 10,
178 .long_retry_limit = 10,
179 .aflags = 0,
180 },
181 },
182 .ap_mgmt_conf = {
183 .enabled_rates = CONF_TX_AP_DEFAULT_MGMT_RATES,
184 .short_retry_limit = 10,
185 .long_retry_limit = 10,
186 .aflags = 0,
187 },
188 .ap_bcst_conf = {
189 .enabled_rates = CONF_HW_BIT_RATE_1MBPS,
190 .short_retry_limit = 10,
191 .long_retry_limit = 10,
192 .aflags = 0,
193 },
Arik Nemtsov79b223f2010-10-16 17:52:59 +0200194 .ap_max_tx_retries = 100,
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200195 .tid_conf_count = 4,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300196 .tid_conf = {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200197 [CONF_TX_AC_BE] = {
198 .queue_id = CONF_TX_AC_BE,
199 .channel_type = CONF_CHANNEL_TYPE_EDCF,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300200 .tsid = CONF_TX_AC_BE,
201 .ps_scheme = CONF_PS_SCHEME_LEGACY,
202 .ack_policy = CONF_ACK_POLICY_LEGACY,
203 .apsd_conf = {0, 0},
204 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200205 [CONF_TX_AC_BK] = {
206 .queue_id = CONF_TX_AC_BK,
207 .channel_type = CONF_CHANNEL_TYPE_EDCF,
208 .tsid = CONF_TX_AC_BK,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300209 .ps_scheme = CONF_PS_SCHEME_LEGACY,
210 .ack_policy = CONF_ACK_POLICY_LEGACY,
211 .apsd_conf = {0, 0},
212 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200213 [CONF_TX_AC_VI] = {
214 .queue_id = CONF_TX_AC_VI,
215 .channel_type = CONF_CHANNEL_TYPE_EDCF,
216 .tsid = CONF_TX_AC_VI,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300217 .ps_scheme = CONF_PS_SCHEME_LEGACY,
218 .ack_policy = CONF_ACK_POLICY_LEGACY,
219 .apsd_conf = {0, 0},
220 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200221 [CONF_TX_AC_VO] = {
222 .queue_id = CONF_TX_AC_VO,
223 .channel_type = CONF_CHANNEL_TYPE_EDCF,
224 .tsid = CONF_TX_AC_VO,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300225 .ps_scheme = CONF_PS_SCHEME_LEGACY,
226 .ack_policy = CONF_ACK_POLICY_LEGACY,
227 .apsd_conf = {0, 0},
228 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300229 },
230 .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD,
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200231 .tx_compl_timeout = 700,
Juuso Oikarinenebba60c2010-04-01 11:38:20 +0300232 .tx_compl_threshold = 4,
233 .basic_rate = CONF_HW_BIT_RATE_1MBPS,
234 .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS,
Arik Nemtsov1e05a812010-10-16 17:44:51 +0200235 .tmpl_short_retry_limit = 10,
236 .tmpl_long_retry_limit = 10,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300237 },
238 .conn = {
239 .wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
Juuso Oikarinen50c500a2010-04-01 11:38:22 +0300240 .listen_interval = 1,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300241 .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED,
242 .bcn_filt_ie_count = 1,
243 .bcn_filt_ie = {
244 [0] = {
245 .ie = WLAN_EID_CHANNEL_SWITCH,
246 .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE,
247 }
248 },
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200249 .synch_fail_thold = 10,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300250 .bss_lose_timeout = 100,
251 .beacon_rx_timeout = 10000,
252 .broadcast_timeout = 20000,
253 .rx_broadcast_in_ps = 1,
Juuso Oikarinen90494a92010-07-08 17:50:00 +0300254 .ps_poll_threshold = 10,
255 .ps_poll_recovery_period = 700,
Juuso Oikarinen11f70f92009-10-13 12:47:46 +0300256 .bet_enable = CONF_BET_MODE_ENABLE,
Juuso Oikarinen84502562009-11-23 23:22:12 +0200257 .bet_max_consecutive = 10,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +0200258 .psm_entry_retries = 5,
Eliad Pelleree608332011-02-02 09:59:34 +0200259 .psm_exit_retries = 255,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +0200260 .psm_entry_nullfunc_retries = 3,
261 .psm_entry_hangover_period = 1,
Juuso Oikarinen50c500a2010-04-01 11:38:22 +0300262 .keep_alive_interval = 55000,
263 .max_listen_interval = 20,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300264 },
Luciano Coelho6e92b412009-12-11 15:40:50 +0200265 .itrim = {
266 .enable = false,
267 .timeout = 50000,
Juuso Oikarinen38ad2d82009-12-11 15:41:08 +0200268 },
269 .pm_config = {
270 .host_clk_settling_time = 5000,
271 .host_fast_wakeup_support = false
Juuso Oikarinen00236aed2010-04-09 11:07:30 +0300272 },
273 .roam_trigger = {
Juuso Oikarinen00236aed2010-04-09 11:07:30 +0300274 .trigger_pacing = 1,
275 .avg_weight_rssi_beacon = 20,
276 .avg_weight_rssi_data = 10,
277 .avg_weight_snr_beacon = 20,
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100278 .avg_weight_snr_data = 10,
Juuso Oikarinenbea39d62010-09-21 08:14:31 +0200279 },
280 .scan = {
281 .min_dwell_time_active = 7500,
282 .max_dwell_time_active = 30000,
Juuso Oikarinenea45b2c2011-01-24 07:01:54 +0100283 .min_dwell_time_passive = 100000,
284 .max_dwell_time_passive = 100000,
Juuso Oikarinenbea39d62010-09-21 08:14:31 +0200285 .num_probe_reqs = 2,
286 },
Juuso Oikarinen644a4862010-10-05 13:11:56 +0200287 .rf = {
288 .tx_per_channel_power_compensation_2 = {
289 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
290 },
291 .tx_per_channel_power_compensation_5 = {
292 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
293 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
294 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
295 },
296 },
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100297 .ht = {
298 .tx_ba_win_size = 64,
299 .inactivity_timeout = 10000,
300 },
Eliad Pellerc8bde242011-02-02 09:59:35 +0200301 .mem = {
Eliad Pellerfe5ef092011-02-02 09:59:36 +0200302 .num_stations = 1,
303 .ssid_profiles = 1,
304 .rx_block_num = 70,
305 .tx_min_block_num = 40,
Eliad Pellerc8bde242011-02-02 09:59:35 +0200306 .dynamic_memory = 0,
307 .min_req_tx_blocks = 104,
308 .min_req_rx_blocks = 22,
309 .tx_min = 27,
310 }
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300311};
312
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200313static void __wl1271_op_remove_interface(struct wl1271 *wl);
Arik Nemtsov7f179b42010-10-16 21:39:06 +0200314static void wl1271_free_ap_keys(struct wl1271 *wl);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200315
316
Juuso Oikarinena1dd8182010-03-18 12:26:31 +0200317static void wl1271_device_release(struct device *dev)
318{
319
320}
321
322static struct platform_device wl1271_device = {
323 .name = "wl1271",
324 .id = -1,
325
326 /* device model insists to have a release function */
327 .dev = {
328 .release = wl1271_device_release,
329 },
330};
331
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300332static LIST_HEAD(wl_list);
333
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300334static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
335 void *arg)
336{
337 struct net_device *dev = arg;
338 struct wireless_dev *wdev;
339 struct wiphy *wiphy;
340 struct ieee80211_hw *hw;
341 struct wl1271 *wl;
342 struct wl1271 *wl_temp;
343 int ret = 0;
344
345 /* Check that this notification is for us. */
346 if (what != NETDEV_CHANGE)
347 return NOTIFY_DONE;
348
349 wdev = dev->ieee80211_ptr;
350 if (wdev == NULL)
351 return NOTIFY_DONE;
352
353 wiphy = wdev->wiphy;
354 if (wiphy == NULL)
355 return NOTIFY_DONE;
356
357 hw = wiphy_priv(wiphy);
358 if (hw == NULL)
359 return NOTIFY_DONE;
360
361 wl_temp = hw->priv;
362 list_for_each_entry(wl, &wl_list, list) {
363 if (wl == wl_temp)
364 break;
365 }
366 if (wl != wl_temp)
367 return NOTIFY_DONE;
368
369 mutex_lock(&wl->mutex);
370
371 if (wl->state == WL1271_STATE_OFF)
372 goto out;
373
374 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
375 goto out;
376
377 ret = wl1271_ps_elp_wakeup(wl, false);
378 if (ret < 0)
379 goto out;
380
381 if ((dev->operstate == IF_OPER_UP) &&
382 !test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags)) {
383 wl1271_cmd_set_sta_state(wl);
384 wl1271_info("Association completed.");
385 }
386
387 wl1271_ps_elp_sleep(wl);
388
389out:
390 mutex_unlock(&wl->mutex);
391
392 return NOTIFY_OK;
393}
394
Juuso Oikarinenb7417d92010-11-10 11:27:19 +0100395static int wl1271_reg_notify(struct wiphy *wiphy,
Luciano Coelho573c67c2010-11-26 13:44:59 +0200396 struct regulatory_request *request)
397{
Juuso Oikarinenb7417d92010-11-10 11:27:19 +0100398 struct ieee80211_supported_band *band;
399 struct ieee80211_channel *ch;
400 int i;
401
402 band = wiphy->bands[IEEE80211_BAND_5GHZ];
403 for (i = 0; i < band->n_channels; i++) {
404 ch = &band->channels[i];
405 if (ch->flags & IEEE80211_CHAN_DISABLED)
406 continue;
407
408 if (ch->flags & IEEE80211_CHAN_RADAR)
409 ch->flags |= IEEE80211_CHAN_NO_IBSS |
410 IEEE80211_CHAN_PASSIVE_SCAN;
411
412 }
413
414 return 0;
415}
416
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300417static void wl1271_conf_init(struct wl1271 *wl)
418{
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300419
420 /*
421 * This function applies the default configuration to the driver. This
422 * function is invoked upon driver load (spi probe.)
423 *
424 * The configuration is stored in a run-time structure in order to
425 * facilitate for run-time adjustment of any of the parameters. Making
426 * changes to the configuration structure will apply the new values on
427 * the next interface up (wl1271_op_start.)
428 */
429
430 /* apply driver default configuration */
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300431 memcpy(&wl->conf, &default_conf, sizeof(default_conf));
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300432}
433
434
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300435static int wl1271_plt_init(struct wl1271 *wl)
436{
Luciano Coelho12419cc2010-02-18 13:25:44 +0200437 struct conf_tx_ac_category *conf_ac;
438 struct conf_tx_tid *conf_tid;
439 int ret, i;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300440
Luciano Coelho98b5dd52009-11-23 23:22:17 +0200441 ret = wl1271_cmd_general_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200442 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200443 return ret;
444
Luciano Coelho98b5dd52009-11-23 23:22:17 +0200445 ret = wl1271_cmd_radio_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200446 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200447 return ret;
448
Juuso Oikarinen644a4862010-10-05 13:11:56 +0200449 ret = wl1271_cmd_ext_radio_parms(wl);
450 if (ret < 0)
451 return ret;
452
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200453 ret = wl1271_sta_init_templates_config(wl);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200454 if (ret < 0)
455 return ret;
456
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300457 ret = wl1271_acx_init_mem_config(wl);
458 if (ret < 0)
459 return ret;
460
Luciano Coelho12419cc2010-02-18 13:25:44 +0200461 /* PHY layer config */
462 ret = wl1271_init_phy_config(wl);
463 if (ret < 0)
464 goto out_free_memmap;
465
466 ret = wl1271_acx_dco_itrim_params(wl);
467 if (ret < 0)
468 goto out_free_memmap;
469
470 /* Initialize connection monitoring thresholds */
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +0200471 ret = wl1271_acx_conn_monit_params(wl, false);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200472 if (ret < 0)
473 goto out_free_memmap;
474
475 /* Bluetooth WLAN coexistence */
476 ret = wl1271_init_pta(wl);
477 if (ret < 0)
478 goto out_free_memmap;
479
480 /* Energy detection */
481 ret = wl1271_init_energy_detection(wl);
482 if (ret < 0)
483 goto out_free_memmap;
484
Gery Kahn1ec610e2011-02-01 03:03:08 -0600485 ret = wl1271_acx_sta_mem_cfg(wl);
486 if (ret < 0)
487 goto out_free_memmap;
488
Luciano Coelho12419cc2010-02-18 13:25:44 +0200489 /* Default fragmentation threshold */
Arik Nemtsov68d069c2010-11-08 10:51:07 +0100490 ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200491 if (ret < 0)
492 goto out_free_memmap;
493
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200494 /* Default TID/AC configuration */
495 BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200496 for (i = 0; i < wl->conf.tx.tid_conf_count; i++) {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200497 conf_ac = &wl->conf.tx.ac_conf[i];
498 ret = wl1271_acx_ac_cfg(wl, conf_ac->ac, conf_ac->cw_min,
499 conf_ac->cw_max, conf_ac->aifsn,
500 conf_ac->tx_op_limit);
501 if (ret < 0)
502 goto out_free_memmap;
503
Luciano Coelho12419cc2010-02-18 13:25:44 +0200504 conf_tid = &wl->conf.tx.tid_conf[i];
505 ret = wl1271_acx_tid_cfg(wl, conf_tid->queue_id,
506 conf_tid->channel_type,
507 conf_tid->tsid,
508 conf_tid->ps_scheme,
509 conf_tid->ack_policy,
510 conf_tid->apsd_conf[0],
511 conf_tid->apsd_conf[1]);
512 if (ret < 0)
513 goto out_free_memmap;
514 }
515
Luciano Coelho12419cc2010-02-18 13:25:44 +0200516 /* Enable data path */
Luciano Coelho94210892009-12-11 15:40:55 +0200517 ret = wl1271_cmd_data_path(wl, 1);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300518 if (ret < 0)
Luciano Coelho12419cc2010-02-18 13:25:44 +0200519 goto out_free_memmap;
520
521 /* Configure for CAM power saving (ie. always active) */
522 ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
523 if (ret < 0)
524 goto out_free_memmap;
525
526 /* configure PM */
527 ret = wl1271_acx_pm_config(wl);
528 if (ret < 0)
529 goto out_free_memmap;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300530
531 return 0;
Luciano Coelho12419cc2010-02-18 13:25:44 +0200532
533 out_free_memmap:
534 kfree(wl->target_mem_map);
535 wl->target_mem_map = NULL;
536
537 return ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300538}
539
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300540static void wl1271_fw_status(struct wl1271 *wl,
Eliad Pellerc8bde242011-02-02 09:59:35 +0200541 struct wl1271_fw_full_status *full_status)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300542{
Eliad Pellerc8bde242011-02-02 09:59:35 +0200543 struct wl1271_fw_common_status *status = &full_status->common;
Juuso Oikarinenac5e1e32010-02-22 08:38:38 +0200544 struct timespec ts;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300545 u32 total = 0;
546 int i;
547
Eliad Pellerc8bde242011-02-02 09:59:35 +0200548 if (wl->bss_type == BSS_TYPE_AP_BSS)
549 wl1271_raw_read(wl, FW_STATUS_ADDR, status,
550 sizeof(struct wl1271_fw_ap_status), false);
551 else
552 wl1271_raw_read(wl, FW_STATUS_ADDR, status,
553 sizeof(struct wl1271_fw_sta_status), false);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300554
555 wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
556 "drv_rx_counter = %d, tx_results_counter = %d)",
557 status->intr,
558 status->fw_rx_counter,
559 status->drv_rx_counter,
560 status->tx_results_counter);
561
562 /* update number of available TX blocks */
563 for (i = 0; i < NUM_TX_QUEUES; i++) {
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300564 u32 cnt = le32_to_cpu(status->tx_released_blks[i]) -
565 wl->tx_blocks_freed[i];
566
567 wl->tx_blocks_freed[i] =
568 le32_to_cpu(status->tx_released_blks[i]);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300569 wl->tx_blocks_available += cnt;
570 total += cnt;
571 }
572
Ido Yariva5225502010-10-12 14:49:10 +0200573 /* if more blocks are available now, tx work can be scheduled */
574 if (total)
575 clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300576
577 /* update the host-chipset time offset */
Juuso Oikarinenac5e1e32010-02-22 08:38:38 +0200578 getnstimeofday(&ts);
579 wl->time_offset = (timespec_to_ns(&ts) >> 10) -
580 (s64)le32_to_cpu(status->fw_localtime);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300581}
582
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200583#define WL1271_IRQ_MAX_LOOPS 10
584
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300585static void wl1271_irq_work(struct work_struct *work)
586{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300587 int ret;
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300588 u32 intr;
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200589 int loopcount = WL1271_IRQ_MAX_LOOPS;
590 unsigned long flags;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300591 struct wl1271 *wl =
592 container_of(work, struct wl1271, irq_work);
593
594 mutex_lock(&wl->mutex);
595
596 wl1271_debug(DEBUG_IRQ, "IRQ work");
597
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200598 if (unlikely(wl->state == WL1271_STATE_OFF))
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300599 goto out;
600
601 ret = wl1271_ps_elp_wakeup(wl, true);
602 if (ret < 0)
603 goto out;
604
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200605 spin_lock_irqsave(&wl->wl_lock, flags);
606 while (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags) && loopcount) {
607 clear_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags);
608 spin_unlock_irqrestore(&wl->wl_lock, flags);
609 loopcount--;
610
611 wl1271_fw_status(wl, wl->fw_status);
Eliad Pellerc8bde242011-02-02 09:59:35 +0200612 intr = le32_to_cpu(wl->fw_status->common.intr);
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200613 if (!intr) {
614 wl1271_debug(DEBUG_IRQ, "Zero interrupt received.");
Dan Carpentercdd08642010-05-08 18:25:17 +0200615 spin_lock_irqsave(&wl->wl_lock, flags);
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200616 continue;
617 }
618
619 intr &= WL1271_INTR_MASK;
620
Eliad Pellerccc83b02010-10-27 14:09:57 +0200621 if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) {
622 wl1271_error("watchdog interrupt received! "
623 "starting recovery.");
624 ieee80211_queue_work(wl->hw, &wl->recovery_work);
625
626 /* restarting the chip. ignore any other interrupt. */
627 goto out;
628 }
629
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200630 if (intr & WL1271_ACX_INTR_DATA) {
631 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
632
633 /* check for tx results */
Eliad Pellerc8bde242011-02-02 09:59:35 +0200634 if (wl->fw_status->common.tx_results_counter !=
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200635 (wl->tx_results_count & 0xff))
636 wl1271_tx_complete(wl);
637
Ido Yariva5225502010-10-12 14:49:10 +0200638 /* Check if any tx blocks were freed */
639 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
Juuso Oikarinen6742f552010-12-13 09:52:37 +0200640 wl->tx_queue_count) {
Ido Yariva5225502010-10-12 14:49:10 +0200641 /*
642 * In order to avoid starvation of the TX path,
643 * call the work function directly.
644 */
645 wl1271_tx_work_locked(wl);
646 }
647
Eliad Pellerc8bde242011-02-02 09:59:35 +0200648 wl1271_rx(wl, &wl->fw_status->common);
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200649 }
650
651 if (intr & WL1271_ACX_INTR_EVENT_A) {
652 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
653 wl1271_event_handle(wl, 0);
654 }
655
656 if (intr & WL1271_ACX_INTR_EVENT_B) {
657 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
658 wl1271_event_handle(wl, 1);
659 }
660
661 if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
662 wl1271_debug(DEBUG_IRQ,
663 "WL1271_ACX_INTR_INIT_COMPLETE");
664
665 if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
666 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
667
668 spin_lock_irqsave(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300669 }
670
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200671 if (test_bit(WL1271_FLAG_IRQ_PENDING, &wl->flags))
672 ieee80211_queue_work(wl->hw, &wl->irq_work);
673 else
674 clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
675 spin_unlock_irqrestore(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300676
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300677 wl1271_ps_elp_sleep(wl);
678
679out:
680 mutex_unlock(&wl->mutex);
681}
682
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300683static int wl1271_fetch_firmware(struct wl1271 *wl)
684{
685 const struct firmware *fw;
Arik Nemtsov166d5042010-10-16 21:44:57 +0200686 const char *fw_name;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300687 int ret;
688
Arik Nemtsov166d5042010-10-16 21:44:57 +0200689 switch (wl->bss_type) {
690 case BSS_TYPE_AP_BSS:
691 fw_name = WL1271_AP_FW_NAME;
692 break;
693 case BSS_TYPE_IBSS:
694 case BSS_TYPE_STA_BSS:
695 fw_name = WL1271_FW_NAME;
696 break;
697 default:
698 wl1271_error("no compatible firmware for bss_type %d",
699 wl->bss_type);
700 return -EINVAL;
701 }
702
703 wl1271_debug(DEBUG_BOOT, "booting firmware %s", fw_name);
704
705 ret = request_firmware(&fw, fw_name, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300706
707 if (ret < 0) {
708 wl1271_error("could not get firmware: %d", ret);
709 return ret;
710 }
711
712 if (fw->size % 4) {
713 wl1271_error("firmware size is not multiple of 32 bits: %zu",
714 fw->size);
715 ret = -EILSEQ;
716 goto out;
717 }
718
Arik Nemtsov166d5042010-10-16 21:44:57 +0200719 vfree(wl->fw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300720 wl->fw_len = fw->size;
Juuso Oikarinen1fba4972009-10-08 21:56:32 +0300721 wl->fw = vmalloc(wl->fw_len);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300722
723 if (!wl->fw) {
724 wl1271_error("could not allocate memory for the firmware");
725 ret = -ENOMEM;
726 goto out;
727 }
728
729 memcpy(wl->fw, fw->data, wl->fw_len);
Arik Nemtsov166d5042010-10-16 21:44:57 +0200730 wl->fw_bss_type = wl->bss_type;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300731 ret = 0;
732
733out:
734 release_firmware(fw);
735
736 return ret;
737}
738
739static int wl1271_fetch_nvs(struct wl1271 *wl)
740{
741 const struct firmware *fw;
742 int ret;
743
Teemu Paasikivi8197b712010-02-22 08:38:23 +0200744 ret = request_firmware(&fw, WL1271_NVS_NAME, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300745
746 if (ret < 0) {
747 wl1271_error("could not get nvs file: %d", ret);
748 return ret;
749 }
750
Julia Lawall929ebd32010-05-15 23:16:39 +0200751 wl->nvs = kmemdup(fw->data, sizeof(struct wl1271_nvs_file), GFP_KERNEL);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300752
753 if (!wl->nvs) {
754 wl1271_error("could not allocate memory for the nvs file");
755 ret = -ENOMEM;
756 goto out;
757 }
758
Juuso Oikarinen02fabb02010-08-19 04:41:15 +0200759 wl->nvs_len = fw->size;
760
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300761out:
762 release_firmware(fw);
763
764 return ret;
765}
766
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200767static void wl1271_recovery_work(struct work_struct *work)
768{
769 struct wl1271 *wl =
770 container_of(work, struct wl1271, recovery_work);
771
772 mutex_lock(&wl->mutex);
773
774 if (wl->state != WL1271_STATE_ON)
775 goto out;
776
777 wl1271_info("Hardware recovery in progress.");
778
Juuso Oikarinend25611d2010-09-30 10:43:27 +0200779 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
780 ieee80211_connection_loss(wl->vif);
781
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200782 /* reboot the chipset */
783 __wl1271_op_remove_interface(wl);
784 ieee80211_restart_hw(wl->hw);
785
786out:
787 mutex_unlock(&wl->mutex);
788}
789
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300790static void wl1271_fw_wakeup(struct wl1271 *wl)
791{
792 u32 elp_reg;
793
794 elp_reg = ELPCTRL_WAKE_UP;
Juuso Oikarinen74621412009-10-12 15:08:54 +0300795 wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300796}
797
798static int wl1271_setup(struct wl1271 *wl)
799{
800 wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL);
801 if (!wl->fw_status)
802 return -ENOMEM;
803
804 wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
805 if (!wl->tx_res_if) {
806 kfree(wl->fw_status);
807 return -ENOMEM;
808 }
809
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300810 return 0;
811}
812
813static int wl1271_chip_wakeup(struct wl1271 *wl)
814{
Juuso Oikarinen451de972009-10-12 15:08:46 +0300815 struct wl1271_partition_set partition;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300816 int ret = 0;
817
Juuso Oikarinen01ac17e2009-12-11 15:41:02 +0200818 msleep(WL1271_PRE_POWER_ON_SLEEP);
Ohad Ben-Cohen2cc78ff2010-09-16 01:22:04 +0200819 ret = wl1271_power_on(wl);
820 if (ret < 0)
821 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300822 msleep(WL1271_POWER_ON_SLEEP);
Teemu Paasikivi9b280722010-02-18 13:25:56 +0200823 wl1271_io_reset(wl);
824 wl1271_io_init(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300825
826 /* We don't need a real memory partition here, because we only want
827 * to use the registers at this point. */
Juuso Oikarinen451de972009-10-12 15:08:46 +0300828 memset(&partition, 0, sizeof(partition));
829 partition.reg.start = REGISTERS_BASE;
830 partition.reg.size = REGISTERS_DOWN_SIZE;
831 wl1271_set_partition(wl, &partition);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300832
833 /* ELP module wake up */
834 wl1271_fw_wakeup(wl);
835
836 /* whal_FwCtrl_BootSm() */
837
838 /* 0. read chip id from CHIP_ID */
Teemu Paasikivi7b048c52010-02-18 13:25:55 +0200839 wl->chip.id = wl1271_read32(wl, CHIP_ID_B);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300840
841 /* 1. check if chip id is valid */
842
843 switch (wl->chip.id) {
844 case CHIP_ID_1271_PG10:
845 wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
846 wl->chip.id);
847
848 ret = wl1271_setup(wl);
849 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200850 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300851 break;
852 case CHIP_ID_1271_PG20:
853 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
854 wl->chip.id);
855
856 ret = wl1271_setup(wl);
857 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200858 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300859 break;
860 default:
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200861 wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300862 ret = -ENODEV;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200863 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300864 }
865
Arik Nemtsov166d5042010-10-16 21:44:57 +0200866 /* Make sure the firmware type matches the BSS type */
867 if (wl->fw == NULL || wl->fw_bss_type != wl->bss_type) {
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300868 ret = wl1271_fetch_firmware(wl);
869 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200870 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300871 }
872
873 /* No NVS from netlink, try to get it from the filesystem */
874 if (wl->nvs == NULL) {
875 ret = wl1271_fetch_nvs(wl);
876 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200877 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300878 }
879
880out:
881 return ret;
882}
883
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300884int wl1271_plt_start(struct wl1271 *wl)
885{
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200886 int retries = WL1271_BOOT_RETRIES;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300887 int ret;
888
889 mutex_lock(&wl->mutex);
890
891 wl1271_notice("power up");
892
893 if (wl->state != WL1271_STATE_OFF) {
894 wl1271_error("cannot go into PLT state because not "
895 "in off state: %d", wl->state);
896 ret = -EBUSY;
897 goto out;
898 }
899
Arik Nemtsov166d5042010-10-16 21:44:57 +0200900 wl->bss_type = BSS_TYPE_STA_BSS;
901
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200902 while (retries) {
903 retries--;
904 ret = wl1271_chip_wakeup(wl);
905 if (ret < 0)
906 goto power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300907
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200908 ret = wl1271_boot(wl);
909 if (ret < 0)
910 goto power_off;
911
912 ret = wl1271_plt_init(wl);
913 if (ret < 0)
914 goto irq_disable;
915
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200916 wl->state = WL1271_STATE_PLT;
917 wl1271_notice("firmware booted in PLT mode (%s)",
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100918 wl->chip.fw_ver_str);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300919 goto out;
920
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200921irq_disable:
922 wl1271_disable_interrupts(wl);
923 mutex_unlock(&wl->mutex);
924 /* Unlocking the mutex in the middle of handling is
925 inherently unsafe. In this case we deem it safe to do,
926 because we need to let any possibly pending IRQ out of
927 the system (and while we are WL1271_STATE_OFF the IRQ
928 work function will not do anything.) Also, any other
929 possible concurrent operations will fail due to the
930 current state, hence the wl1271 struct should be safe. */
931 cancel_work_sync(&wl->irq_work);
932 mutex_lock(&wl->mutex);
933power_off:
934 wl1271_power_off(wl);
935 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300936
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +0200937 wl1271_error("firmware boot in PLT mode failed despite %d retries",
938 WL1271_BOOT_RETRIES);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300939out:
940 mutex_unlock(&wl->mutex);
941
942 return ret;
943}
944
Juuso Oikarinen4ae3fa82011-01-14 12:48:46 +0100945int __wl1271_plt_stop(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300946{
947 int ret = 0;
948
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300949 wl1271_notice("power down");
950
951 if (wl->state != WL1271_STATE_PLT) {
952 wl1271_error("cannot power down because not in PLT "
953 "state: %d", wl->state);
954 ret = -EBUSY;
955 goto out;
956 }
957
958 wl1271_disable_interrupts(wl);
959 wl1271_power_off(wl);
960
961 wl->state = WL1271_STATE_OFF;
Luciano Coelhobd5ea182009-10-13 12:47:58 +0300962 wl->rx_counter = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300963
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300964 mutex_unlock(&wl->mutex);
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +0200965 cancel_work_sync(&wl->irq_work);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200966 cancel_work_sync(&wl->recovery_work);
Juuso Oikarinen4ae3fa82011-01-14 12:48:46 +0100967 mutex_lock(&wl->mutex);
968out:
969 return ret;
970}
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +0200971
Juuso Oikarinen4ae3fa82011-01-14 12:48:46 +0100972int wl1271_plt_stop(struct wl1271 *wl)
973{
974 int ret;
975
976 mutex_lock(&wl->mutex);
977 ret = __wl1271_plt_stop(wl);
978 mutex_unlock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300979 return ret;
980}
981
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300982static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
983{
984 struct wl1271 *wl = hw->priv;
Juuso Oikarinen830fb672009-12-11 15:41:06 +0200985 unsigned long flags;
Juuso Oikarinen6742f552010-12-13 09:52:37 +0200986 int q;
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +0200987 u8 hlid = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300988
Juuso Oikarinen830fb672009-12-11 15:41:06 +0200989 spin_lock_irqsave(&wl->wl_lock, flags);
Juuso Oikarinen6742f552010-12-13 09:52:37 +0200990 wl->tx_queue_count++;
Arik Nemtsovf4d08dd2011-02-23 00:22:24 +0200991
992 /*
993 * The workqueue is slow to process the tx_queue and we need stop
994 * the queue here, otherwise the queue will get too long.
995 */
996 if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
997 wl1271_debug(DEBUG_TX, "op_tx: stopping queues");
998 ieee80211_stop_queues(wl->hw);
999 set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
1000 }
1001
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001002 spin_unlock_irqrestore(&wl->wl_lock, flags);
1003
1004 /* queue the packet */
Juuso Oikarinen6742f552010-12-13 09:52:37 +02001005 q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02001006 if (wl->bss_type == BSS_TYPE_AP_BSS) {
1007 hlid = wl1271_tx_get_hlid(skb);
1008 wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q);
1009 skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
1010 } else {
1011 skb_queue_tail(&wl->tx_queue[q], skb);
1012 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001013
1014 /*
1015 * The chip specific setup must run before the first TX packet -
1016 * before that, the tx_work will not be initialized!
1017 */
1018
Ido Yariva5225502010-10-12 14:49:10 +02001019 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
1020 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001021
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001022 return NETDEV_TX_OK;
1023}
1024
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03001025static struct notifier_block wl1271_dev_notifier = {
1026 .notifier_call = wl1271_dev_notify,
1027};
1028
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001029static int wl1271_op_start(struct ieee80211_hw *hw)
1030{
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001031 wl1271_debug(DEBUG_MAC80211, "mac80211 start");
1032
1033 /*
1034 * We have to delay the booting of the hardware because
1035 * we need to know the local MAC address before downloading and
1036 * initializing the firmware. The MAC address cannot be changed
1037 * after boot, and without the proper MAC address, the firmware
1038 * will not function properly.
1039 *
1040 * The MAC address is first known when the corresponding interface
1041 * is added. That is where we will initialize the hardware.
Arik Nemtsov166d5042010-10-16 21:44:57 +02001042 *
1043 * In addition, we currently have different firmwares for AP and managed
1044 * operation. We will know which to boot according to interface type.
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001045 */
1046
1047 return 0;
1048}
1049
1050static void wl1271_op_stop(struct ieee80211_hw *hw)
1051{
1052 wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
1053}
1054
1055static int wl1271_op_add_interface(struct ieee80211_hw *hw,
1056 struct ieee80211_vif *vif)
1057{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001058 struct wl1271 *wl = hw->priv;
John W. Linvilleac01e942010-07-28 17:09:41 -04001059 struct wiphy *wiphy = hw->wiphy;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001060 int retries = WL1271_BOOT_RETRIES;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001061 int ret = 0;
Eliad Peller71125ab2010-10-28 21:46:43 +02001062 bool booted = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001063
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001064 wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
1065 vif->type, vif->addr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001066
1067 mutex_lock(&wl->mutex);
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001068 if (wl->vif) {
Eliad Peller71125ab2010-10-28 21:46:43 +02001069 wl1271_debug(DEBUG_MAC80211,
1070 "multiple vifs are not supported yet");
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001071 ret = -EBUSY;
1072 goto out;
1073 }
1074
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001075 switch (vif->type) {
1076 case NL80211_IFTYPE_STATION:
1077 wl->bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001078 wl->set_bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001079 break;
1080 case NL80211_IFTYPE_ADHOC:
1081 wl->bss_type = BSS_TYPE_IBSS;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001082 wl->set_bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001083 break;
Arik Nemtsov038d9252010-10-16 21:53:24 +02001084 case NL80211_IFTYPE_AP:
1085 wl->bss_type = BSS_TYPE_AP_BSS;
1086 break;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001087 default:
1088 ret = -EOPNOTSUPP;
1089 goto out;
1090 }
1091
1092 memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001093
1094 if (wl->state != WL1271_STATE_OFF) {
1095 wl1271_error("cannot start because not in off state: %d",
1096 wl->state);
1097 ret = -EBUSY;
1098 goto out;
1099 }
1100
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001101 while (retries) {
1102 retries--;
1103 ret = wl1271_chip_wakeup(wl);
1104 if (ret < 0)
1105 goto power_off;
1106
1107 ret = wl1271_boot(wl);
1108 if (ret < 0)
1109 goto power_off;
1110
1111 ret = wl1271_hw_init(wl);
1112 if (ret < 0)
1113 goto irq_disable;
1114
Eliad Peller71125ab2010-10-28 21:46:43 +02001115 booted = true;
1116 break;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001117
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001118irq_disable:
1119 wl1271_disable_interrupts(wl);
1120 mutex_unlock(&wl->mutex);
1121 /* Unlocking the mutex in the middle of handling is
1122 inherently unsafe. In this case we deem it safe to do,
1123 because we need to let any possibly pending IRQ out of
1124 the system (and while we are WL1271_STATE_OFF the IRQ
1125 work function will not do anything.) Also, any other
1126 possible concurrent operations will fail due to the
1127 current state, hence the wl1271 struct should be safe. */
1128 cancel_work_sync(&wl->irq_work);
1129 mutex_lock(&wl->mutex);
1130power_off:
1131 wl1271_power_off(wl);
1132 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001133
Eliad Peller71125ab2010-10-28 21:46:43 +02001134 if (!booted) {
1135 wl1271_error("firmware boot failed despite %d retries",
1136 WL1271_BOOT_RETRIES);
1137 goto out;
1138 }
1139
1140 wl->vif = vif;
1141 wl->state = WL1271_STATE_ON;
Levi, Shahar4b7fac72011-01-23 07:27:22 +01001142 wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str);
Eliad Peller71125ab2010-10-28 21:46:43 +02001143
1144 /* update hw/fw version info in wiphy struct */
1145 wiphy->hw_version = wl->chip.id;
Levi, Shahar4b7fac72011-01-23 07:27:22 +01001146 strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
Eliad Peller71125ab2010-10-28 21:46:43 +02001147 sizeof(wiphy->fw_version));
1148
Luciano Coelhofb6a6812010-12-03 17:05:40 +02001149 /*
1150 * Now we know if 11a is supported (info from the NVS), so disable
1151 * 11a channels if not supported
1152 */
1153 if (!wl->enable_11a)
1154 wiphy->bands[IEEE80211_BAND_5GHZ]->n_channels = 0;
1155
1156 wl1271_debug(DEBUG_MAC80211, "11a is %ssupported",
1157 wl->enable_11a ? "" : "not ");
1158
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +03001159out:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001160 mutex_unlock(&wl->mutex);
1161
Juuso Oikarineneb887df2010-07-08 17:49:58 +03001162 if (!ret)
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001163 list_add(&wl->list, &wl_list);
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001164
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001165 return ret;
1166}
1167
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001168static void __wl1271_op_remove_interface(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001169{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001170 int i;
1171
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001172 wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001173
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001174 wl1271_info("down");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001175
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001176 list_del(&wl->list);
1177
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001178 WARN_ON(wl->state != WL1271_STATE_ON);
1179
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001180 /* enable dyn ps just in case (if left on due to fw crash etc) */
Juuso Oikarinen9a547bf2010-07-08 17:50:04 +03001181 if (wl->bss_type == BSS_TYPE_STA_BSS)
Juuso Oikarinenf532be62010-07-08 17:50:05 +03001182 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001183
Luciano Coelho08688d62010-07-08 17:50:07 +03001184 if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
Luciano Coelho08688d62010-07-08 17:50:07 +03001185 wl->scan.state = WL1271_SCAN_STATE_IDLE;
1186 kfree(wl->scan.scanned_ch);
1187 wl->scan.scanned_ch = NULL;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02001188 wl->scan.req = NULL;
Juuso Oikarinen76a029f2010-07-29 04:54:45 +03001189 ieee80211_scan_completed(wl->hw, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001190 }
1191
1192 wl->state = WL1271_STATE_OFF;
1193
1194 wl1271_disable_interrupts(wl);
1195
1196 mutex_unlock(&wl->mutex);
1197
Juuso Oikarinen78abd322010-09-21 06:23:32 +02001198 cancel_delayed_work_sync(&wl->scan_complete_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001199 cancel_work_sync(&wl->irq_work);
1200 cancel_work_sync(&wl->tx_work);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001201 cancel_delayed_work_sync(&wl->pspoll_work);
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +02001202 cancel_delayed_work_sync(&wl->elp_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001203
1204 mutex_lock(&wl->mutex);
1205
1206 /* let's notify MAC80211 about the remaining pending TX frames */
Juuso Oikarinen781608c2010-05-24 11:18:17 +03001207 wl1271_tx_reset(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001208 wl1271_power_off(wl);
1209
1210 memset(wl->bssid, 0, ETH_ALEN);
1211 memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1);
1212 wl->ssid_len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001213 wl->bss_type = MAX_BSS_TYPE;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001214 wl->set_bss_type = MAX_BSS_TYPE;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001215 wl->band = IEEE80211_BAND_2GHZ;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001216
1217 wl->rx_counter = 0;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02001218 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001219 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
1220 wl->tx_blocks_available = 0;
1221 wl->tx_results_count = 0;
1222 wl->tx_packets_count = 0;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001223 wl->tx_security_last_seq = 0;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001224 wl->tx_security_seq = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001225 wl->time_offset = 0;
1226 wl->session_counter = 0;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001227 wl->rate_set = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001228 wl->flags = 0;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001229 wl->vif = NULL;
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001230 wl->filters = 0;
Arik Nemtsov7f179b42010-10-16 21:39:06 +02001231 wl1271_free_ap_keys(wl);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02001232 memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));
Luciano Coelhod6e19d12009-10-12 15:08:43 +03001233
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001234 for (i = 0; i < NUM_TX_QUEUES; i++)
1235 wl->tx_blocks_freed[i] = 0;
1236
1237 wl1271_debugfs_reset(wl);
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03001238
1239 kfree(wl->fw_status);
1240 wl->fw_status = NULL;
1241 kfree(wl->tx_res_if);
1242 wl->tx_res_if = NULL;
1243 kfree(wl->target_mem_map);
1244 wl->target_mem_map = NULL;
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001245}
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03001246
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001247static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
1248 struct ieee80211_vif *vif)
1249{
1250 struct wl1271 *wl = hw->priv;
1251
1252 mutex_lock(&wl->mutex);
Juuso Oikarinen67353292010-11-18 15:19:02 +02001253 /*
1254 * wl->vif can be null here if someone shuts down the interface
1255 * just when hardware recovery has been started.
1256 */
1257 if (wl->vif) {
1258 WARN_ON(wl->vif != vif);
1259 __wl1271_op_remove_interface(wl);
1260 }
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001261
Juuso Oikarinen67353292010-11-18 15:19:02 +02001262 mutex_unlock(&wl->mutex);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001263 cancel_work_sync(&wl->recovery_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001264}
1265
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001266static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
1267{
Arik Nemtsovae113b52010-10-16 18:45:07 +02001268 wl1271_set_default_filters(wl);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001269
1270 /* combine requested filters with current filter config */
1271 filters = wl->filters | filters;
1272
1273 wl1271_debug(DEBUG_FILTERS, "RX filters set: ");
1274
1275 if (filters & FIF_PROMISC_IN_BSS) {
1276 wl1271_debug(DEBUG_FILTERS, " - FIF_PROMISC_IN_BSS");
1277 wl->rx_config &= ~CFG_UNI_FILTER_EN;
1278 wl->rx_config |= CFG_BSSID_FILTER_EN;
1279 }
1280 if (filters & FIF_BCN_PRBRESP_PROMISC) {
1281 wl1271_debug(DEBUG_FILTERS, " - FIF_BCN_PRBRESP_PROMISC");
1282 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1283 wl->rx_config &= ~CFG_SSID_FILTER_EN;
1284 }
1285 if (filters & FIF_OTHER_BSS) {
1286 wl1271_debug(DEBUG_FILTERS, " - FIF_OTHER_BSS");
1287 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1288 }
1289 if (filters & FIF_CONTROL) {
1290 wl1271_debug(DEBUG_FILTERS, " - FIF_CONTROL");
1291 wl->rx_filter |= CFG_RX_CTL_EN;
1292 }
1293 if (filters & FIF_FCSFAIL) {
1294 wl1271_debug(DEBUG_FILTERS, " - FIF_FCSFAIL");
1295 wl->rx_filter |= CFG_RX_FCS_ERROR;
1296 }
1297}
1298
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001299static int wl1271_dummy_join(struct wl1271 *wl)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001300{
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001301 int ret = 0;
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001302 /* we need to use a dummy BSSID for now */
1303 static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
1304 0xad, 0xbe, 0xef };
1305
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001306 memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
1307
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001308 /* pass through frames from all BSS */
1309 wl1271_configure_filters(wl, FIF_OTHER_BSS);
1310
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001311 ret = wl1271_cmd_join(wl, wl->set_bss_type);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001312 if (ret < 0)
1313 goto out;
1314
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001315 set_bit(WL1271_FLAG_JOINED, &wl->flags);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001316
1317out:
1318 return ret;
1319}
1320
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001321static int wl1271_join(struct wl1271 *wl, bool set_assoc)
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001322{
1323 int ret;
1324
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001325 /*
1326 * One of the side effects of the JOIN command is that is clears
1327 * WPA/WPA2 keys from the chipset. Performing a JOIN while associated
1328 * to a WPA/WPA2 access point will therefore kill the data-path.
1329 * Currently there is no supported scenario for JOIN during
1330 * association - if it becomes a supported scenario, the WPA/WPA2 keys
1331 * must be handled somehow.
1332 *
1333 */
1334 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1335 wl1271_info("JOIN while associated.");
1336
1337 if (set_assoc)
1338 set_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
1339
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001340 ret = wl1271_cmd_join(wl, wl->set_bss_type);
1341 if (ret < 0)
1342 goto out;
1343
1344 set_bit(WL1271_FLAG_JOINED, &wl->flags);
1345
1346 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1347 goto out;
1348
1349 /*
1350 * The join command disable the keep-alive mode, shut down its process,
1351 * and also clear the template config, so we need to reset it all after
1352 * the join. The acx_aid starts the keep-alive process, and the order
1353 * of the commands below is relevant.
1354 */
1355 ret = wl1271_acx_keep_alive_mode(wl, true);
1356 if (ret < 0)
1357 goto out;
1358
1359 ret = wl1271_acx_aid(wl, wl->aid);
1360 if (ret < 0)
1361 goto out;
1362
1363 ret = wl1271_cmd_build_klv_null_data(wl);
1364 if (ret < 0)
1365 goto out;
1366
1367 ret = wl1271_acx_keep_alive_config(wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
1368 ACX_KEEP_ALIVE_TPL_VALID);
1369 if (ret < 0)
1370 goto out;
1371
1372out:
1373 return ret;
1374}
1375
1376static int wl1271_unjoin(struct wl1271 *wl)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001377{
1378 int ret;
1379
1380 /* to stop listening to a channel, we disconnect */
1381 ret = wl1271_cmd_disconnect(wl);
1382 if (ret < 0)
1383 goto out;
1384
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001385 clear_bit(WL1271_FLAG_JOINED, &wl->flags);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001386 memset(wl->bssid, 0, ETH_ALEN);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001387
1388 /* stop filterting packets based on bssid */
1389 wl1271_configure_filters(wl, FIF_OTHER_BSS);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001390
1391out:
1392 return ret;
1393}
1394
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001395static void wl1271_set_band_rate(struct wl1271 *wl)
1396{
1397 if (wl->band == IEEE80211_BAND_2GHZ)
1398 wl->basic_rate_set = wl->conf.tx.basic_rate;
1399 else
1400 wl->basic_rate_set = wl->conf.tx.basic_rate_5;
1401}
1402
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001403static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle)
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03001404{
1405 int ret;
1406
1407 if (idle) {
1408 if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
1409 ret = wl1271_unjoin(wl);
1410 if (ret < 0)
1411 goto out;
1412 }
Arik Nemtsove0fe3712010-10-16 18:19:53 +02001413 wl->rate_set = wl1271_tx_min_rate_get(wl);
Arik Nemtsov79b223f2010-10-16 17:52:59 +02001414 ret = wl1271_acx_sta_rate_policies(wl);
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03001415 if (ret < 0)
1416 goto out;
1417 ret = wl1271_acx_keep_alive_config(
1418 wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
1419 ACX_KEEP_ALIVE_TPL_INVALID);
1420 if (ret < 0)
1421 goto out;
1422 set_bit(WL1271_FLAG_IDLE, &wl->flags);
1423 } else {
1424 /* increment the session counter */
1425 wl->session_counter++;
1426 if (wl->session_counter >= SESSION_COUNTER_MAX)
1427 wl->session_counter = 0;
1428 ret = wl1271_dummy_join(wl);
1429 if (ret < 0)
1430 goto out;
1431 clear_bit(WL1271_FLAG_IDLE, &wl->flags);
1432 }
1433
1434out:
1435 return ret;
1436}
1437
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001438static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
1439{
1440 struct wl1271 *wl = hw->priv;
1441 struct ieee80211_conf *conf = &hw->conf;
1442 int channel, ret = 0;
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001443 bool is_ap;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001444
1445 channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
1446
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001447 wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s"
1448 " changed 0x%x",
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001449 channel,
1450 conf->flags & IEEE80211_CONF_PS ? "on" : "off",
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001451 conf->power_level,
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001452 conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
1453 changed);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001454
Juuso Oikarinen781608c2010-05-24 11:18:17 +03001455 /*
1456 * mac80211 will go to idle nearly immediately after transmitting some
1457 * frames, such as the deauth. To make sure those frames reach the air,
1458 * wait here until the TX queue is fully flushed.
1459 */
1460 if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
1461 (conf->flags & IEEE80211_CONF_IDLE))
1462 wl1271_tx_flush(wl);
1463
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001464 mutex_lock(&wl->mutex);
1465
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02001466 if (unlikely(wl->state == WL1271_STATE_OFF)) {
1467 ret = -EAGAIN;
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001468 goto out;
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02001469 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001470
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001471 is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
1472
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001473 ret = wl1271_ps_elp_wakeup(wl, false);
1474 if (ret < 0)
1475 goto out;
1476
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001477 /* if the channel changes while joined, join again */
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001478 if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
1479 ((wl->band != conf->channel->band) ||
1480 (wl->channel != channel))) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001481 wl->band = conf->channel->band;
1482 wl->channel = channel;
1483
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001484 if (!is_ap) {
1485 /*
1486 * FIXME: the mac80211 should really provide a fixed
1487 * rate to use here. for now, just use the smallest
1488 * possible rate for the band as a fixed rate for
1489 * association frames and other control messages.
1490 */
1491 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1492 wl1271_set_band_rate(wl);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001493
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001494 wl->basic_rate = wl1271_tx_min_rate_get(wl);
1495 ret = wl1271_acx_sta_rate_policies(wl);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001496 if (ret < 0)
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001497 wl1271_warning("rate policy for channel "
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001498 "failed %d", ret);
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001499
1500 if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
1501 ret = wl1271_join(wl, false);
1502 if (ret < 0)
1503 wl1271_warning("cmd join on channel "
1504 "failed %d", ret);
1505 }
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03001506 }
1507 }
1508
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02001509 if (changed & IEEE80211_CONF_CHANGE_IDLE && !is_ap) {
1510 ret = wl1271_sta_handle_idle(wl,
1511 conf->flags & IEEE80211_CONF_IDLE);
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03001512 if (ret < 0)
1513 wl1271_warning("idle mode change failed %d", ret);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001514 }
1515
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001516 /*
1517 * if mac80211 changes the PSM mode, make sure the mode is not
1518 * incorrectly changed after the pspoll failure active window.
1519 */
1520 if (changed & IEEE80211_CONF_CHANGE_PS)
1521 clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
1522
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001523 if (conf->flags & IEEE80211_CONF_PS &&
1524 !test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
1525 set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001526
1527 /*
1528 * We enter PSM only if we're already associated.
1529 * If we're not, we'll enter it when joining an SSID,
1530 * through the bss_info_changed() hook.
1531 */
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001532 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02001533 wl1271_debug(DEBUG_PSM, "psm enabled");
Juuso Oikarinend8c42c02010-02-18 13:25:36 +02001534 ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02001535 wl->basic_rate, true);
Juuso Oikarinenaf5e0842009-12-11 15:41:00 +02001536 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001537 } else if (!(conf->flags & IEEE80211_CONF_PS) &&
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001538 test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02001539 wl1271_debug(DEBUG_PSM, "psm disabled");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001540
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001541 clear_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001542
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001543 if (test_bit(WL1271_FLAG_PSM, &wl->flags))
Juuso Oikarinend8c42c02010-02-18 13:25:36 +02001544 ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02001545 wl->basic_rate, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001546 }
1547
1548 if (conf->power_level != wl->power_level) {
1549 ret = wl1271_acx_tx_power(wl, conf->power_level);
1550 if (ret < 0)
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02001551 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001552
1553 wl->power_level = conf->power_level;
1554 }
1555
1556out_sleep:
1557 wl1271_ps_elp_sleep(wl);
1558
1559out:
1560 mutex_unlock(&wl->mutex);
1561
1562 return ret;
1563}
1564
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001565struct wl1271_filter_params {
1566 bool enabled;
1567 int mc_list_length;
1568 u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
1569};
1570
Jiri Pirko22bedad2010-04-01 21:22:57 +00001571static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw,
1572 struct netdev_hw_addr_list *mc_list)
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001573{
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001574 struct wl1271_filter_params *fp;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001575 struct netdev_hw_addr *ha;
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001576 struct wl1271 *wl = hw->priv;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001577
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001578 if (unlikely(wl->state == WL1271_STATE_OFF))
1579 return 0;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001580
Juuso Oikarinen74441132009-10-13 12:47:53 +03001581 fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001582 if (!fp) {
1583 wl1271_error("Out of memory setting filters.");
1584 return 0;
1585 }
1586
1587 /* update multicast filtering parameters */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001588 fp->mc_list_length = 0;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001589 if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) {
1590 fp->enabled = false;
1591 } else {
1592 fp->enabled = true;
1593 netdev_hw_addr_list_for_each(ha, mc_list) {
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001594 memcpy(fp->mc_list[fp->mc_list_length],
Jiri Pirko22bedad2010-04-01 21:22:57 +00001595 ha->addr, ETH_ALEN);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001596 fp->mc_list_length++;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001597 }
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001598 }
1599
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001600 return (u64)(unsigned long)fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001601}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001602
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001603#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
1604 FIF_ALLMULTI | \
1605 FIF_FCSFAIL | \
1606 FIF_BCN_PRBRESP_PROMISC | \
1607 FIF_CONTROL | \
1608 FIF_OTHER_BSS)
1609
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001610static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
1611 unsigned int changed,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001612 unsigned int *total, u64 multicast)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001613{
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001614 struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001615 struct wl1271 *wl = hw->priv;
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001616 int ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001617
Arik Nemtsov7d057862010-10-16 19:25:35 +02001618 wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter changed %x"
1619 " total %x", changed, *total);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001620
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001621 mutex_lock(&wl->mutex);
1622
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03001623 *total &= WL1271_SUPPORTED_FILTERS;
1624 changed &= WL1271_SUPPORTED_FILTERS;
1625
1626 if (unlikely(wl->state == WL1271_STATE_OFF))
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001627 goto out;
1628
1629 ret = wl1271_ps_elp_wakeup(wl, false);
1630 if (ret < 0)
1631 goto out;
1632
Arik Nemtsov7d057862010-10-16 19:25:35 +02001633 if (wl->bss_type != BSS_TYPE_AP_BSS) {
1634 if (*total & FIF_ALLMULTI)
1635 ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
1636 else if (fp)
1637 ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
1638 fp->mc_list,
1639 fp->mc_list_length);
1640 if (ret < 0)
1641 goto out_sleep;
1642 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001643
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001644 /* determine, whether supported filter values have changed */
1645 if (changed == 0)
1646 goto out_sleep;
1647
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001648 /* configure filters */
1649 wl->filters = *total;
1650 wl1271_configure_filters(wl, 0);
1651
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001652 /* apply configured filters */
1653 ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
1654 if (ret < 0)
1655 goto out_sleep;
1656
1657out_sleep:
1658 wl1271_ps_elp_sleep(wl);
1659
1660out:
1661 mutex_unlock(&wl->mutex);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001662 kfree(fp);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001663}
1664
Arik Nemtsov7f179b42010-10-16 21:39:06 +02001665static int wl1271_record_ap_key(struct wl1271 *wl, u8 id, u8 key_type,
1666 u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
1667 u16 tx_seq_16)
1668{
1669 struct wl1271_ap_key *ap_key;
1670 int i;
1671
1672 wl1271_debug(DEBUG_CRYPT, "record ap key id %d", (int)id);
1673
1674 if (key_size > MAX_KEY_SIZE)
1675 return -EINVAL;
1676
1677 /*
1678 * Find next free entry in ap_keys. Also check we are not replacing
1679 * an existing key.
1680 */
1681 for (i = 0; i < MAX_NUM_KEYS; i++) {
1682 if (wl->recorded_ap_keys[i] == NULL)
1683 break;
1684
1685 if (wl->recorded_ap_keys[i]->id == id) {
1686 wl1271_warning("trying to record key replacement");
1687 return -EINVAL;
1688 }
1689 }
1690
1691 if (i == MAX_NUM_KEYS)
1692 return -EBUSY;
1693
1694 ap_key = kzalloc(sizeof(*ap_key), GFP_KERNEL);
1695 if (!ap_key)
1696 return -ENOMEM;
1697
1698 ap_key->id = id;
1699 ap_key->key_type = key_type;
1700 ap_key->key_size = key_size;
1701 memcpy(ap_key->key, key, key_size);
1702 ap_key->hlid = hlid;
1703 ap_key->tx_seq_32 = tx_seq_32;
1704 ap_key->tx_seq_16 = tx_seq_16;
1705
1706 wl->recorded_ap_keys[i] = ap_key;
1707 return 0;
1708}
1709
1710static void wl1271_free_ap_keys(struct wl1271 *wl)
1711{
1712 int i;
1713
1714 for (i = 0; i < MAX_NUM_KEYS; i++) {
1715 kfree(wl->recorded_ap_keys[i]);
1716 wl->recorded_ap_keys[i] = NULL;
1717 }
1718}
1719
1720static int wl1271_ap_init_hwenc(struct wl1271 *wl)
1721{
1722 int i, ret = 0;
1723 struct wl1271_ap_key *key;
1724 bool wep_key_added = false;
1725
1726 for (i = 0; i < MAX_NUM_KEYS; i++) {
1727 if (wl->recorded_ap_keys[i] == NULL)
1728 break;
1729
1730 key = wl->recorded_ap_keys[i];
1731 ret = wl1271_cmd_set_ap_key(wl, KEY_ADD_OR_REPLACE,
1732 key->id, key->key_type,
1733 key->key_size, key->key,
1734 key->hlid, key->tx_seq_32,
1735 key->tx_seq_16);
1736 if (ret < 0)
1737 goto out;
1738
1739 if (key->key_type == KEY_WEP)
1740 wep_key_added = true;
1741 }
1742
1743 if (wep_key_added) {
1744 ret = wl1271_cmd_set_ap_default_wep_key(wl, wl->default_key);
1745 if (ret < 0)
1746 goto out;
1747 }
1748
1749out:
1750 wl1271_free_ap_keys(wl);
1751 return ret;
1752}
1753
1754static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
1755 u8 key_size, const u8 *key, u32 tx_seq_32,
1756 u16 tx_seq_16, struct ieee80211_sta *sta)
1757{
1758 int ret;
1759 bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
1760
1761 if (is_ap) {
1762 struct wl1271_station *wl_sta;
1763 u8 hlid;
1764
1765 if (sta) {
1766 wl_sta = (struct wl1271_station *)sta->drv_priv;
1767 hlid = wl_sta->hlid;
1768 } else {
1769 hlid = WL1271_AP_BROADCAST_HLID;
1770 }
1771
1772 if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
1773 /*
1774 * We do not support removing keys after AP shutdown.
1775 * Pretend we do to make mac80211 happy.
1776 */
1777 if (action != KEY_ADD_OR_REPLACE)
1778 return 0;
1779
1780 ret = wl1271_record_ap_key(wl, id,
1781 key_type, key_size,
1782 key, hlid, tx_seq_32,
1783 tx_seq_16);
1784 } else {
1785 ret = wl1271_cmd_set_ap_key(wl, action,
1786 id, key_type, key_size,
1787 key, hlid, tx_seq_32,
1788 tx_seq_16);
1789 }
1790
1791 if (ret < 0)
1792 return ret;
1793 } else {
1794 const u8 *addr;
1795 static const u8 bcast_addr[ETH_ALEN] = {
1796 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
1797 };
1798
1799 addr = sta ? sta->addr : bcast_addr;
1800
1801 if (is_zero_ether_addr(addr)) {
1802 /* We dont support TX only encryption */
1803 return -EOPNOTSUPP;
1804 }
1805
1806 /* The wl1271 does not allow to remove unicast keys - they
1807 will be cleared automatically on next CMD_JOIN. Ignore the
1808 request silently, as we dont want the mac80211 to emit
1809 an error message. */
1810 if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr))
1811 return 0;
1812
1813 ret = wl1271_cmd_set_sta_key(wl, action,
1814 id, key_type, key_size,
1815 key, addr, tx_seq_32,
1816 tx_seq_16);
1817 if (ret < 0)
1818 return ret;
1819
1820 /* the default WEP key needs to be configured at least once */
1821 if (key_type == KEY_WEP) {
1822 ret = wl1271_cmd_set_sta_default_wep_key(wl,
1823 wl->default_key);
1824 if (ret < 0)
1825 return ret;
1826 }
1827 }
1828
1829 return 0;
1830}
1831
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001832static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
1833 struct ieee80211_vif *vif,
1834 struct ieee80211_sta *sta,
1835 struct ieee80211_key_conf *key_conf)
1836{
1837 struct wl1271 *wl = hw->priv;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001838 int ret;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001839 u32 tx_seq_32 = 0;
1840 u16 tx_seq_16 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001841 u8 key_type;
1842
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001843 wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
1844
Arik Nemtsov7f179b42010-10-16 21:39:06 +02001845 wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001846 wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
Johannes Berg97359d12010-08-10 09:46:38 +02001847 key_conf->cipher, key_conf->keyidx,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001848 key_conf->keylen, key_conf->flags);
1849 wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
1850
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001851 mutex_lock(&wl->mutex);
1852
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02001853 if (unlikely(wl->state == WL1271_STATE_OFF)) {
1854 ret = -EAGAIN;
1855 goto out_unlock;
1856 }
1857
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001858 ret = wl1271_ps_elp_wakeup(wl, false);
1859 if (ret < 0)
1860 goto out_unlock;
1861
Johannes Berg97359d12010-08-10 09:46:38 +02001862 switch (key_conf->cipher) {
1863 case WLAN_CIPHER_SUITE_WEP40:
1864 case WLAN_CIPHER_SUITE_WEP104:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001865 key_type = KEY_WEP;
1866
1867 key_conf->hw_key_idx = key_conf->keyidx;
1868 break;
Johannes Berg97359d12010-08-10 09:46:38 +02001869 case WLAN_CIPHER_SUITE_TKIP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001870 key_type = KEY_TKIP;
1871
1872 key_conf->hw_key_idx = key_conf->keyidx;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001873 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1874 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001875 break;
Johannes Berg97359d12010-08-10 09:46:38 +02001876 case WLAN_CIPHER_SUITE_CCMP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001877 key_type = KEY_AES;
1878
1879 key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001880 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1881 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001882 break;
Juuso Oikarinen7a557242010-09-27 12:42:07 +02001883 case WL1271_CIPHER_SUITE_GEM:
1884 key_type = KEY_GEM;
1885 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
1886 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
1887 break;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001888 default:
Johannes Berg97359d12010-08-10 09:46:38 +02001889 wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001890
1891 ret = -EOPNOTSUPP;
1892 goto out_sleep;
1893 }
1894
1895 switch (cmd) {
1896 case SET_KEY:
Arik Nemtsov7f179b42010-10-16 21:39:06 +02001897 ret = wl1271_set_key(wl, KEY_ADD_OR_REPLACE,
1898 key_conf->keyidx, key_type,
1899 key_conf->keylen, key_conf->key,
1900 tx_seq_32, tx_seq_16, sta);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001901 if (ret < 0) {
1902 wl1271_error("Could not add or replace key");
1903 goto out_sleep;
1904 }
1905 break;
1906
1907 case DISABLE_KEY:
Arik Nemtsov7f179b42010-10-16 21:39:06 +02001908 ret = wl1271_set_key(wl, KEY_REMOVE,
1909 key_conf->keyidx, key_type,
1910 key_conf->keylen, key_conf->key,
1911 0, 0, sta);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001912 if (ret < 0) {
1913 wl1271_error("Could not remove key");
1914 goto out_sleep;
1915 }
1916 break;
1917
1918 default:
1919 wl1271_error("Unsupported key cmd 0x%x", cmd);
1920 ret = -EOPNOTSUPP;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001921 break;
1922 }
1923
1924out_sleep:
1925 wl1271_ps_elp_sleep(wl);
1926
1927out_unlock:
1928 mutex_unlock(&wl->mutex);
1929
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001930 return ret;
1931}
1932
1933static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
Johannes Berga060bbf2010-04-27 11:59:34 +02001934 struct ieee80211_vif *vif,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001935 struct cfg80211_scan_request *req)
1936{
1937 struct wl1271 *wl = hw->priv;
1938 int ret;
1939 u8 *ssid = NULL;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001940 size_t len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001941
1942 wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
1943
1944 if (req->n_ssids) {
1945 ssid = req->ssids[0].ssid;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001946 len = req->ssids[0].ssid_len;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001947 }
1948
1949 mutex_lock(&wl->mutex);
1950
Juuso Oikarinenb739a422010-10-26 13:24:38 +02001951 if (wl->state == WL1271_STATE_OFF) {
1952 /*
1953 * We cannot return -EBUSY here because cfg80211 will expect
1954 * a call to ieee80211_scan_completed if we do - in this case
1955 * there won't be any call.
1956 */
1957 ret = -EAGAIN;
1958 goto out;
1959 }
1960
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001961 ret = wl1271_ps_elp_wakeup(wl, false);
1962 if (ret < 0)
1963 goto out;
1964
Luciano Coelho5924f892010-08-04 03:46:22 +03001965 ret = wl1271_scan(hw->priv, ssid, len, req);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001966
1967 wl1271_ps_elp_sleep(wl);
1968
1969out:
1970 mutex_unlock(&wl->mutex);
1971
1972 return ret;
1973}
1974
Arik Nemtsov68d069c2010-11-08 10:51:07 +01001975static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
1976{
1977 struct wl1271 *wl = hw->priv;
1978 int ret = 0;
1979
1980 mutex_lock(&wl->mutex);
1981
1982 if (unlikely(wl->state == WL1271_STATE_OFF)) {
1983 ret = -EAGAIN;
1984 goto out;
1985 }
1986
1987 ret = wl1271_ps_elp_wakeup(wl, false);
1988 if (ret < 0)
1989 goto out;
1990
1991 ret = wl1271_acx_frag_threshold(wl, (u16)value);
1992 if (ret < 0)
1993 wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret);
1994
1995 wl1271_ps_elp_sleep(wl);
1996
1997out:
1998 mutex_unlock(&wl->mutex);
1999
2000 return ret;
2001}
2002
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002003static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
2004{
2005 struct wl1271 *wl = hw->priv;
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03002006 int ret = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002007
2008 mutex_lock(&wl->mutex);
2009
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002010 if (unlikely(wl->state == WL1271_STATE_OFF)) {
2011 ret = -EAGAIN;
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03002012 goto out;
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002013 }
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03002014
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002015 ret = wl1271_ps_elp_wakeup(wl, false);
2016 if (ret < 0)
2017 goto out;
2018
2019 ret = wl1271_acx_rts_threshold(wl, (u16) value);
2020 if (ret < 0)
2021 wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret);
2022
2023 wl1271_ps_elp_sleep(wl);
2024
2025out:
2026 mutex_unlock(&wl->mutex);
2027
2028 return ret;
2029}
2030
Arik Nemtsove78a2872010-10-16 19:07:21 +02002031static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb,
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002032 int offset)
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002033{
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002034 u8 *ptr = skb->data + offset;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002035
2036 /* find the location of the ssid in the beacon */
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002037 while (ptr < skb->data + skb->len) {
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002038 if (ptr[0] == WLAN_EID_SSID) {
2039 wl->ssid_len = ptr[1];
2040 memcpy(wl->ssid, ptr+2, wl->ssid_len);
Arik Nemtsove78a2872010-10-16 19:07:21 +02002041 return 0;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002042 }
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002043 ptr += (ptr[1] + 2);
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002044 }
Arik Nemtsove78a2872010-10-16 19:07:21 +02002045
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002046 wl1271_error("No SSID in IEs!\n");
Arik Nemtsove78a2872010-10-16 19:07:21 +02002047 return -ENOENT;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002048}
2049
Arik Nemtsove78a2872010-10-16 19:07:21 +02002050static int wl1271_bss_erp_info_changed(struct wl1271 *wl,
2051 struct ieee80211_bss_conf *bss_conf,
2052 u32 changed)
2053{
2054 int ret = 0;
2055
2056 if (changed & BSS_CHANGED_ERP_SLOT) {
2057 if (bss_conf->use_short_slot)
2058 ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
2059 else
2060 ret = wl1271_acx_slot(wl, SLOT_TIME_LONG);
2061 if (ret < 0) {
2062 wl1271_warning("Set slot time failed %d", ret);
2063 goto out;
2064 }
2065 }
2066
2067 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
2068 if (bss_conf->use_short_preamble)
2069 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT);
2070 else
2071 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG);
2072 }
2073
2074 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
2075 if (bss_conf->use_cts_prot)
2076 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE);
2077 else
2078 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE);
2079 if (ret < 0) {
2080 wl1271_warning("Set ctsprotect failed %d", ret);
2081 goto out;
2082 }
2083 }
2084
2085out:
2086 return ret;
2087}
2088
2089static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
2090 struct ieee80211_vif *vif,
2091 struct ieee80211_bss_conf *bss_conf,
2092 u32 changed)
2093{
2094 bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
2095 int ret = 0;
2096
2097 if ((changed & BSS_CHANGED_BEACON_INT)) {
2098 wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d",
2099 bss_conf->beacon_int);
2100
2101 wl->beacon_int = bss_conf->beacon_int;
2102 }
2103
2104 if ((changed & BSS_CHANGED_BEACON)) {
2105 struct ieee80211_hdr *hdr;
2106 int ieoffset = offsetof(struct ieee80211_mgmt,
2107 u.beacon.variable);
2108 struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
2109 u16 tmpl_id;
2110
2111 if (!beacon)
2112 goto out;
2113
2114 wl1271_debug(DEBUG_MASTER, "beacon updated");
2115
2116 ret = wl1271_ssid_set(wl, beacon, ieoffset);
2117 if (ret < 0) {
2118 dev_kfree_skb(beacon);
2119 goto out;
2120 }
2121 tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
2122 CMD_TEMPL_BEACON;
2123 ret = wl1271_cmd_template_set(wl, tmpl_id,
2124 beacon->data,
2125 beacon->len, 0,
2126 wl1271_tx_min_rate_get(wl));
2127 if (ret < 0) {
2128 dev_kfree_skb(beacon);
2129 goto out;
2130 }
2131
2132 hdr = (struct ieee80211_hdr *) beacon->data;
2133 hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
2134 IEEE80211_STYPE_PROBE_RESP);
2135
2136 tmpl_id = is_ap ? CMD_TEMPL_AP_PROBE_RESPONSE :
2137 CMD_TEMPL_PROBE_RESPONSE;
2138 ret = wl1271_cmd_template_set(wl,
2139 tmpl_id,
2140 beacon->data,
2141 beacon->len, 0,
2142 wl1271_tx_min_rate_get(wl));
2143 dev_kfree_skb(beacon);
2144 if (ret < 0)
2145 goto out;
2146 }
2147
2148out:
2149 return ret;
2150}
2151
2152/* AP mode changes */
2153static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002154 struct ieee80211_vif *vif,
2155 struct ieee80211_bss_conf *bss_conf,
2156 u32 changed)
2157{
Arik Nemtsove78a2872010-10-16 19:07:21 +02002158 int ret = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002159
Arik Nemtsove78a2872010-10-16 19:07:21 +02002160 if ((changed & BSS_CHANGED_BASIC_RATES)) {
2161 u32 rates = bss_conf->basic_rates;
2162 struct conf_tx_rate_class mgmt_rc;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002163
Arik Nemtsove78a2872010-10-16 19:07:21 +02002164 wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates);
2165 wl->basic_rate = wl1271_tx_min_rate_get(wl);
2166 wl1271_debug(DEBUG_AP, "basic rates: 0x%x",
2167 wl->basic_rate_set);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002168
Arik Nemtsove78a2872010-10-16 19:07:21 +02002169 /* update the AP management rate policy with the new rates */
2170 mgmt_rc.enabled_rates = wl->basic_rate_set;
2171 mgmt_rc.long_retry_limit = 10;
2172 mgmt_rc.short_retry_limit = 10;
2173 mgmt_rc.aflags = 0;
2174 ret = wl1271_acx_ap_rate_policy(wl, &mgmt_rc,
2175 ACX_TX_AP_MODE_MGMT_RATE);
2176 if (ret < 0) {
2177 wl1271_error("AP mgmt policy change failed %d", ret);
2178 goto out;
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02002179 }
2180 }
2181
Arik Nemtsove78a2872010-10-16 19:07:21 +02002182 ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed);
2183 if (ret < 0)
2184 goto out;
2185
2186 if ((changed & BSS_CHANGED_BEACON_ENABLED)) {
2187 if (bss_conf->enable_beacon) {
2188 if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
2189 ret = wl1271_cmd_start_bss(wl);
2190 if (ret < 0)
2191 goto out;
2192
2193 set_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
2194 wl1271_debug(DEBUG_AP, "started AP");
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002195
2196 ret = wl1271_ap_init_hwenc(wl);
2197 if (ret < 0)
2198 goto out;
Arik Nemtsove78a2872010-10-16 19:07:21 +02002199 }
2200 } else {
2201 if (test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
2202 ret = wl1271_cmd_stop_bss(wl);
2203 if (ret < 0)
2204 goto out;
2205
2206 clear_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
2207 wl1271_debug(DEBUG_AP, "stopped AP");
2208 }
2209 }
2210 }
2211
2212 ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
2213 if (ret < 0)
2214 goto out;
2215out:
2216 return;
2217}
2218
2219/* STA/IBSS mode changes */
2220static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
2221 struct ieee80211_vif *vif,
2222 struct ieee80211_bss_conf *bss_conf,
2223 u32 changed)
2224{
2225 bool do_join = false, set_assoc = false;
2226 bool is_ibss = (wl->bss_type == BSS_TYPE_IBSS);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002227 u32 sta_rate_set = 0;
Arik Nemtsove78a2872010-10-16 19:07:21 +02002228 int ret;
Luciano Coelho2d6e4e762011-01-11 19:07:21 +01002229 struct ieee80211_sta *sta;
Arik Nemtsova1008852011-02-12 23:24:20 +02002230 bool sta_exists = false;
2231 struct ieee80211_sta_ht_cap sta_ht_cap;
Arik Nemtsove78a2872010-10-16 19:07:21 +02002232
2233 if (is_ibss) {
2234 ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf,
2235 changed);
2236 if (ret < 0)
2237 goto out;
2238 }
2239
2240 if ((changed & BSS_CHANGED_BEACON_INT) && is_ibss)
2241 do_join = true;
2242
2243 /* Need to update the SSID (for filtering etc) */
2244 if ((changed & BSS_CHANGED_BEACON) && is_ibss)
2245 do_join = true;
2246
2247 if ((changed & BSS_CHANGED_BEACON_ENABLED) && is_ibss) {
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02002248 wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
2249 bss_conf->enable_beacon ? "enabled" : "disabled");
2250
2251 if (bss_conf->enable_beacon)
2252 wl->set_bss_type = BSS_TYPE_IBSS;
2253 else
2254 wl->set_bss_type = BSS_TYPE_STA_BSS;
2255 do_join = true;
2256 }
2257
Arik Nemtsove78a2872010-10-16 19:07:21 +02002258 if ((changed & BSS_CHANGED_CQM)) {
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03002259 bool enable = false;
2260 if (bss_conf->cqm_rssi_thold)
2261 enable = true;
2262 ret = wl1271_acx_rssi_snr_trigger(wl, enable,
2263 bss_conf->cqm_rssi_thold,
2264 bss_conf->cqm_rssi_hyst);
2265 if (ret < 0)
2266 goto out;
2267 wl->rssi_thold = bss_conf->cqm_rssi_thold;
2268 }
2269
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002270 if ((changed & BSS_CHANGED_BSSID) &&
2271 /*
2272 * Now we know the correct bssid, so we send a new join command
2273 * and enable the BSSID filter
2274 */
2275 memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
Arik Nemtsove78a2872010-10-16 19:07:21 +02002276 memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
Juuso Oikarinena0cb7be2010-03-18 12:26:44 +02002277
Eliad Pellerfa287b82010-12-26 09:27:50 +01002278 if (!is_zero_ether_addr(wl->bssid)) {
2279 ret = wl1271_cmd_build_null_data(wl);
2280 if (ret < 0)
2281 goto out;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002282
Eliad Pellerfa287b82010-12-26 09:27:50 +01002283 ret = wl1271_build_qos_null_data(wl);
2284 if (ret < 0)
2285 goto out;
Saravanan Dhanabal141418c2010-04-28 09:50:00 +03002286
Eliad Pellerfa287b82010-12-26 09:27:50 +01002287 /* filter out all packets not from this BSSID */
2288 wl1271_configure_filters(wl, 0);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02002289
Eliad Pellerfa287b82010-12-26 09:27:50 +01002290 /* Need to update the BSSID (for filtering etc) */
2291 do_join = true;
2292 }
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002293 }
2294
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002295 rcu_read_lock();
2296 sta = ieee80211_find_sta(vif, bss_conf->bssid);
2297 if (sta) {
2298 /* save the supp_rates of the ap */
2299 sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band];
2300 if (sta->ht_cap.ht_supported)
2301 sta_rate_set |=
2302 (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET);
Arik Nemtsova1008852011-02-12 23:24:20 +02002303 sta_ht_cap = sta->ht_cap;
2304 sta_exists = true;
2305 }
2306 rcu_read_unlock();
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002307
Arik Nemtsova1008852011-02-12 23:24:20 +02002308 if (sta_exists) {
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002309 /* handle new association with HT and HT information change */
2310 if ((changed & BSS_CHANGED_HT) &&
2311 (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
Arik Nemtsova1008852011-02-12 23:24:20 +02002312 ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap,
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002313 true);
2314 if (ret < 0) {
2315 wl1271_warning("Set ht cap true failed %d",
2316 ret);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002317 goto out;
2318 }
2319 ret = wl1271_acx_set_ht_information(wl,
2320 bss_conf->ht_operation_mode);
2321 if (ret < 0) {
2322 wl1271_warning("Set ht information failed %d",
2323 ret);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002324 goto out;
2325 }
2326 }
2327 /* handle new association without HT and disassociation */
2328 else if (changed & BSS_CHANGED_ASSOC) {
Arik Nemtsova1008852011-02-12 23:24:20 +02002329 ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap,
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002330 false);
2331 if (ret < 0) {
2332 wl1271_warning("Set ht cap false failed %d",
2333 ret);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002334 goto out;
2335 }
2336 }
2337 }
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002338
Arik Nemtsove78a2872010-10-16 19:07:21 +02002339 if ((changed & BSS_CHANGED_ASSOC)) {
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002340 if (bss_conf->assoc) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002341 u32 rates;
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002342 int ieoffset;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002343 wl->aid = bss_conf->aid;
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002344 set_assoc = true;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002345
Juuso Oikarinen90494a92010-07-08 17:50:00 +03002346 wl->ps_poll_failures = 0;
2347
Luciano Coelhoae751ba2009-10-12 15:08:57 +03002348 /*
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002349 * use basic rates from AP, and determine lowest rate
2350 * to use with control frames.
2351 */
2352 rates = bss_conf->basic_rates;
2353 wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
2354 rates);
Arik Nemtsove0fe3712010-10-16 18:19:53 +02002355 wl->basic_rate = wl1271_tx_min_rate_get(wl);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002356 if (sta_rate_set)
2357 wl->rate_set = wl1271_tx_enabled_rates_get(wl,
2358 sta_rate_set);
Arik Nemtsov79b223f2010-10-16 17:52:59 +02002359 ret = wl1271_acx_sta_rate_policies(wl);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002360 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02002361 goto out;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002362
2363 /*
Luciano Coelhoae751ba2009-10-12 15:08:57 +03002364 * with wl1271, we don't need to update the
2365 * beacon_int and dtim_period, because the firmware
2366 * updates it by itself when the first beacon is
2367 * received after a join.
2368 */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002369 ret = wl1271_cmd_build_ps_poll(wl, wl->aid);
2370 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02002371 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002372
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02002373 /*
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002374 * Get a template for hardware connection maintenance
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02002375 */
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002376 dev_kfree_skb(wl->probereq);
2377 wl->probereq = wl1271_cmd_build_ap_probe_req(wl, NULL);
2378 ieoffset = offsetof(struct ieee80211_mgmt,
2379 u.probe_req.variable);
2380 wl1271_ssid_set(wl, wl->probereq, ieoffset);
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02002381
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02002382 /* enable the connection monitoring feature */
2383 ret = wl1271_acx_conn_monit_params(wl, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002384 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02002385 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002386
2387 /* If we want to go in PSM but we're not there yet */
Juuso Oikarinen71449f82009-12-11 15:41:07 +02002388 if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) &&
2389 !test_bit(WL1271_FLAG_PSM, &wl->flags)) {
Arik Nemtsove78a2872010-10-16 19:07:21 +02002390 enum wl1271_cmd_ps_mode mode;
2391
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002392 mode = STATION_POWER_SAVE_MODE;
Juuso Oikarinen65cddbf12010-08-24 06:28:03 +03002393 ret = wl1271_ps_set_mode(wl, mode,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02002394 wl->basic_rate,
Juuso Oikarinen65cddbf12010-08-24 06:28:03 +03002395 true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002396 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02002397 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002398 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03002399 } else {
2400 /* use defaults when not associated */
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03002401 clear_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags);
Juuso Oikarinen830fb672009-12-11 15:41:06 +02002402 clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
Juuso Oikarinend94cd292009-10-08 21:56:25 +03002403 wl->aid = 0;
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02002404
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002405 /* free probe-request template */
2406 dev_kfree_skb(wl->probereq);
2407 wl->probereq = NULL;
2408
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03002409 /* re-enable dynamic ps - just in case */
Juuso Oikarinenf532be62010-07-08 17:50:05 +03002410 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03002411
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002412 /* revert back to minimum rates for the current band */
2413 wl1271_set_band_rate(wl);
Arik Nemtsove0fe3712010-10-16 18:19:53 +02002414 wl->basic_rate = wl1271_tx_min_rate_get(wl);
Arik Nemtsov79b223f2010-10-16 17:52:59 +02002415 ret = wl1271_acx_sta_rate_policies(wl);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002416 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02002417 goto out;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002418
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02002419 /* disable connection monitor features */
2420 ret = wl1271_acx_conn_monit_params(wl, false);
Juuso Oikarinenc1899552010-03-26 12:53:32 +02002421
2422 /* Disable the keep-alive feature */
2423 ret = wl1271_acx_keep_alive_mode(wl, false);
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02002424 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02002425 goto out;
Juuso Oikarinenb84a7d32010-11-22 12:59:08 +02002426
2427 /* restore the bssid filter and go to dummy bssid */
2428 wl1271_unjoin(wl);
2429 wl1271_dummy_join(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002430 }
2431 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03002432
Arik Nemtsove78a2872010-10-16 19:07:21 +02002433 ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
2434 if (ret < 0)
2435 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002436
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03002437 if (changed & BSS_CHANGED_ARP_FILTER) {
2438 __be32 addr = bss_conf->arp_addr_list[0];
2439 WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
2440
Eliad Pellerc5312772010-12-09 11:31:27 +02002441 if (bss_conf->arp_addr_cnt == 1 &&
2442 bss_conf->arp_filter_enabled) {
2443 /*
2444 * The template should have been configured only upon
2445 * association. however, it seems that the correct ip
2446 * isn't being set (when sending), so we have to
2447 * reconfigure the template upon every ip change.
2448 */
2449 ret = wl1271_cmd_build_arp_rsp(wl, addr);
2450 if (ret < 0) {
2451 wl1271_warning("build arp rsp failed: %d", ret);
Arik Nemtsove78a2872010-10-16 19:07:21 +02002452 goto out;
Eliad Pellerc5312772010-12-09 11:31:27 +02002453 }
2454
2455 ret = wl1271_acx_arp_ip_filter(wl,
Eliad Pellere5e2f242011-01-24 19:19:03 +01002456 ACX_ARP_FILTER_ARP_FILTERING,
Eliad Pellerc5312772010-12-09 11:31:27 +02002457 addr);
2458 } else
2459 ret = wl1271_acx_arp_ip_filter(wl, 0, addr);
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03002460
2461 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02002462 goto out;
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03002463 }
2464
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02002465 if (do_join) {
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002466 ret = wl1271_join(wl, set_assoc);
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02002467 if (ret < 0) {
2468 wl1271_warning("cmd join failed %d", ret);
Arik Nemtsove78a2872010-10-16 19:07:21 +02002469 goto out;
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02002470 }
Juuso Oikarinenc1899552010-03-26 12:53:32 +02002471 }
2472
Arik Nemtsove78a2872010-10-16 19:07:21 +02002473out:
2474 return;
2475}
2476
2477static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
2478 struct ieee80211_vif *vif,
2479 struct ieee80211_bss_conf *bss_conf,
2480 u32 changed)
2481{
2482 struct wl1271 *wl = hw->priv;
2483 bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
2484 int ret;
2485
2486 wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
2487 (int)changed);
2488
2489 mutex_lock(&wl->mutex);
2490
2491 if (unlikely(wl->state == WL1271_STATE_OFF))
2492 goto out;
2493
2494 ret = wl1271_ps_elp_wakeup(wl, false);
2495 if (ret < 0)
2496 goto out;
2497
2498 if (is_ap)
2499 wl1271_bss_info_changed_ap(wl, vif, bss_conf, changed);
2500 else
2501 wl1271_bss_info_changed_sta(wl, vif, bss_conf, changed);
2502
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002503 wl1271_ps_elp_sleep(wl);
2504
2505out:
2506 mutex_unlock(&wl->mutex);
2507}
2508
Kalle Valoc6999d82010-02-18 13:25:41 +02002509static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
2510 const struct ieee80211_tx_queue_params *params)
2511{
2512 struct wl1271 *wl = hw->priv;
Kalle Valo4695dc92010-03-18 12:26:38 +02002513 u8 ps_scheme;
Arik Nemtsov488fc542010-10-16 20:33:45 +02002514 int ret = 0;
Kalle Valoc6999d82010-02-18 13:25:41 +02002515
2516 mutex_lock(&wl->mutex);
2517
2518 wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
2519
Kalle Valo4695dc92010-03-18 12:26:38 +02002520 if (params->uapsd)
2521 ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
2522 else
2523 ps_scheme = CONF_PS_SCHEME_LEGACY;
2524
Arik Nemtsov488fc542010-10-16 20:33:45 +02002525 if (wl->state == WL1271_STATE_OFF) {
2526 /*
2527 * If the state is off, the parameters will be recorded and
2528 * configured on init. This happens in AP-mode.
2529 */
2530 struct conf_tx_ac_category *conf_ac =
2531 &wl->conf.tx.ac_conf[wl1271_tx_get_queue(queue)];
2532 struct conf_tx_tid *conf_tid =
2533 &wl->conf.tx.tid_conf[wl1271_tx_get_queue(queue)];
2534
2535 conf_ac->ac = wl1271_tx_get_queue(queue);
2536 conf_ac->cw_min = (u8)params->cw_min;
2537 conf_ac->cw_max = params->cw_max;
2538 conf_ac->aifsn = params->aifs;
2539 conf_ac->tx_op_limit = params->txop << 5;
2540
2541 conf_tid->queue_id = wl1271_tx_get_queue(queue);
2542 conf_tid->channel_type = CONF_CHANNEL_TYPE_EDCF;
2543 conf_tid->tsid = wl1271_tx_get_queue(queue);
2544 conf_tid->ps_scheme = ps_scheme;
2545 conf_tid->ack_policy = CONF_ACK_POLICY_LEGACY;
2546 conf_tid->apsd_conf[0] = 0;
2547 conf_tid->apsd_conf[1] = 0;
2548 } else {
2549 ret = wl1271_ps_elp_wakeup(wl, false);
2550 if (ret < 0)
2551 goto out;
2552
2553 /*
2554 * the txop is confed in units of 32us by the mac80211,
2555 * we need us
2556 */
2557 ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
2558 params->cw_min, params->cw_max,
2559 params->aifs, params->txop << 5);
2560 if (ret < 0)
2561 goto out_sleep;
2562
2563 ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
2564 CONF_CHANNEL_TYPE_EDCF,
2565 wl1271_tx_get_queue(queue),
2566 ps_scheme, CONF_ACK_POLICY_LEGACY,
2567 0, 0);
2568 if (ret < 0)
2569 goto out_sleep;
Kalle Valoc82c1dd2010-02-18 13:25:47 +02002570
2571out_sleep:
Arik Nemtsov488fc542010-10-16 20:33:45 +02002572 wl1271_ps_elp_sleep(wl);
2573 }
Kalle Valoc6999d82010-02-18 13:25:41 +02002574
2575out:
2576 mutex_unlock(&wl->mutex);
2577
2578 return ret;
2579}
2580
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03002581static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw)
2582{
2583
2584 struct wl1271 *wl = hw->priv;
2585 u64 mactime = ULLONG_MAX;
2586 int ret;
2587
2588 wl1271_debug(DEBUG_MAC80211, "mac80211 get tsf");
2589
2590 mutex_lock(&wl->mutex);
2591
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002592 if (unlikely(wl->state == WL1271_STATE_OFF))
2593 goto out;
2594
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03002595 ret = wl1271_ps_elp_wakeup(wl, false);
2596 if (ret < 0)
2597 goto out;
2598
2599 ret = wl1271_acx_tsf_info(wl, &mactime);
2600 if (ret < 0)
2601 goto out_sleep;
2602
2603out_sleep:
2604 wl1271_ps_elp_sleep(wl);
2605
2606out:
2607 mutex_unlock(&wl->mutex);
2608 return mactime;
2609}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002610
John W. Linvilleece550d2010-07-28 16:41:06 -04002611static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
2612 struct survey_info *survey)
2613{
2614 struct wl1271 *wl = hw->priv;
2615 struct ieee80211_conf *conf = &hw->conf;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02002616
John W. Linvilleece550d2010-07-28 16:41:06 -04002617 if (idx != 0)
2618 return -ENOENT;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02002619
John W. Linvilleece550d2010-07-28 16:41:06 -04002620 survey->channel = conf->channel;
2621 survey->filled = SURVEY_INFO_NOISE_DBM;
2622 survey->noise = wl->noise;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02002623
John W. Linvilleece550d2010-07-28 16:41:06 -04002624 return 0;
2625}
2626
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02002627static int wl1271_allocate_hlid(struct wl1271 *wl,
2628 struct ieee80211_sta *sta,
2629 u8 *hlid)
2630{
2631 struct wl1271_station *wl_sta;
2632 int id;
2633
2634 id = find_first_zero_bit(wl->ap_hlid_map, AP_MAX_STATIONS);
2635 if (id >= AP_MAX_STATIONS) {
2636 wl1271_warning("could not allocate HLID - too much stations");
2637 return -EBUSY;
2638 }
2639
2640 wl_sta = (struct wl1271_station *)sta->drv_priv;
2641
2642 __set_bit(id, wl->ap_hlid_map);
2643 wl_sta->hlid = WL1271_AP_STA_HLID_START + id;
2644 *hlid = wl_sta->hlid;
2645 return 0;
2646}
2647
2648static void wl1271_free_hlid(struct wl1271 *wl, u8 hlid)
2649{
2650 int id = hlid - WL1271_AP_STA_HLID_START;
2651
2652 __clear_bit(id, wl->ap_hlid_map);
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02002653 wl1271_tx_reset_link_queues(wl, hlid);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02002654}
2655
2656static int wl1271_op_sta_add(struct ieee80211_hw *hw,
2657 struct ieee80211_vif *vif,
2658 struct ieee80211_sta *sta)
2659{
2660 struct wl1271 *wl = hw->priv;
2661 int ret = 0;
2662 u8 hlid;
2663
2664 mutex_lock(&wl->mutex);
2665
2666 if (unlikely(wl->state == WL1271_STATE_OFF))
2667 goto out;
2668
2669 if (wl->bss_type != BSS_TYPE_AP_BSS)
2670 goto out;
2671
2672 wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);
2673
2674 ret = wl1271_allocate_hlid(wl, sta, &hlid);
2675 if (ret < 0)
2676 goto out;
2677
2678 ret = wl1271_ps_elp_wakeup(wl, false);
2679 if (ret < 0)
2680 goto out;
2681
2682 ret = wl1271_cmd_add_sta(wl, sta, hlid);
2683 if (ret < 0)
2684 goto out_sleep;
2685
2686out_sleep:
2687 wl1271_ps_elp_sleep(wl);
2688
2689out:
2690 mutex_unlock(&wl->mutex);
2691 return ret;
2692}
2693
2694static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
2695 struct ieee80211_vif *vif,
2696 struct ieee80211_sta *sta)
2697{
2698 struct wl1271 *wl = hw->priv;
2699 struct wl1271_station *wl_sta;
2700 int ret = 0, id;
2701
2702 mutex_lock(&wl->mutex);
2703
2704 if (unlikely(wl->state == WL1271_STATE_OFF))
2705 goto out;
2706
2707 if (wl->bss_type != BSS_TYPE_AP_BSS)
2708 goto out;
2709
2710 wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
2711
2712 wl_sta = (struct wl1271_station *)sta->drv_priv;
2713 id = wl_sta->hlid - WL1271_AP_STA_HLID_START;
2714 if (WARN_ON(!test_bit(id, wl->ap_hlid_map)))
2715 goto out;
2716
2717 ret = wl1271_ps_elp_wakeup(wl, false);
2718 if (ret < 0)
2719 goto out;
2720
2721 ret = wl1271_cmd_remove_sta(wl, wl_sta->hlid);
2722 if (ret < 0)
2723 goto out_sleep;
2724
2725 wl1271_free_hlid(wl, wl_sta->hlid);
2726
2727out_sleep:
2728 wl1271_ps_elp_sleep(wl);
2729
2730out:
2731 mutex_unlock(&wl->mutex);
2732 return ret;
2733}
2734
Levi, Shaharbbba3e62011-01-23 07:27:23 +01002735int wl1271_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
Luciano Coelho7c3ee9e2011-01-31 09:41:52 +02002736 enum ieee80211_ampdu_mlme_action action,
2737 struct ieee80211_sta *sta, u16 tid, u16 *ssn,
2738 u8 buf_size)
Levi, Shaharbbba3e62011-01-23 07:27:23 +01002739{
2740 struct wl1271 *wl = hw->priv;
2741 int ret;
2742
2743 mutex_lock(&wl->mutex);
2744
2745 if (unlikely(wl->state == WL1271_STATE_OFF)) {
2746 ret = -EAGAIN;
2747 goto out;
2748 }
2749
2750 ret = wl1271_ps_elp_wakeup(wl, false);
2751 if (ret < 0)
2752 goto out;
2753
2754 switch (action) {
2755 case IEEE80211_AMPDU_RX_START:
2756 if (wl->ba_support) {
2757 ret = wl1271_acx_set_ba_receiver_session(wl, tid, *ssn,
2758 true);
2759 if (!ret)
2760 wl->ba_rx_bitmap |= BIT(tid);
2761 } else {
2762 ret = -ENOTSUPP;
2763 }
2764 break;
2765
2766 case IEEE80211_AMPDU_RX_STOP:
2767 ret = wl1271_acx_set_ba_receiver_session(wl, tid, 0, false);
2768 if (!ret)
2769 wl->ba_rx_bitmap &= ~BIT(tid);
2770 break;
2771
2772 /*
2773 * The BA initiator session management in FW independently.
2774 * Falling break here on purpose for all TX APDU commands.
2775 */
2776 case IEEE80211_AMPDU_TX_START:
2777 case IEEE80211_AMPDU_TX_STOP:
2778 case IEEE80211_AMPDU_TX_OPERATIONAL:
2779 ret = -EINVAL;
2780 break;
2781
2782 default:
2783 wl1271_error("Incorrect ampdu action id=%x\n", action);
2784 ret = -EINVAL;
2785 }
2786
2787 wl1271_ps_elp_sleep(wl);
2788
2789out:
2790 mutex_unlock(&wl->mutex);
2791
2792 return ret;
2793}
2794
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002795/* can't be const, mac80211 writes to this */
2796static struct ieee80211_rate wl1271_rates[] = {
2797 { .bitrate = 10,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002798 .hw_value = CONF_HW_BIT_RATE_1MBPS,
2799 .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002800 { .bitrate = 20,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002801 .hw_value = CONF_HW_BIT_RATE_2MBPS,
2802 .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002803 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2804 { .bitrate = 55,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002805 .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
2806 .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002807 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2808 { .bitrate = 110,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002809 .hw_value = CONF_HW_BIT_RATE_11MBPS,
2810 .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002811 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
2812 { .bitrate = 60,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002813 .hw_value = CONF_HW_BIT_RATE_6MBPS,
2814 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002815 { .bitrate = 90,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002816 .hw_value = CONF_HW_BIT_RATE_9MBPS,
2817 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002818 { .bitrate = 120,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002819 .hw_value = CONF_HW_BIT_RATE_12MBPS,
2820 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002821 { .bitrate = 180,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002822 .hw_value = CONF_HW_BIT_RATE_18MBPS,
2823 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002824 { .bitrate = 240,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002825 .hw_value = CONF_HW_BIT_RATE_24MBPS,
2826 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002827 { .bitrate = 360,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002828 .hw_value = CONF_HW_BIT_RATE_36MBPS,
2829 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002830 { .bitrate = 480,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002831 .hw_value = CONF_HW_BIT_RATE_48MBPS,
2832 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002833 { .bitrate = 540,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03002834 .hw_value = CONF_HW_BIT_RATE_54MBPS,
2835 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002836};
2837
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002838/* can't be const, mac80211 writes to this */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002839static struct ieee80211_channel wl1271_channels[] = {
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02002840 { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002841 { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002842 { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
2843 { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
2844 { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002845 { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002846 { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
2847 { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
2848 { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002849 { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002850 { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
2851 { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
2852 { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
Arik Nemtsov6c89b7b2011-01-18 20:39:52 +01002853 { .hw_value = 14, .center_freq = 2484, .max_power = 25 },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002854};
2855
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002856/* mapping to indexes for wl1271_rates */
Tobias Klausera0ea9492010-05-20 10:38:11 +02002857static const u8 wl1271_rate_to_idx_2ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002858 /* MCS rates are used only with 11n */
Shahar Levi18357852010-10-13 16:09:41 +02002859 7, /* CONF_HW_RXTX_RATE_MCS7 */
2860 6, /* CONF_HW_RXTX_RATE_MCS6 */
2861 5, /* CONF_HW_RXTX_RATE_MCS5 */
2862 4, /* CONF_HW_RXTX_RATE_MCS4 */
2863 3, /* CONF_HW_RXTX_RATE_MCS3 */
2864 2, /* CONF_HW_RXTX_RATE_MCS2 */
2865 1, /* CONF_HW_RXTX_RATE_MCS1 */
2866 0, /* CONF_HW_RXTX_RATE_MCS0 */
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002867
2868 11, /* CONF_HW_RXTX_RATE_54 */
2869 10, /* CONF_HW_RXTX_RATE_48 */
2870 9, /* CONF_HW_RXTX_RATE_36 */
2871 8, /* CONF_HW_RXTX_RATE_24 */
2872
2873 /* TI-specific rate */
2874 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
2875
2876 7, /* CONF_HW_RXTX_RATE_18 */
2877 6, /* CONF_HW_RXTX_RATE_12 */
2878 3, /* CONF_HW_RXTX_RATE_11 */
2879 5, /* CONF_HW_RXTX_RATE_9 */
2880 4, /* CONF_HW_RXTX_RATE_6 */
2881 2, /* CONF_HW_RXTX_RATE_5_5 */
2882 1, /* CONF_HW_RXTX_RATE_2 */
2883 0 /* CONF_HW_RXTX_RATE_1 */
2884};
2885
Shahar Levie8b03a22010-10-13 16:09:39 +02002886/* 11n STA capabilities */
2887#define HW_RX_HIGHEST_RATE 72
2888
Shahar Levi00d20102010-11-08 11:20:10 +00002889#ifdef CONFIG_WL12XX_HT
2890#define WL12XX_HT_CAP { \
Shahar Levie8b03a22010-10-13 16:09:39 +02002891 .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20, \
2892 .ht_supported = true, \
2893 .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, \
2894 .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \
2895 .mcs = { \
2896 .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \
2897 .rx_highest = cpu_to_le16(HW_RX_HIGHEST_RATE), \
2898 .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \
2899 }, \
2900}
Shahar Levi18357852010-10-13 16:09:41 +02002901#else
Shahar Levi00d20102010-11-08 11:20:10 +00002902#define WL12XX_HT_CAP { \
Shahar Levi18357852010-10-13 16:09:41 +02002903 .ht_supported = false, \
2904}
2905#endif
Shahar Levie8b03a22010-10-13 16:09:39 +02002906
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002907/* can't be const, mac80211 writes to this */
2908static struct ieee80211_supported_band wl1271_band_2ghz = {
2909 .channels = wl1271_channels,
2910 .n_channels = ARRAY_SIZE(wl1271_channels),
2911 .bitrates = wl1271_rates,
2912 .n_bitrates = ARRAY_SIZE(wl1271_rates),
Shahar Levi00d20102010-11-08 11:20:10 +00002913 .ht_cap = WL12XX_HT_CAP,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002914};
2915
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002916/* 5 GHz data rates for WL1273 */
2917static struct ieee80211_rate wl1271_rates_5ghz[] = {
2918 { .bitrate = 60,
2919 .hw_value = CONF_HW_BIT_RATE_6MBPS,
2920 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
2921 { .bitrate = 90,
2922 .hw_value = CONF_HW_BIT_RATE_9MBPS,
2923 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
2924 { .bitrate = 120,
2925 .hw_value = CONF_HW_BIT_RATE_12MBPS,
2926 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
2927 { .bitrate = 180,
2928 .hw_value = CONF_HW_BIT_RATE_18MBPS,
2929 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
2930 { .bitrate = 240,
2931 .hw_value = CONF_HW_BIT_RATE_24MBPS,
2932 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
2933 { .bitrate = 360,
2934 .hw_value = CONF_HW_BIT_RATE_36MBPS,
2935 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
2936 { .bitrate = 480,
2937 .hw_value = CONF_HW_BIT_RATE_48MBPS,
2938 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
2939 { .bitrate = 540,
2940 .hw_value = CONF_HW_BIT_RATE_54MBPS,
2941 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
2942};
2943
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002944/* 5 GHz band channels for WL1273 */
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002945static struct ieee80211_channel wl1271_channels_5ghz[] = {
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002946 { .hw_value = 7, .center_freq = 5035},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002947 { .hw_value = 8, .center_freq = 5040},
2948 { .hw_value = 9, .center_freq = 5045},
2949 { .hw_value = 11, .center_freq = 5055},
2950 { .hw_value = 12, .center_freq = 5060},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002951 { .hw_value = 16, .center_freq = 5080},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002952 { .hw_value = 34, .center_freq = 5170},
2953 { .hw_value = 36, .center_freq = 5180},
2954 { .hw_value = 38, .center_freq = 5190},
2955 { .hw_value = 40, .center_freq = 5200},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002956 { .hw_value = 42, .center_freq = 5210},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002957 { .hw_value = 44, .center_freq = 5220},
2958 { .hw_value = 46, .center_freq = 5230},
2959 { .hw_value = 48, .center_freq = 5240},
2960 { .hw_value = 52, .center_freq = 5260},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002961 { .hw_value = 56, .center_freq = 5280},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002962 { .hw_value = 60, .center_freq = 5300},
2963 { .hw_value = 64, .center_freq = 5320},
2964 { .hw_value = 100, .center_freq = 5500},
2965 { .hw_value = 104, .center_freq = 5520},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002966 { .hw_value = 108, .center_freq = 5540},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002967 { .hw_value = 112, .center_freq = 5560},
2968 { .hw_value = 116, .center_freq = 5580},
2969 { .hw_value = 120, .center_freq = 5600},
2970 { .hw_value = 124, .center_freq = 5620},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002971 { .hw_value = 128, .center_freq = 5640},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002972 { .hw_value = 132, .center_freq = 5660},
2973 { .hw_value = 136, .center_freq = 5680},
2974 { .hw_value = 140, .center_freq = 5700},
2975 { .hw_value = 149, .center_freq = 5745},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02002976 { .hw_value = 153, .center_freq = 5765},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01002977 { .hw_value = 157, .center_freq = 5785},
2978 { .hw_value = 161, .center_freq = 5805},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03002979 { .hw_value = 165, .center_freq = 5825},
2980};
2981
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002982/* mapping to indexes for wl1271_rates_5ghz */
Tobias Klausera0ea9492010-05-20 10:38:11 +02002983static const u8 wl1271_rate_to_idx_5ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002984 /* MCS rates are used only with 11n */
Shahar Levi18357852010-10-13 16:09:41 +02002985 7, /* CONF_HW_RXTX_RATE_MCS7 */
2986 6, /* CONF_HW_RXTX_RATE_MCS6 */
2987 5, /* CONF_HW_RXTX_RATE_MCS5 */
2988 4, /* CONF_HW_RXTX_RATE_MCS4 */
2989 3, /* CONF_HW_RXTX_RATE_MCS3 */
2990 2, /* CONF_HW_RXTX_RATE_MCS2 */
2991 1, /* CONF_HW_RXTX_RATE_MCS1 */
2992 0, /* CONF_HW_RXTX_RATE_MCS0 */
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02002993
2994 7, /* CONF_HW_RXTX_RATE_54 */
2995 6, /* CONF_HW_RXTX_RATE_48 */
2996 5, /* CONF_HW_RXTX_RATE_36 */
2997 4, /* CONF_HW_RXTX_RATE_24 */
2998
2999 /* TI-specific rate */
3000 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
3001
3002 3, /* CONF_HW_RXTX_RATE_18 */
3003 2, /* CONF_HW_RXTX_RATE_12 */
3004 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_11 */
3005 1, /* CONF_HW_RXTX_RATE_9 */
3006 0, /* CONF_HW_RXTX_RATE_6 */
3007 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_5_5 */
3008 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_2 */
3009 CONF_HW_RXTX_RATE_UNSUPPORTED /* CONF_HW_RXTX_RATE_1 */
3010};
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03003011
3012static struct ieee80211_supported_band wl1271_band_5ghz = {
3013 .channels = wl1271_channels_5ghz,
3014 .n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
3015 .bitrates = wl1271_rates_5ghz,
3016 .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
Shahar Levi00d20102010-11-08 11:20:10 +00003017 .ht_cap = WL12XX_HT_CAP,
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03003018};
3019
Tobias Klausera0ea9492010-05-20 10:38:11 +02003020static const u8 *wl1271_band_rate_to_idx[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003021 [IEEE80211_BAND_2GHZ] = wl1271_rate_to_idx_2ghz,
3022 [IEEE80211_BAND_5GHZ] = wl1271_rate_to_idx_5ghz
3023};
3024
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003025static const struct ieee80211_ops wl1271_ops = {
3026 .start = wl1271_op_start,
3027 .stop = wl1271_op_stop,
3028 .add_interface = wl1271_op_add_interface,
3029 .remove_interface = wl1271_op_remove_interface,
3030 .config = wl1271_op_config,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03003031 .prepare_multicast = wl1271_op_prepare_multicast,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003032 .configure_filter = wl1271_op_configure_filter,
3033 .tx = wl1271_op_tx,
3034 .set_key = wl1271_op_set_key,
3035 .hw_scan = wl1271_op_hw_scan,
3036 .bss_info_changed = wl1271_op_bss_info_changed,
Arik Nemtsov68d069c2010-11-08 10:51:07 +01003037 .set_frag_threshold = wl1271_op_set_frag_threshold,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003038 .set_rts_threshold = wl1271_op_set_rts_threshold,
Kalle Valoc6999d82010-02-18 13:25:41 +02003039 .conf_tx = wl1271_op_conf_tx,
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03003040 .get_tsf = wl1271_op_get_tsf,
John W. Linvilleece550d2010-07-28 16:41:06 -04003041 .get_survey = wl1271_op_get_survey,
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003042 .sta_add = wl1271_op_sta_add,
3043 .sta_remove = wl1271_op_sta_remove,
Levi, Shaharbbba3e62011-01-23 07:27:23 +01003044 .ampdu_action = wl1271_op_ampdu_action,
Kalle Valoc8c90872010-02-18 13:25:53 +02003045 CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003046};
3047
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003048
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02003049u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band)
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003050{
3051 u8 idx;
3052
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02003053 BUG_ON(band >= sizeof(wl1271_band_rate_to_idx)/sizeof(u8 *));
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003054
3055 if (unlikely(rate >= CONF_HW_RXTX_RATE_MAX)) {
3056 wl1271_error("Illegal RX rate from HW: %d", rate);
3057 return 0;
3058 }
3059
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02003060 idx = wl1271_band_rate_to_idx[band][rate];
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003061 if (unlikely(idx == CONF_HW_RXTX_RATE_UNSUPPORTED)) {
3062 wl1271_error("Unsupported RX rate from HW: %d", rate);
3063 return 0;
3064 }
3065
3066 return idx;
3067}
3068
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003069static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
3070 struct device_attribute *attr,
3071 char *buf)
3072{
3073 struct wl1271 *wl = dev_get_drvdata(dev);
3074 ssize_t len;
3075
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02003076 len = PAGE_SIZE;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003077
3078 mutex_lock(&wl->mutex);
3079 len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
3080 wl->sg_enabled);
3081 mutex_unlock(&wl->mutex);
3082
3083 return len;
3084
3085}
3086
3087static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
3088 struct device_attribute *attr,
3089 const char *buf, size_t count)
3090{
3091 struct wl1271 *wl = dev_get_drvdata(dev);
3092 unsigned long res;
3093 int ret;
3094
3095 ret = strict_strtoul(buf, 10, &res);
3096
3097 if (ret < 0) {
3098 wl1271_warning("incorrect value written to bt_coex_mode");
3099 return count;
3100 }
3101
3102 mutex_lock(&wl->mutex);
3103
3104 res = !!res;
3105
3106 if (res == wl->sg_enabled)
3107 goto out;
3108
3109 wl->sg_enabled = res;
3110
3111 if (wl->state == WL1271_STATE_OFF)
3112 goto out;
3113
3114 ret = wl1271_ps_elp_wakeup(wl, false);
3115 if (ret < 0)
3116 goto out;
3117
3118 wl1271_acx_sg_enable(wl, wl->sg_enabled);
3119 wl1271_ps_elp_sleep(wl);
3120
3121 out:
3122 mutex_unlock(&wl->mutex);
3123 return count;
3124}
3125
3126static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
3127 wl1271_sysfs_show_bt_coex_state,
3128 wl1271_sysfs_store_bt_coex_state);
3129
Juuso Oikarinend717fd62010-05-07 11:38:58 +03003130static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
3131 struct device_attribute *attr,
3132 char *buf)
3133{
3134 struct wl1271 *wl = dev_get_drvdata(dev);
3135 ssize_t len;
3136
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02003137 len = PAGE_SIZE;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03003138
3139 mutex_lock(&wl->mutex);
3140 if (wl->hw_pg_ver >= 0)
3141 len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
3142 else
3143 len = snprintf(buf, len, "n/a\n");
3144 mutex_unlock(&wl->mutex);
3145
3146 return len;
3147}
3148
3149static DEVICE_ATTR(hw_pg_ver, S_IRUGO | S_IWUSR,
3150 wl1271_sysfs_show_hw_pg_ver, NULL);
3151
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02003152int wl1271_register_hw(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003153{
3154 int ret;
3155
3156 if (wl->mac80211_registered)
3157 return 0;
3158
Arik Nemtsov31d26ec2010-10-16 21:49:52 +02003159 ret = wl1271_fetch_nvs(wl);
3160 if (ret == 0) {
3161 u8 *nvs_ptr = (u8 *)wl->nvs->nvs;
3162
3163 wl->mac_addr[0] = nvs_ptr[11];
3164 wl->mac_addr[1] = nvs_ptr[10];
3165 wl->mac_addr[2] = nvs_ptr[6];
3166 wl->mac_addr[3] = nvs_ptr[5];
3167 wl->mac_addr[4] = nvs_ptr[4];
3168 wl->mac_addr[5] = nvs_ptr[3];
3169 }
3170
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003171 SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
3172
3173 ret = ieee80211_register_hw(wl->hw);
3174 if (ret < 0) {
3175 wl1271_error("unable to register mac80211 hw: %d", ret);
3176 return ret;
3177 }
3178
3179 wl->mac80211_registered = true;
3180
Eliad Pellerd60080a2010-11-24 12:53:16 +02003181 wl1271_debugfs_init(wl);
3182
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03003183 register_netdevice_notifier(&wl1271_dev_notifier);
3184
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003185 wl1271_notice("loaded");
3186
3187 return 0;
3188}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02003189EXPORT_SYMBOL_GPL(wl1271_register_hw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003190
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003191void wl1271_unregister_hw(struct wl1271 *wl)
3192{
Juuso Oikarinen4ae3fa82011-01-14 12:48:46 +01003193 if (wl->state == WL1271_STATE_PLT)
3194 __wl1271_plt_stop(wl);
3195
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03003196 unregister_netdevice_notifier(&wl1271_dev_notifier);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003197 ieee80211_unregister_hw(wl->hw);
3198 wl->mac80211_registered = false;
3199
3200}
3201EXPORT_SYMBOL_GPL(wl1271_unregister_hw);
3202
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02003203int wl1271_init_ieee80211(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003204{
Juuso Oikarinen7a557242010-09-27 12:42:07 +02003205 static const u32 cipher_suites[] = {
3206 WLAN_CIPHER_SUITE_WEP40,
3207 WLAN_CIPHER_SUITE_WEP104,
3208 WLAN_CIPHER_SUITE_TKIP,
3209 WLAN_CIPHER_SUITE_CCMP,
3210 WL1271_CIPHER_SUITE_GEM,
3211 };
3212
Juuso Oikarinen1e2b7972009-10-08 21:56:20 +03003213 /* The tx descriptor buffer and the TKIP space. */
3214 wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
3215 sizeof(struct wl1271_tx_hw_descr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003216
3217 /* unit us */
3218 /* FIXME: find a proper value */
3219 wl->hw->channel_change_time = 10000;
Juuso Oikarinen50c500a2010-04-01 11:38:22 +03003220 wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003221
3222 wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
Juuso Oikarinen03442a32009-11-23 23:22:14 +02003223 IEEE80211_HW_BEACON_FILTER |
Juuso Oikarinen0a343322010-02-22 08:38:41 +02003224 IEEE80211_HW_SUPPORTS_PS |
Kalle Valo4695dc92010-03-18 12:26:38 +02003225 IEEE80211_HW_SUPPORTS_UAPSD |
Juuso Oikarinena9af0922010-03-26 12:53:30 +02003226 IEEE80211_HW_HAS_RATE_CONTROL |
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03003227 IEEE80211_HW_CONNECTION_MONITOR |
Eliad Peller62c07402011-02-02 11:20:05 +02003228 IEEE80211_HW_SUPPORTS_CQM_RSSI |
3229 IEEE80211_HW_REPORTS_TX_ACK_STATUS;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003230
Juuso Oikarinen7a557242010-09-27 12:42:07 +02003231 wl->hw->wiphy->cipher_suites = cipher_suites;
3232 wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
3233
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02003234 wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
Arik Nemtsov038d9252010-10-16 21:53:24 +02003235 BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003236 wl->hw->wiphy->max_scan_ssids = 1;
Guy Eilamea559b42010-12-09 16:54:59 +02003237 /*
3238 * Maximum length of elements in scanning probe request templates
3239 * should be the maximum length possible for a template, without
3240 * the IEEE80211 header of the template
3241 */
3242 wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
3243 sizeof(struct ieee80211_header);
Luciano Coelhoa8aaaf52011-01-11 18:25:18 +01003244
3245 /*
3246 * We keep local copies of the band structs because we need to
3247 * modify them on a per-device basis.
3248 */
3249 memcpy(&wl->bands[IEEE80211_BAND_2GHZ], &wl1271_band_2ghz,
3250 sizeof(wl1271_band_2ghz));
3251 memcpy(&wl->bands[IEEE80211_BAND_5GHZ], &wl1271_band_5ghz,
3252 sizeof(wl1271_band_5ghz));
3253
3254 wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
3255 &wl->bands[IEEE80211_BAND_2GHZ];
3256 wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
3257 &wl->bands[IEEE80211_BAND_5GHZ];
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03003258
Kalle Valo12bd8942010-03-18 12:26:33 +02003259 wl->hw->queues = 4;
Juuso Oikarinen31627dc2010-03-26 12:53:12 +02003260 wl->hw->max_rates = 1;
Kalle Valo12bd8942010-03-18 12:26:33 +02003261
Juuso Oikarinenb7417d92010-11-10 11:27:19 +01003262 wl->hw->wiphy->reg_notifier = wl1271_reg_notify;
3263
Teemu Paasikivi8197b712010-02-22 08:38:23 +02003264 SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003265
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003266 wl->hw->sta_data_size = sizeof(struct wl1271_station);
3267
Luciano Coelho4c9cfa72011-01-12 14:27:03 +01003268 wl->hw->max_rx_aggregation_subframes = 8;
3269
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003270 return 0;
3271}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02003272EXPORT_SYMBOL_GPL(wl1271_init_ieee80211);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003273
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003274#define WL1271_DEFAULT_CHANNEL 0
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02003275
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02003276struct ieee80211_hw *wl1271_alloc_hw(void)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003277{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003278 struct ieee80211_hw *hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003279 struct platform_device *plat_dev = NULL;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003280 struct wl1271 *wl;
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02003281 int i, j, ret;
Ido Yariv1f37cbc2010-09-30 13:28:27 +02003282 unsigned int order;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003283
3284 hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
3285 if (!hw) {
3286 wl1271_error("could not alloc ieee80211_hw");
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003287 ret = -ENOMEM;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003288 goto err_hw_alloc;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003289 }
3290
Julia Lawall929ebd32010-05-15 23:16:39 +02003291 plat_dev = kmemdup(&wl1271_device, sizeof(wl1271_device), GFP_KERNEL);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003292 if (!plat_dev) {
3293 wl1271_error("could not allocate platform_device");
3294 ret = -ENOMEM;
3295 goto err_plat_alloc;
3296 }
3297
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003298 wl = hw->priv;
3299 memset(wl, 0, sizeof(*wl));
3300
Juuso Oikarinen01c09162009-10-13 12:47:55 +03003301 INIT_LIST_HEAD(&wl->list);
3302
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003303 wl->hw = hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003304 wl->plat_dev = plat_dev;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003305
Juuso Oikarinen6742f552010-12-13 09:52:37 +02003306 for (i = 0; i < NUM_TX_QUEUES; i++)
3307 skb_queue_head_init(&wl->tx_queue[i]);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003308
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02003309 for (i = 0; i < NUM_TX_QUEUES; i++)
3310 for (j = 0; j < AP_MAX_LINKS; j++)
3311 skb_queue_head_init(&wl->links[j].tx_queue[i]);
3312
Juuso Oikarinen37b70a82009-10-08 21:56:21 +03003313 INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03003314 INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work);
Juuso Oikarinen117b38d2010-09-30 10:43:28 +02003315 INIT_WORK(&wl->irq_work, wl1271_irq_work);
3316 INIT_WORK(&wl->tx_work, wl1271_tx_work);
3317 INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
3318 INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003319 wl->channel = WL1271_DEFAULT_CHANNEL;
Juuso Oikarinen60e84c22010-03-26 12:53:25 +02003320 wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003321 wl->default_key = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003322 wl->rx_counter = 0;
Arik Nemtsovae113b52010-10-16 18:45:07 +02003323 wl->rx_config = WL1271_DEFAULT_STA_RX_CONFIG;
3324 wl->rx_filter = WL1271_DEFAULT_STA_RX_FILTER;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02003325 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003326 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
Juuso Oikarinena6fe2312009-12-11 15:40:58 +02003327 wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003328 wl->basic_rate = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02003329 wl->rate_set = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03003330 wl->band = IEEE80211_BAND_2GHZ;
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03003331 wl->vif = NULL;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02003332 wl->flags = 0;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003333 wl->sg_enabled = true;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03003334 wl->hw_pg_ver = -1;
Arik Nemtsov166d5042010-10-16 21:44:57 +02003335 wl->bss_type = MAX_BSS_TYPE;
3336 wl->set_bss_type = MAX_BSS_TYPE;
3337 wl->fw_bss_type = MAX_BSS_TYPE;
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02003338 wl->last_tx_hlid = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003339
Ido Yariv25eeb9e2010-10-12 16:20:06 +02003340 memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
Juuso Oikarinenbe7078c2009-10-08 21:56:26 +03003341 for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003342 wl->tx_frames[i] = NULL;
3343
3344 spin_lock_init(&wl->wl_lock);
3345
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003346 wl->state = WL1271_STATE_OFF;
3347 mutex_init(&wl->mutex);
3348
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02003349 /* Apply default driver configuration. */
3350 wl1271_conf_init(wl);
3351
Ido Yariv1f37cbc2010-09-30 13:28:27 +02003352 order = get_order(WL1271_AGGR_BUFFER_SIZE);
3353 wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
3354 if (!wl->aggr_buf) {
3355 ret = -ENOMEM;
3356 goto err_hw;
3357 }
3358
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003359 /* Register platform device */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003360 ret = platform_device_register(wl->plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003361 if (ret) {
3362 wl1271_error("couldn't register platform device");
Ido Yariv1f37cbc2010-09-30 13:28:27 +02003363 goto err_aggr;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003364 }
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003365 dev_set_drvdata(&wl->plat_dev->dev, wl);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003366
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003367 /* Create sysfs file to control bt coex state */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003368 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003369 if (ret < 0) {
3370 wl1271_error("failed to create sysfs file bt_coex_state");
3371 goto err_platform;
3372 }
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003373
Juuso Oikarinend717fd62010-05-07 11:38:58 +03003374 /* Create sysfs file to get HW PG version */
3375 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
3376 if (ret < 0) {
3377 wl1271_error("failed to create sysfs file hw_pg_ver");
3378 goto err_bt_coex_state;
3379 }
3380
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02003381 return hw;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003382
Juuso Oikarinend717fd62010-05-07 11:38:58 +03003383err_bt_coex_state:
3384 device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
3385
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003386err_platform:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003387 platform_device_unregister(wl->plat_dev);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003388
Ido Yariv1f37cbc2010-09-30 13:28:27 +02003389err_aggr:
3390 free_pages((unsigned long)wl->aggr_buf, order);
3391
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003392err_hw:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003393 wl1271_debugfs_exit(wl);
3394 kfree(plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003395
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003396err_plat_alloc:
3397 ieee80211_free_hw(hw);
3398
3399err_hw_alloc:
3400
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02003401 return ERR_PTR(ret);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02003402}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02003403EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02003404
3405int wl1271_free_hw(struct wl1271 *wl)
3406{
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003407 platform_device_unregister(wl->plat_dev);
Ido Yariv1f37cbc2010-09-30 13:28:27 +02003408 free_pages((unsigned long)wl->aggr_buf,
3409 get_order(WL1271_AGGR_BUFFER_SIZE));
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003410 kfree(wl->plat_dev);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02003411
3412 wl1271_debugfs_exit(wl);
3413
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02003414 vfree(wl->fw);
3415 wl->fw = NULL;
3416 kfree(wl->nvs);
3417 wl->nvs = NULL;
3418
3419 kfree(wl->fw_status);
3420 kfree(wl->tx_res_if);
3421
3422 ieee80211_free_hw(wl->hw);
3423
3424 return 0;
3425}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02003426EXPORT_SYMBOL_GPL(wl1271_free_hw);
3427
Guy Eilam491bbd62011-01-12 10:33:29 +01003428u32 wl12xx_debug_level = DEBUG_NONE;
Eliad Peller17c17552010-12-12 12:15:35 +02003429EXPORT_SYMBOL_GPL(wl12xx_debug_level);
Guy Eilam491bbd62011-01-12 10:33:29 +01003430module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
Eliad Peller17c17552010-12-12 12:15:35 +02003431MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
3432
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02003433MODULE_LICENSE("GPL");
Luciano Coelhob1a48ca2011-02-22 14:19:28 +02003434MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02003435MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");