blob: 15d8166fbf664c5aee435f1aceefa85a6e10c4fc [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>
Ido Yariv341b7cd2011-03-31 10:07:01 +020033#include <linux/wl12xx.h>
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030034
Shahar Levi00d20102010-11-08 11:20:10 +000035#include "wl12xx.h"
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030036#include "wl12xx_80211.h"
Shahar Levi00d20102010-11-08 11:20:10 +000037#include "reg.h"
38#include "io.h"
39#include "event.h"
40#include "tx.h"
41#include "rx.h"
42#include "ps.h"
43#include "init.h"
44#include "debugfs.h"
45#include "cmd.h"
46#include "boot.h"
47#include "testmode.h"
48#include "scan.h"
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030049
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +020050#define WL1271_BOOT_RETRIES 3
51
Juuso Oikarinen8a080482009-10-13 12:47:44 +030052static struct conf_drv_settings default_conf = {
53 .sg = {
Arik Nemtsov801f8702011-04-18 14:15:20 +030054 .sta_params = {
Juuso Oikarinen1b00f542010-03-18 12:26:30 +020055 [CONF_SG_BT_PER_THRESHOLD] = 7500,
56 [CONF_SG_HV3_MAX_OVERRIDE] = 0,
57 [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400,
Luciano Coelhod9482e22011-03-21 17:58:32 +020058 [CONF_SG_BT_LOAD_RATIO] = 200,
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +030059 [CONF_SG_AUTO_PS_MODE] = 1,
Juuso Oikarinen1b00f542010-03-18 12:26:30 +020060 [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
61 [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
62 [CONF_SG_ANTENNA_CONFIGURATION] = 0,
63 [CONF_SG_BEACON_MISS_PERCENT] = 60,
64 [CONF_SG_RATE_ADAPT_THRESH] = 12,
65 [CONF_SG_RATE_ADAPT_SNR] = 0,
66 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR] = 10,
67 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR] = 30,
68 [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR] = 8,
69 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR] = 20,
70 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR] = 50,
71 /* Note: with UPSD, this should be 4 */
72 [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR] = 8,
73 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR] = 7,
74 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR] = 25,
75 [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR] = 20,
76 /* Note: with UPDS, this should be 15 */
77 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR] = 8,
78 /* Note: with UPDS, this should be 50 */
79 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR] = 40,
80 /* Note: with UPDS, this should be 10 */
81 [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR] = 20,
82 [CONF_SG_RXT] = 1200,
83 [CONF_SG_TXT] = 1000,
84 [CONF_SG_ADAPTIVE_RXT_TXT] = 1,
85 [CONF_SG_PS_POLL_TIMEOUT] = 10,
86 [CONF_SG_UPSD_TIMEOUT] = 10,
87 [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR] = 7,
88 [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR] = 15,
89 [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR] = 15,
90 [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR] = 8,
91 [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR] = 20,
92 [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR] = 15,
93 [CONF_SG_WLAN_ACTIVE_BT_ACL_MIN_BR] = 20,
94 [CONF_SG_WLAN_ACTIVE_BT_ACL_MAX_BR] = 50,
95 [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_BR] = 10,
96 [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
97 [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP] = 800,
98 [CONF_SG_PASSIVE_SCAN_A2DP_BT_TIME] = 75,
99 [CONF_SG_PASSIVE_SCAN_A2DP_WLAN_TIME] = 15,
100 [CONF_SG_HV3_MAX_SERVED] = 6,
101 [CONF_SG_DHCP_TIME] = 5000,
102 [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
103 },
Arik Nemtsov801f8702011-04-18 14:15:20 +0300104 .ap_params = {
105 [CONF_SG_BT_PER_THRESHOLD] = 7500,
106 [CONF_SG_HV3_MAX_OVERRIDE] = 0,
107 [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400,
108 [CONF_SG_BT_LOAD_RATIO] = 50,
109 [CONF_SG_AUTO_PS_MODE] = 1,
110 [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
111 [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
112 [CONF_SG_ANTENNA_CONFIGURATION] = 0,
113 [CONF_SG_BEACON_MISS_PERCENT] = 60,
114 [CONF_SG_RATE_ADAPT_THRESH] = 64,
115 [CONF_SG_RATE_ADAPT_SNR] = 1,
116 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR] = 10,
117 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR] = 25,
118 [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR] = 25,
119 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR] = 20,
120 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR] = 25,
121 [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR] = 25,
122 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR] = 7,
123 [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR] = 25,
124 [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR] = 25,
125 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR] = 8,
126 [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR] = 25,
127 [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR] = 25,
128 [CONF_SG_RXT] = 1200,
129 [CONF_SG_TXT] = 1000,
130 [CONF_SG_ADAPTIVE_RXT_TXT] = 1,
131 [CONF_SG_PS_POLL_TIMEOUT] = 10,
132 [CONF_SG_UPSD_TIMEOUT] = 10,
133 [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR] = 7,
134 [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR] = 15,
135 [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR] = 15,
136 [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR] = 8,
137 [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR] = 20,
138 [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR] = 15,
139 [CONF_SG_WLAN_ACTIVE_BT_ACL_MIN_BR] = 20,
140 [CONF_SG_WLAN_ACTIVE_BT_ACL_MAX_BR] = 50,
141 [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_BR] = 10,
142 [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
143 [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP] = 800,
144 [CONF_SG_PASSIVE_SCAN_A2DP_BT_TIME] = 75,
145 [CONF_SG_PASSIVE_SCAN_A2DP_WLAN_TIME] = 15,
146 [CONF_SG_HV3_MAX_SERVED] = 6,
147 [CONF_SG_DHCP_TIME] = 5000,
148 [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
149 [CONF_SG_TEMP_PARAM_1] = 0,
150 [CONF_SG_TEMP_PARAM_2] = 0,
151 [CONF_SG_TEMP_PARAM_3] = 0,
152 [CONF_SG_TEMP_PARAM_4] = 0,
153 [CONF_SG_TEMP_PARAM_5] = 0,
154 [CONF_SG_AP_BEACON_MISS_TX] = 3,
155 [CONF_SG_RX_WINDOW_LENGTH] = 6,
156 [CONF_SG_AP_CONNECTION_PROTECTION_TIME] = 50,
157 [CONF_SG_TEMP_PARAM_6] = 1,
158 },
Juuso Oikarinen1b00f542010-03-18 12:26:30 +0200159 .state = CONF_SG_PROTECTIVE,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300160 },
161 .rx = {
162 .rx_msdu_life_time = 512000,
163 .packet_detection_threshold = 0,
164 .ps_poll_timeout = 15,
165 .upsd_timeout = 15,
Arik Nemtsov5f704d12011-04-18 14:15:21 +0300166 .rts_threshold = IEEE80211_MAX_RTS_THRESHOLD,
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200167 .rx_cca_threshold = 0,
168 .irq_blk_threshold = 0xFFFF,
169 .irq_pkt_threshold = 0,
170 .irq_timeout = 600,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300171 .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY,
172 },
173 .tx = {
174 .tx_energy_detection = 0,
Arik Nemtsov1e05a812010-10-16 17:44:51 +0200175 .sta_rc_conf = {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +0300176 .enabled_rates = 0,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300177 .short_retry_limit = 10,
178 .long_retry_limit = 10,
Arik Nemtsov1e05a812010-10-16 17:44:51 +0200179 .aflags = 0,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300180 },
181 .ac_conf_count = 4,
182 .ac_conf = {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200183 [CONF_TX_AC_BE] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300184 .ac = CONF_TX_AC_BE,
185 .cw_min = 15,
186 .cw_max = 63,
187 .aifsn = 3,
188 .tx_op_limit = 0,
189 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200190 [CONF_TX_AC_BK] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300191 .ac = CONF_TX_AC_BK,
192 .cw_min = 15,
193 .cw_max = 63,
194 .aifsn = 7,
195 .tx_op_limit = 0,
196 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200197 [CONF_TX_AC_VI] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300198 .ac = CONF_TX_AC_VI,
199 .cw_min = 15,
200 .cw_max = 63,
201 .aifsn = CONF_TX_AIFS_PIFS,
202 .tx_op_limit = 3008,
203 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200204 [CONF_TX_AC_VO] = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300205 .ac = CONF_TX_AC_VO,
206 .cw_min = 15,
207 .cw_max = 63,
208 .aifsn = CONF_TX_AIFS_PIFS,
209 .tx_op_limit = 1504,
210 },
211 },
Luciano Coelho25eaea302011-05-02 12:37:33 +0300212 .ap_max_tx_retries = 100,
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200213 .tid_conf_count = 4,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300214 .tid_conf = {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200215 [CONF_TX_AC_BE] = {
216 .queue_id = CONF_TX_AC_BE,
217 .channel_type = CONF_CHANNEL_TYPE_EDCF,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300218 .tsid = CONF_TX_AC_BE,
219 .ps_scheme = CONF_PS_SCHEME_LEGACY,
220 .ack_policy = CONF_ACK_POLICY_LEGACY,
221 .apsd_conf = {0, 0},
222 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200223 [CONF_TX_AC_BK] = {
224 .queue_id = CONF_TX_AC_BK,
225 .channel_type = CONF_CHANNEL_TYPE_EDCF,
226 .tsid = CONF_TX_AC_BK,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300227 .ps_scheme = CONF_PS_SCHEME_LEGACY,
228 .ack_policy = CONF_ACK_POLICY_LEGACY,
229 .apsd_conf = {0, 0},
230 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200231 [CONF_TX_AC_VI] = {
232 .queue_id = CONF_TX_AC_VI,
233 .channel_type = CONF_CHANNEL_TYPE_EDCF,
234 .tsid = CONF_TX_AC_VI,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300235 .ps_scheme = CONF_PS_SCHEME_LEGACY,
236 .ack_policy = CONF_ACK_POLICY_LEGACY,
237 .apsd_conf = {0, 0},
238 },
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200239 [CONF_TX_AC_VO] = {
240 .queue_id = CONF_TX_AC_VO,
241 .channel_type = CONF_CHANNEL_TYPE_EDCF,
242 .tsid = CONF_TX_AC_VO,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300243 .ps_scheme = CONF_PS_SCHEME_LEGACY,
244 .ack_policy = CONF_ACK_POLICY_LEGACY,
245 .apsd_conf = {0, 0},
246 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300247 },
248 .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD,
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200249 .tx_compl_timeout = 700,
Juuso Oikarinenebba60c2010-04-01 11:38:20 +0300250 .tx_compl_threshold = 4,
251 .basic_rate = CONF_HW_BIT_RATE_1MBPS,
252 .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS,
Arik Nemtsov1e05a812010-10-16 17:44:51 +0200253 .tmpl_short_retry_limit = 10,
254 .tmpl_long_retry_limit = 10,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300255 },
256 .conn = {
257 .wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
Juuso Oikarinen50c500a2010-04-01 11:38:22 +0300258 .listen_interval = 1,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300259 .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED,
Shahar Levibc76b942011-05-11 11:14:22 +0300260 .bcn_filt_ie_count = 2,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300261 .bcn_filt_ie = {
262 [0] = {
263 .ie = WLAN_EID_CHANNEL_SWITCH,
264 .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE,
Shahar Levibc76b942011-05-11 11:14:22 +0300265 },
266 [1] = {
267 .ie = WLAN_EID_HT_INFORMATION,
268 .rule = CONF_BCN_RULE_PASS_ON_CHANGE,
269 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300270 },
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200271 .synch_fail_thold = 10,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300272 .bss_lose_timeout = 100,
273 .beacon_rx_timeout = 10000,
274 .broadcast_timeout = 20000,
275 .rx_broadcast_in_ps = 1,
Juuso Oikarinen90494a92010-07-08 17:50:00 +0300276 .ps_poll_threshold = 10,
277 .ps_poll_recovery_period = 700,
Juuso Oikarinen11f70f92009-10-13 12:47:46 +0300278 .bet_enable = CONF_BET_MODE_ENABLE,
Ohad Ben-Cohen958b20e02011-03-14 18:53:10 +0200279 .bet_max_consecutive = 50,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +0200280 .psm_entry_retries = 5,
Shahar Levi23708412011-04-13 14:52:50 +0300281 .psm_exit_retries = 16,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +0200282 .psm_entry_nullfunc_retries = 3,
283 .psm_entry_hangover_period = 1,
Juuso Oikarinen50c500a2010-04-01 11:38:22 +0300284 .keep_alive_interval = 55000,
285 .max_listen_interval = 20,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300286 },
Luciano Coelho6e92b412009-12-11 15:40:50 +0200287 .itrim = {
288 .enable = false,
289 .timeout = 50000,
Juuso Oikarinen38ad2d82009-12-11 15:41:08 +0200290 },
291 .pm_config = {
292 .host_clk_settling_time = 5000,
293 .host_fast_wakeup_support = false
Juuso Oikarinen00236aed2010-04-09 11:07:30 +0300294 },
295 .roam_trigger = {
Juuso Oikarinen00236aed2010-04-09 11:07:30 +0300296 .trigger_pacing = 1,
297 .avg_weight_rssi_beacon = 20,
298 .avg_weight_rssi_data = 10,
299 .avg_weight_snr_beacon = 20,
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100300 .avg_weight_snr_data = 10,
Juuso Oikarinenbea39d62010-09-21 08:14:31 +0200301 },
302 .scan = {
303 .min_dwell_time_active = 7500,
304 .max_dwell_time_active = 30000,
Juuso Oikarinenea45b2c2011-01-24 07:01:54 +0100305 .min_dwell_time_passive = 100000,
306 .max_dwell_time_passive = 100000,
Juuso Oikarinenbea39d62010-09-21 08:14:31 +0200307 .num_probe_reqs = 2,
308 },
Luciano Coelho3a9d60e2011-05-10 14:06:31 +0300309 .sched_scan = {
310 /* sched_scan requires dwell times in TU instead of TU/1000 */
311 .min_dwell_time_active = 8,
312 .max_dwell_time_active = 30,
313 .dwell_time_passive = 100,
Luciano Coelho50a66d72011-05-27 15:34:47 +0300314 .dwell_time_dfs = 150,
Luciano Coelho3a9d60e2011-05-10 14:06:31 +0300315 .num_probe_reqs = 2,
316 .rssi_threshold = -90,
317 .snr_threshold = 0,
318 },
Juuso Oikarinen644a4862010-10-05 13:11:56 +0200319 .rf = {
320 .tx_per_channel_power_compensation_2 = {
321 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
322 },
323 .tx_per_channel_power_compensation_5 = {
324 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
325 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
326 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
327 },
328 },
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100329 .ht = {
330 .tx_ba_win_size = 64,
331 .inactivity_timeout = 10000,
332 },
Shahar Levi13b107d2011-03-06 16:32:12 +0200333 .mem_wl127x = {
Eliad Pellerfe5ef092011-02-02 09:59:36 +0200334 .num_stations = 1,
335 .ssid_profiles = 1,
336 .rx_block_num = 70,
337 .tx_min_block_num = 40,
Ido Yariv4cf557f2011-04-18 16:45:10 +0300338 .dynamic_memory = 1,
Ido Yarivb16d4b62011-03-01 15:14:44 +0200339 .min_req_tx_blocks = 100,
Eliad Pellerc8bde242011-02-02 09:59:35 +0200340 .min_req_rx_blocks = 22,
341 .tx_min = 27,
Shahar Levi13b107d2011-03-06 16:32:12 +0200342 },
343 .mem_wl128x = {
344 .num_stations = 1,
345 .ssid_profiles = 1,
346 .rx_block_num = 40,
347 .tx_min_block_num = 40,
348 .dynamic_memory = 1,
349 .min_req_tx_blocks = 45,
350 .min_req_rx_blocks = 22,
351 .tx_min = 27,
352 },
Shahar Leviff868432011-04-11 15:41:46 +0300353 .fm_coex = {
354 .enable = true,
355 .swallow_period = 5,
356 .n_divider_fref_set_1 = 0xff, /* default */
357 .n_divider_fref_set_2 = 12,
358 .m_divider_fref_set_1 = 148,
359 .m_divider_fref_set_2 = 0xffff, /* default */
360 .coex_pll_stabilization_time = 0xffffffff, /* default */
361 .ldo_stabilization_time = 0xffff, /* default */
362 .fm_disturbed_band_margin = 0xff, /* default */
363 .swallow_clk_diff = 0xff, /* default */
364 },
Eliad Pellerf84673d2011-05-15 11:10:28 +0300365 .rx_streaming = {
366 .duration = 150,
367 .queues = 0x1,
368 .interval = 20,
Eliad Peller77ddaa12011-05-15 11:10:29 +0300369 .always = 0,
Eliad Pellerf84673d2011-05-15 11:10:28 +0300370 },
Luciano Coelhoafb7d3c2011-04-01 20:48:02 +0300371 .hci_io_ds = HCI_IO_DS_6MA,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300372};
373
Arik Nemtsov7dece1c2011-04-18 14:15:28 +0300374static void __wl1271_op_remove_interface(struct wl1271 *wl,
375 bool reset_tx_queues);
Arik Nemtsov7f179b42010-10-16 21:39:06 +0200376static void wl1271_free_ap_keys(struct wl1271 *wl);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +0200377
378
Juuso Oikarinena1dd8182010-03-18 12:26:31 +0200379static void wl1271_device_release(struct device *dev)
380{
381
382}
383
384static struct platform_device wl1271_device = {
385 .name = "wl1271",
386 .id = -1,
387
388 /* device model insists to have a release function */
389 .dev = {
390 .release = wl1271_device_release,
391 },
392};
393
Juuso Oikarinenf9f774c2011-03-21 10:43:36 +0200394static DEFINE_MUTEX(wl_list_mutex);
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300395static LIST_HEAD(wl_list);
396
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300397static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
398 void *arg)
399{
400 struct net_device *dev = arg;
401 struct wireless_dev *wdev;
402 struct wiphy *wiphy;
403 struct ieee80211_hw *hw;
404 struct wl1271 *wl;
405 struct wl1271 *wl_temp;
406 int ret = 0;
407
408 /* Check that this notification is for us. */
409 if (what != NETDEV_CHANGE)
410 return NOTIFY_DONE;
411
412 wdev = dev->ieee80211_ptr;
413 if (wdev == NULL)
414 return NOTIFY_DONE;
415
416 wiphy = wdev->wiphy;
417 if (wiphy == NULL)
418 return NOTIFY_DONE;
419
420 hw = wiphy_priv(wiphy);
421 if (hw == NULL)
422 return NOTIFY_DONE;
423
424 wl_temp = hw->priv;
Juuso Oikarinenf9f774c2011-03-21 10:43:36 +0200425 mutex_lock(&wl_list_mutex);
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300426 list_for_each_entry(wl, &wl_list, list) {
427 if (wl == wl_temp)
428 break;
429 }
Juuso Oikarinenf9f774c2011-03-21 10:43:36 +0200430 mutex_unlock(&wl_list_mutex);
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300431 if (wl != wl_temp)
432 return NOTIFY_DONE;
433
434 mutex_lock(&wl->mutex);
435
436 if (wl->state == WL1271_STATE_OFF)
437 goto out;
438
439 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
440 goto out;
441
Ido Yariva6208652011-03-01 15:14:41 +0200442 ret = wl1271_ps_elp_wakeup(wl);
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +0300443 if (ret < 0)
444 goto out;
445
446 if ((dev->operstate == IF_OPER_UP) &&
447 !test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags)) {
448 wl1271_cmd_set_sta_state(wl);
449 wl1271_info("Association completed.");
450 }
451
452 wl1271_ps_elp_sleep(wl);
453
454out:
455 mutex_unlock(&wl->mutex);
456
457 return NOTIFY_OK;
458}
459
Juuso Oikarinenb7417d92010-11-10 11:27:19 +0100460static int wl1271_reg_notify(struct wiphy *wiphy,
Luciano Coelho573c67c2010-11-26 13:44:59 +0200461 struct regulatory_request *request)
462{
Juuso Oikarinenb7417d92010-11-10 11:27:19 +0100463 struct ieee80211_supported_band *band;
464 struct ieee80211_channel *ch;
465 int i;
466
467 band = wiphy->bands[IEEE80211_BAND_5GHZ];
468 for (i = 0; i < band->n_channels; i++) {
469 ch = &band->channels[i];
470 if (ch->flags & IEEE80211_CHAN_DISABLED)
471 continue;
472
473 if (ch->flags & IEEE80211_CHAN_RADAR)
474 ch->flags |= IEEE80211_CHAN_NO_IBSS |
475 IEEE80211_CHAN_PASSIVE_SCAN;
476
477 }
478
479 return 0;
480}
481
Eliad Peller77ddaa12011-05-15 11:10:29 +0300482static int wl1271_set_rx_streaming(struct wl1271 *wl, bool enable)
483{
484 int ret = 0;
485
486 /* we should hold wl->mutex */
487 ret = wl1271_acx_ps_rx_streaming(wl, enable);
488 if (ret < 0)
489 goto out;
490
491 if (enable)
492 set_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags);
493 else
494 clear_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags);
495out:
496 return ret;
497}
498
499/*
500 * this function is being called when the rx_streaming interval
501 * has beed changed or rx_streaming should be disabled
502 */
503int wl1271_recalc_rx_streaming(struct wl1271 *wl)
504{
505 int ret = 0;
506 int period = wl->conf.rx_streaming.interval;
507
508 /* don't reconfigure if rx_streaming is disabled */
509 if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
510 goto out;
511
512 /* reconfigure/disable according to new streaming_period */
513 if (period &&
514 test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) &&
515 (wl->conf.rx_streaming.always ||
516 test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
517 ret = wl1271_set_rx_streaming(wl, true);
518 else {
519 ret = wl1271_set_rx_streaming(wl, false);
520 /* don't cancel_work_sync since we might deadlock */
521 del_timer_sync(&wl->rx_streaming_timer);
522 }
523out:
524 return ret;
525}
526
527static void wl1271_rx_streaming_enable_work(struct work_struct *work)
528{
529 int ret;
530 struct wl1271 *wl =
531 container_of(work, struct wl1271, rx_streaming_enable_work);
532
533 mutex_lock(&wl->mutex);
534
535 if (test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags) ||
536 !test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) ||
537 (!wl->conf.rx_streaming.always &&
538 !test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
539 goto out;
540
541 if (!wl->conf.rx_streaming.interval)
542 goto out;
543
544 ret = wl1271_ps_elp_wakeup(wl);
545 if (ret < 0)
546 goto out;
547
548 ret = wl1271_set_rx_streaming(wl, true);
549 if (ret < 0)
550 goto out_sleep;
551
552 /* stop it after some time of inactivity */
553 mod_timer(&wl->rx_streaming_timer,
554 jiffies + msecs_to_jiffies(wl->conf.rx_streaming.duration));
555
556out_sleep:
557 wl1271_ps_elp_sleep(wl);
558out:
559 mutex_unlock(&wl->mutex);
560}
561
562static void wl1271_rx_streaming_disable_work(struct work_struct *work)
563{
564 int ret;
565 struct wl1271 *wl =
566 container_of(work, struct wl1271, rx_streaming_disable_work);
567
568 mutex_lock(&wl->mutex);
569
570 if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
571 goto out;
572
573 ret = wl1271_ps_elp_wakeup(wl);
574 if (ret < 0)
575 goto out;
576
577 ret = wl1271_set_rx_streaming(wl, false);
578 if (ret)
579 goto out_sleep;
580
581out_sleep:
582 wl1271_ps_elp_sleep(wl);
583out:
584 mutex_unlock(&wl->mutex);
585}
586
587static void wl1271_rx_streaming_timer(unsigned long data)
588{
589 struct wl1271 *wl = (struct wl1271 *)data;
590 ieee80211_queue_work(wl->hw, &wl->rx_streaming_disable_work);
591}
592
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300593static void wl1271_conf_init(struct wl1271 *wl)
594{
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300595
596 /*
597 * This function applies the default configuration to the driver. This
598 * function is invoked upon driver load (spi probe.)
599 *
600 * The configuration is stored in a run-time structure in order to
601 * facilitate for run-time adjustment of any of the parameters. Making
602 * changes to the configuration structure will apply the new values on
603 * the next interface up (wl1271_op_start.)
604 */
605
606 /* apply driver default configuration */
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300607 memcpy(&wl->conf, &default_conf, sizeof(default_conf));
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300608}
609
610
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300611static int wl1271_plt_init(struct wl1271 *wl)
612{
Luciano Coelho12419cc2010-02-18 13:25:44 +0200613 struct conf_tx_ac_category *conf_ac;
614 struct conf_tx_tid *conf_tid;
615 int ret, i;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300616
Shahar Levi49d750ca2011-03-06 16:32:09 +0200617 if (wl->chip.id == CHIP_ID_1283_PG20)
618 ret = wl128x_cmd_general_parms(wl);
619 else
620 ret = wl1271_cmd_general_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200621 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200622 return ret;
623
Shahar Levi49d750ca2011-03-06 16:32:09 +0200624 if (wl->chip.id == CHIP_ID_1283_PG20)
625 ret = wl128x_cmd_radio_parms(wl);
626 else
627 ret = wl1271_cmd_radio_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200628 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200629 return ret;
630
Shahar Levi49d750ca2011-03-06 16:32:09 +0200631 if (wl->chip.id != CHIP_ID_1283_PG20) {
632 ret = wl1271_cmd_ext_radio_parms(wl);
633 if (ret < 0)
634 return ret;
635 }
Juuso Oikarinen644a4862010-10-05 13:11:56 +0200636 if (ret < 0)
637 return ret;
638
Shahar Levi48a61472011-03-06 16:32:08 +0200639 /* Chip-specific initializations */
640 ret = wl1271_chip_specific_init(wl);
641 if (ret < 0)
642 return ret;
643
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200644 ret = wl1271_sta_init_templates_config(wl);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200645 if (ret < 0)
646 return ret;
647
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300648 ret = wl1271_acx_init_mem_config(wl);
649 if (ret < 0)
650 return ret;
651
Luciano Coelho12419cc2010-02-18 13:25:44 +0200652 /* PHY layer config */
653 ret = wl1271_init_phy_config(wl);
654 if (ret < 0)
655 goto out_free_memmap;
656
657 ret = wl1271_acx_dco_itrim_params(wl);
658 if (ret < 0)
659 goto out_free_memmap;
660
661 /* Initialize connection monitoring thresholds */
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +0200662 ret = wl1271_acx_conn_monit_params(wl, false);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200663 if (ret < 0)
664 goto out_free_memmap;
665
666 /* Bluetooth WLAN coexistence */
667 ret = wl1271_init_pta(wl);
668 if (ret < 0)
669 goto out_free_memmap;
670
Shahar Leviff868432011-04-11 15:41:46 +0300671 /* FM WLAN coexistence */
672 ret = wl1271_acx_fm_coex(wl);
673 if (ret < 0)
674 goto out_free_memmap;
675
Luciano Coelho12419cc2010-02-18 13:25:44 +0200676 /* Energy detection */
677 ret = wl1271_init_energy_detection(wl);
678 if (ret < 0)
679 goto out_free_memmap;
680
Gery Kahn1ec610e2011-02-01 03:03:08 -0600681 ret = wl1271_acx_sta_mem_cfg(wl);
682 if (ret < 0)
683 goto out_free_memmap;
684
Luciano Coelho12419cc2010-02-18 13:25:44 +0200685 /* Default fragmentation threshold */
Arik Nemtsov68d069c2010-11-08 10:51:07 +0100686 ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200687 if (ret < 0)
688 goto out_free_memmap;
689
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200690 /* Default TID/AC configuration */
691 BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count);
Luciano Coelho12419cc2010-02-18 13:25:44 +0200692 for (i = 0; i < wl->conf.tx.tid_conf_count; i++) {
Juuso Oikarinen9987a9d2010-09-01 11:31:12 +0200693 conf_ac = &wl->conf.tx.ac_conf[i];
694 ret = wl1271_acx_ac_cfg(wl, conf_ac->ac, conf_ac->cw_min,
695 conf_ac->cw_max, conf_ac->aifsn,
696 conf_ac->tx_op_limit);
697 if (ret < 0)
698 goto out_free_memmap;
699
Luciano Coelho12419cc2010-02-18 13:25:44 +0200700 conf_tid = &wl->conf.tx.tid_conf[i];
701 ret = wl1271_acx_tid_cfg(wl, conf_tid->queue_id,
702 conf_tid->channel_type,
703 conf_tid->tsid,
704 conf_tid->ps_scheme,
705 conf_tid->ack_policy,
706 conf_tid->apsd_conf[0],
707 conf_tid->apsd_conf[1]);
708 if (ret < 0)
709 goto out_free_memmap;
710 }
711
Luciano Coelho12419cc2010-02-18 13:25:44 +0200712 /* Enable data path */
Luciano Coelho94210892009-12-11 15:40:55 +0200713 ret = wl1271_cmd_data_path(wl, 1);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300714 if (ret < 0)
Luciano Coelho12419cc2010-02-18 13:25:44 +0200715 goto out_free_memmap;
716
717 /* Configure for CAM power saving (ie. always active) */
718 ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
719 if (ret < 0)
720 goto out_free_memmap;
721
722 /* configure PM */
723 ret = wl1271_acx_pm_config(wl);
724 if (ret < 0)
725 goto out_free_memmap;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300726
727 return 0;
Luciano Coelho12419cc2010-02-18 13:25:44 +0200728
729 out_free_memmap:
730 kfree(wl->target_mem_map);
731 wl->target_mem_map = NULL;
732
733 return ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300734}
735
Arik Nemtsovb622d992011-02-23 00:22:31 +0200736static void wl1271_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_blks)
737{
738 bool fw_ps;
739
740 /* only regulate station links */
741 if (hlid < WL1271_AP_STA_HLID_START)
742 return;
743
744 fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
745
746 /*
747 * Wake up from high level PS if the STA is asleep with too little
748 * blocks in FW or if the STA is awake.
749 */
750 if (!fw_ps || tx_blks < WL1271_PS_STA_MAX_BLOCKS)
751 wl1271_ps_link_end(wl, hlid);
752
753 /* Start high-level PS if the STA is asleep with enough blocks in FW */
754 else if (fw_ps && tx_blks >= WL1271_PS_STA_MAX_BLOCKS)
755 wl1271_ps_link_start(wl, hlid, true);
756}
757
758static void wl1271_irq_update_links_status(struct wl1271 *wl,
759 struct wl1271_fw_ap_status *status)
760{
761 u32 cur_fw_ps_map;
762 u8 hlid;
763
764 cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
765 if (wl->ap_fw_ps_map != cur_fw_ps_map) {
766 wl1271_debug(DEBUG_PSM,
767 "link ps prev 0x%x cur 0x%x changed 0x%x",
768 wl->ap_fw_ps_map, cur_fw_ps_map,
769 wl->ap_fw_ps_map ^ cur_fw_ps_map);
770
771 wl->ap_fw_ps_map = cur_fw_ps_map;
772 }
773
774 for (hlid = WL1271_AP_STA_HLID_START; hlid < AP_MAX_LINKS; hlid++) {
775 u8 cnt = status->tx_lnk_free_blks[hlid] -
776 wl->links[hlid].prev_freed_blks;
777
778 wl->links[hlid].prev_freed_blks =
779 status->tx_lnk_free_blks[hlid];
780 wl->links[hlid].allocated_blks -= cnt;
781
782 wl1271_irq_ps_regulate_link(wl, hlid,
783 wl->links[hlid].allocated_blks);
784 }
785}
786
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300787static void wl1271_fw_status(struct wl1271 *wl,
Eliad Pellerc8bde242011-02-02 09:59:35 +0200788 struct wl1271_fw_full_status *full_status)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300789{
Eliad Pellerc8bde242011-02-02 09:59:35 +0200790 struct wl1271_fw_common_status *status = &full_status->common;
Juuso Oikarinenac5e1e32010-02-22 08:38:38 +0200791 struct timespec ts;
Shahar Levi13b107d2011-03-06 16:32:12 +0200792 u32 old_tx_blk_count = wl->tx_blocks_available;
Ido Yarivd2f4d472011-03-31 10:07:00 +0200793 u32 freed_blocks = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300794 int i;
795
Shahar Levi13b107d2011-03-06 16:32:12 +0200796 if (wl->bss_type == BSS_TYPE_AP_BSS) {
Eliad Pellerc8bde242011-02-02 09:59:35 +0200797 wl1271_raw_read(wl, FW_STATUS_ADDR, status,
798 sizeof(struct wl1271_fw_ap_status), false);
Shahar Levi13b107d2011-03-06 16:32:12 +0200799 } else {
Eliad Pellerc8bde242011-02-02 09:59:35 +0200800 wl1271_raw_read(wl, FW_STATUS_ADDR, status,
801 sizeof(struct wl1271_fw_sta_status), false);
Shahar Levi13b107d2011-03-06 16:32:12 +0200802 }
803
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300804 wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
805 "drv_rx_counter = %d, tx_results_counter = %d)",
806 status->intr,
807 status->fw_rx_counter,
808 status->drv_rx_counter,
809 status->tx_results_counter);
810
811 /* update number of available TX blocks */
812 for (i = 0; i < NUM_TX_QUEUES; i++) {
Ido Yarivd2f4d472011-03-31 10:07:00 +0200813 freed_blocks += le32_to_cpu(status->tx_released_blks[i]) -
814 wl->tx_blocks_freed[i];
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300815
816 wl->tx_blocks_freed[i] =
817 le32_to_cpu(status->tx_released_blks[i]);
Shahar Levi13b107d2011-03-06 16:32:12 +0200818 }
819
Ido Yarivd2f4d472011-03-31 10:07:00 +0200820 wl->tx_allocated_blocks -= freed_blocks;
Shahar Levi13b107d2011-03-06 16:32:12 +0200821
Ido Yarivd2f4d472011-03-31 10:07:00 +0200822 if (wl->bss_type == BSS_TYPE_AP_BSS) {
823 /* Update num of allocated TX blocks per link and ps status */
824 wl1271_irq_update_links_status(wl, &full_status->ap);
825 wl->tx_blocks_available += freed_blocks;
826 } else {
827 int avail = full_status->sta.tx_total - wl->tx_allocated_blocks;
828
829 /*
830 * The FW might change the total number of TX memblocks before
831 * we get a notification about blocks being released. Thus, the
832 * available blocks calculation might yield a temporary result
833 * which is lower than the actual available blocks. Keeping in
834 * mind that only blocks that were allocated can be moved from
835 * TX to RX, tx_blocks_available should never decrease here.
836 */
837 wl->tx_blocks_available = max((int)wl->tx_blocks_available,
838 avail);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300839 }
840
Ido Yariva5225502010-10-12 14:49:10 +0200841 /* if more blocks are available now, tx work can be scheduled */
Shahar Levi13b107d2011-03-06 16:32:12 +0200842 if (wl->tx_blocks_available > old_tx_blk_count)
Ido Yariva5225502010-10-12 14:49:10 +0200843 clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300844
845 /* update the host-chipset time offset */
Juuso Oikarinenac5e1e32010-02-22 08:38:38 +0200846 getnstimeofday(&ts);
847 wl->time_offset = (timespec_to_ns(&ts) >> 10) -
848 (s64)le32_to_cpu(status->fw_localtime);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300849}
850
Ido Yariva6208652011-03-01 15:14:41 +0200851static void wl1271_flush_deferred_work(struct wl1271 *wl)
852{
853 struct sk_buff *skb;
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200854
Ido Yariva6208652011-03-01 15:14:41 +0200855 /* Pass all received frames to the network stack */
856 while ((skb = skb_dequeue(&wl->deferred_rx_queue)))
857 ieee80211_rx_ni(wl->hw, skb);
858
859 /* Return sent skbs to the network stack */
860 while ((skb = skb_dequeue(&wl->deferred_tx_queue)))
861 ieee80211_tx_status(wl->hw, skb);
862}
863
864static void wl1271_netstack_work(struct work_struct *work)
865{
866 struct wl1271 *wl =
867 container_of(work, struct wl1271, netstack_work);
868
869 do {
870 wl1271_flush_deferred_work(wl);
871 } while (skb_queue_len(&wl->deferred_rx_queue));
872}
873
874#define WL1271_IRQ_MAX_LOOPS 256
875
876irqreturn_t wl1271_irq(int irq, void *cookie)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300877{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300878 int ret;
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300879 u32 intr;
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200880 int loopcount = WL1271_IRQ_MAX_LOOPS;
Ido Yariva6208652011-03-01 15:14:41 +0200881 struct wl1271 *wl = (struct wl1271 *)cookie;
882 bool done = false;
883 unsigned int defer_count;
Ido Yarivb07d4032011-03-01 15:14:43 +0200884 unsigned long flags;
885
886 /* TX might be handled here, avoid redundant work */
887 set_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
888 cancel_work_sync(&wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300889
Ido Yariv341b7cd2011-03-31 10:07:01 +0200890 /*
891 * In case edge triggered interrupt must be used, we cannot iterate
892 * more than once without introducing race conditions with the hardirq.
893 */
894 if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
895 loopcount = 1;
896
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300897 mutex_lock(&wl->mutex);
898
899 wl1271_debug(DEBUG_IRQ, "IRQ work");
900
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200901 if (unlikely(wl->state == WL1271_STATE_OFF))
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300902 goto out;
903
Ido Yariva6208652011-03-01 15:14:41 +0200904 ret = wl1271_ps_elp_wakeup(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300905 if (ret < 0)
906 goto out;
907
Ido Yariva6208652011-03-01 15:14:41 +0200908 while (!done && loopcount--) {
909 /*
910 * In order to avoid a race with the hardirq, clear the flag
911 * before acknowledging the chip. Since the mutex is held,
912 * wl1271_ps_elp_wakeup cannot be called concurrently.
913 */
914 clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
915 smp_mb__after_clear_bit();
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200916
917 wl1271_fw_status(wl, wl->fw_status);
Eliad Pellerc8bde242011-02-02 09:59:35 +0200918 intr = le32_to_cpu(wl->fw_status->common.intr);
Ido Yariva6208652011-03-01 15:14:41 +0200919 intr &= WL1271_INTR_MASK;
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200920 if (!intr) {
Ido Yariva6208652011-03-01 15:14:41 +0200921 done = true;
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200922 continue;
923 }
924
Eliad Pellerccc83b02010-10-27 14:09:57 +0200925 if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) {
926 wl1271_error("watchdog interrupt received! "
927 "starting recovery.");
928 ieee80211_queue_work(wl->hw, &wl->recovery_work);
929
930 /* restarting the chip. ignore any other interrupt. */
931 goto out;
932 }
933
Ido Yariva6208652011-03-01 15:14:41 +0200934 if (likely(intr & WL1271_ACX_INTR_DATA)) {
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200935 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
936
Ido Yariv8aad2462011-03-01 15:14:38 +0200937 wl1271_rx(wl, &wl->fw_status->common);
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200938
Ido Yariva5225502010-10-12 14:49:10 +0200939 /* Check if any tx blocks were freed */
Ido Yarivb07d4032011-03-01 15:14:43 +0200940 spin_lock_irqsave(&wl->wl_lock, flags);
Ido Yariva5225502010-10-12 14:49:10 +0200941 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
Juuso Oikarinen6742f552010-12-13 09:52:37 +0200942 wl->tx_queue_count) {
Ido Yarivb07d4032011-03-01 15:14:43 +0200943 spin_unlock_irqrestore(&wl->wl_lock, flags);
Ido Yariva5225502010-10-12 14:49:10 +0200944 /*
945 * In order to avoid starvation of the TX path,
946 * call the work function directly.
947 */
948 wl1271_tx_work_locked(wl);
Ido Yarivb07d4032011-03-01 15:14:43 +0200949 } else {
950 spin_unlock_irqrestore(&wl->wl_lock, flags);
Ido Yariva5225502010-10-12 14:49:10 +0200951 }
952
Ido Yariv8aad2462011-03-01 15:14:38 +0200953 /* check for tx results */
954 if (wl->fw_status->common.tx_results_counter !=
955 (wl->tx_results_count & 0xff))
956 wl1271_tx_complete(wl);
Ido Yariva6208652011-03-01 15:14:41 +0200957
958 /* Make sure the deferred queues don't get too long */
959 defer_count = skb_queue_len(&wl->deferred_tx_queue) +
960 skb_queue_len(&wl->deferred_rx_queue);
961 if (defer_count > WL1271_DEFERRED_QUEUE_LIMIT)
962 wl1271_flush_deferred_work(wl);
Juuso Oikarinen1e73eb62010-02-22 08:38:37 +0200963 }
964
965 if (intr & WL1271_ACX_INTR_EVENT_A) {
966 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
967 wl1271_event_handle(wl, 0);
968 }
969
970 if (intr & WL1271_ACX_INTR_EVENT_B) {
971 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
972 wl1271_event_handle(wl, 1);
973 }
974
975 if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
976 wl1271_debug(DEBUG_IRQ,
977 "WL1271_ACX_INTR_INIT_COMPLETE");
978
979 if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
980 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300981 }
982
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300983 wl1271_ps_elp_sleep(wl);
984
985out:
Ido Yarivb07d4032011-03-01 15:14:43 +0200986 spin_lock_irqsave(&wl->wl_lock, flags);
987 /* In case TX was not handled here, queue TX work */
988 clear_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
989 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
990 wl->tx_queue_count)
991 ieee80211_queue_work(wl->hw, &wl->tx_work);
992 spin_unlock_irqrestore(&wl->wl_lock, flags);
993
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300994 mutex_unlock(&wl->mutex);
Ido Yariva6208652011-03-01 15:14:41 +0200995
996 return IRQ_HANDLED;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300997}
Ido Yariva6208652011-03-01 15:14:41 +0200998EXPORT_SYMBOL_GPL(wl1271_irq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300999
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001000static int wl1271_fetch_firmware(struct wl1271 *wl)
1001{
1002 const struct firmware *fw;
Arik Nemtsov166d5042010-10-16 21:44:57 +02001003 const char *fw_name;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001004 int ret;
1005
Arik Nemtsov166d5042010-10-16 21:44:57 +02001006 switch (wl->bss_type) {
1007 case BSS_TYPE_AP_BSS:
Arik Nemtsov1aed55f2011-03-06 16:32:18 +02001008 if (wl->chip.id == CHIP_ID_1283_PG20)
1009 fw_name = WL128X_AP_FW_NAME;
1010 else
1011 fw_name = WL127X_AP_FW_NAME;
Arik Nemtsov166d5042010-10-16 21:44:57 +02001012 break;
1013 case BSS_TYPE_IBSS:
1014 case BSS_TYPE_STA_BSS:
Shahar Levibc765bf2011-03-06 16:32:10 +02001015 if (wl->chip.id == CHIP_ID_1283_PG20)
1016 fw_name = WL128X_FW_NAME;
1017 else
1018 fw_name = WL1271_FW_NAME;
Arik Nemtsov166d5042010-10-16 21:44:57 +02001019 break;
1020 default:
1021 wl1271_error("no compatible firmware for bss_type %d",
1022 wl->bss_type);
1023 return -EINVAL;
1024 }
1025
1026 wl1271_debug(DEBUG_BOOT, "booting firmware %s", fw_name);
1027
1028 ret = request_firmware(&fw, fw_name, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001029
1030 if (ret < 0) {
1031 wl1271_error("could not get firmware: %d", ret);
1032 return ret;
1033 }
1034
1035 if (fw->size % 4) {
1036 wl1271_error("firmware size is not multiple of 32 bits: %zu",
1037 fw->size);
1038 ret = -EILSEQ;
1039 goto out;
1040 }
1041
Arik Nemtsov166d5042010-10-16 21:44:57 +02001042 vfree(wl->fw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001043 wl->fw_len = fw->size;
Juuso Oikarinen1fba4972009-10-08 21:56:32 +03001044 wl->fw = vmalloc(wl->fw_len);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001045
1046 if (!wl->fw) {
1047 wl1271_error("could not allocate memory for the firmware");
1048 ret = -ENOMEM;
1049 goto out;
1050 }
1051
1052 memcpy(wl->fw, fw->data, wl->fw_len);
Arik Nemtsov166d5042010-10-16 21:44:57 +02001053 wl->fw_bss_type = wl->bss_type;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001054 ret = 0;
1055
1056out:
1057 release_firmware(fw);
1058
1059 return ret;
1060}
1061
1062static int wl1271_fetch_nvs(struct wl1271 *wl)
1063{
1064 const struct firmware *fw;
1065 int ret;
1066
Shahar Levi5aa42342011-03-06 16:32:07 +02001067 ret = request_firmware(&fw, WL12XX_NVS_NAME, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001068
1069 if (ret < 0) {
1070 wl1271_error("could not get nvs file: %d", ret);
1071 return ret;
1072 }
1073
Shahar Levibc765bf2011-03-06 16:32:10 +02001074 wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001075
1076 if (!wl->nvs) {
1077 wl1271_error("could not allocate memory for the nvs file");
1078 ret = -ENOMEM;
1079 goto out;
1080 }
1081
Juuso Oikarinen02fabb02010-08-19 04:41:15 +02001082 wl->nvs_len = fw->size;
1083
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001084out:
1085 release_firmware(fw);
1086
1087 return ret;
1088}
1089
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001090static void wl1271_recovery_work(struct work_struct *work)
1091{
1092 struct wl1271 *wl =
1093 container_of(work, struct wl1271, recovery_work);
1094
1095 mutex_lock(&wl->mutex);
1096
1097 if (wl->state != WL1271_STATE_ON)
1098 goto out;
1099
Arik Nemtsov52dcaf52011-04-18 14:15:24 +03001100 wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x",
1101 wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4));
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001102
Juuso Oikarinend25611d2010-09-30 10:43:27 +02001103 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1104 ieee80211_connection_loss(wl->vif);
1105
Arik Nemtsov7dece1c2011-04-18 14:15:28 +03001106 /* Prevent spurious TX during FW restart */
1107 ieee80211_stop_queues(wl->hw);
1108
Luciano Coelho33c2c062011-05-10 14:46:02 +03001109 if (wl->sched_scanning) {
1110 ieee80211_sched_scan_stopped(wl->hw);
1111 wl->sched_scanning = false;
1112 }
1113
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001114 /* reboot the chipset */
Arik Nemtsov7dece1c2011-04-18 14:15:28 +03001115 __wl1271_op_remove_interface(wl, false);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001116 ieee80211_restart_hw(wl->hw);
1117
Arik Nemtsov7dece1c2011-04-18 14:15:28 +03001118 /*
1119 * Its safe to enable TX now - the queues are stopped after a request
1120 * to restart the HW.
1121 */
1122 ieee80211_wake_queues(wl->hw);
1123
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001124out:
1125 mutex_unlock(&wl->mutex);
1126}
1127
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001128static void wl1271_fw_wakeup(struct wl1271 *wl)
1129{
1130 u32 elp_reg;
1131
1132 elp_reg = ELPCTRL_WAKE_UP;
Juuso Oikarinen74621412009-10-12 15:08:54 +03001133 wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001134}
1135
1136static int wl1271_setup(struct wl1271 *wl)
1137{
1138 wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL);
1139 if (!wl->fw_status)
1140 return -ENOMEM;
1141
1142 wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
1143 if (!wl->tx_res_if) {
1144 kfree(wl->fw_status);
1145 return -ENOMEM;
1146 }
1147
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001148 return 0;
1149}
1150
1151static int wl1271_chip_wakeup(struct wl1271 *wl)
1152{
Juuso Oikarinen451de972009-10-12 15:08:46 +03001153 struct wl1271_partition_set partition;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001154 int ret = 0;
1155
Juuso Oikarinen01ac17e2009-12-11 15:41:02 +02001156 msleep(WL1271_PRE_POWER_ON_SLEEP);
Ohad Ben-Cohen2cc78ff2010-09-16 01:22:04 +02001157 ret = wl1271_power_on(wl);
1158 if (ret < 0)
1159 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001160 msleep(WL1271_POWER_ON_SLEEP);
Teemu Paasikivi9b280722010-02-18 13:25:56 +02001161 wl1271_io_reset(wl);
1162 wl1271_io_init(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001163
1164 /* We don't need a real memory partition here, because we only want
1165 * to use the registers at this point. */
Juuso Oikarinen451de972009-10-12 15:08:46 +03001166 memset(&partition, 0, sizeof(partition));
1167 partition.reg.start = REGISTERS_BASE;
1168 partition.reg.size = REGISTERS_DOWN_SIZE;
1169 wl1271_set_partition(wl, &partition);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001170
1171 /* ELP module wake up */
1172 wl1271_fw_wakeup(wl);
1173
1174 /* whal_FwCtrl_BootSm() */
1175
1176 /* 0. read chip id from CHIP_ID */
Teemu Paasikivi7b048c52010-02-18 13:25:55 +02001177 wl->chip.id = wl1271_read32(wl, CHIP_ID_B);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001178
1179 /* 1. check if chip id is valid */
1180
1181 switch (wl->chip.id) {
1182 case CHIP_ID_1271_PG10:
1183 wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
1184 wl->chip.id);
1185
1186 ret = wl1271_setup(wl);
1187 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001188 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001189 break;
1190 case CHIP_ID_1271_PG20:
1191 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
1192 wl->chip.id);
1193
Shahar Levi564f5952011-04-04 10:20:39 +03001194 /* end-of-transaction flag should be set in wl127x AP mode */
1195 if (wl->bss_type == BSS_TYPE_AP_BSS)
1196 wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION;
1197
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001198 ret = wl1271_setup(wl);
1199 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001200 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001201 break;
Shahar Levi0830cee2011-03-06 16:32:20 +02001202 case CHIP_ID_1283_PG20:
1203 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",
1204 wl->chip.id);
1205
1206 ret = wl1271_setup(wl);
1207 if (ret < 0)
1208 goto out;
Ido Yariv0da13da2011-03-31 10:06:58 +02001209 if (wl1271_set_block_size(wl))
1210 wl->quirks |= WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT;
Shahar Levi0830cee2011-03-06 16:32:20 +02001211 break;
1212 case CHIP_ID_1283_PG10:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001213 default:
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001214 wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001215 ret = -ENODEV;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001216 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001217 }
1218
Arik Nemtsov166d5042010-10-16 21:44:57 +02001219 /* Make sure the firmware type matches the BSS type */
1220 if (wl->fw == NULL || wl->fw_bss_type != wl->bss_type) {
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001221 ret = wl1271_fetch_firmware(wl);
1222 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001223 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001224 }
1225
1226 /* No NVS from netlink, try to get it from the filesystem */
1227 if (wl->nvs == NULL) {
1228 ret = wl1271_fetch_nvs(wl);
1229 if (ret < 0)
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001230 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001231 }
1232
1233out:
1234 return ret;
1235}
1236
Luciano Coelhoe7ddf542011-03-10 15:24:57 +02001237static unsigned int wl1271_get_fw_ver_quirks(struct wl1271 *wl)
1238{
1239 unsigned int quirks = 0;
1240 unsigned int *fw_ver = wl->chip.fw_ver;
1241
1242 /* Only for wl127x */
1243 if ((fw_ver[FW_VER_CHIP] == FW_VER_CHIP_WL127X) &&
1244 /* Check STA version */
1245 (((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) &&
1246 (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_STA_MIN)) ||
1247 /* Check AP version */
1248 ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) &&
1249 (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_AP_MIN))))
1250 quirks |= WL12XX_QUIRK_USE_2_SPARE_BLOCKS;
1251
1252 return quirks;
1253}
1254
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001255int wl1271_plt_start(struct wl1271 *wl)
1256{
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001257 int retries = WL1271_BOOT_RETRIES;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001258 int ret;
1259
1260 mutex_lock(&wl->mutex);
1261
1262 wl1271_notice("power up");
1263
1264 if (wl->state != WL1271_STATE_OFF) {
1265 wl1271_error("cannot go into PLT state because not "
1266 "in off state: %d", wl->state);
1267 ret = -EBUSY;
1268 goto out;
1269 }
1270
Arik Nemtsov166d5042010-10-16 21:44:57 +02001271 wl->bss_type = BSS_TYPE_STA_BSS;
1272
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001273 while (retries) {
1274 retries--;
1275 ret = wl1271_chip_wakeup(wl);
1276 if (ret < 0)
1277 goto power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001278
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001279 ret = wl1271_boot(wl);
1280 if (ret < 0)
1281 goto power_off;
1282
1283 ret = wl1271_plt_init(wl);
1284 if (ret < 0)
1285 goto irq_disable;
1286
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001287 wl->state = WL1271_STATE_PLT;
1288 wl1271_notice("firmware booted in PLT mode (%s)",
Levi, Shahar4b7fac72011-01-23 07:27:22 +01001289 wl->chip.fw_ver_str);
Luciano Coelhoe7ddf542011-03-10 15:24:57 +02001290
1291 /* Check if any quirks are needed with older fw versions */
1292 wl->quirks |= wl1271_get_fw_ver_quirks(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001293 goto out;
1294
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001295irq_disable:
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001296 mutex_unlock(&wl->mutex);
1297 /* Unlocking the mutex in the middle of handling is
1298 inherently unsafe. In this case we deem it safe to do,
1299 because we need to let any possibly pending IRQ out of
1300 the system (and while we are WL1271_STATE_OFF the IRQ
1301 work function will not do anything.) Also, any other
1302 possible concurrent operations will fail due to the
1303 current state, hence the wl1271 struct should be safe. */
Ido Yariva6208652011-03-01 15:14:41 +02001304 wl1271_disable_interrupts(wl);
1305 wl1271_flush_deferred_work(wl);
1306 cancel_work_sync(&wl->netstack_work);
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001307 mutex_lock(&wl->mutex);
1308power_off:
1309 wl1271_power_off(wl);
1310 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001311
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001312 wl1271_error("firmware boot in PLT mode failed despite %d retries",
1313 WL1271_BOOT_RETRIES);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001314out:
1315 mutex_unlock(&wl->mutex);
1316
1317 return ret;
1318}
1319
Luciano Coelho4623ec72011-03-21 19:26:41 +02001320static int __wl1271_plt_stop(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001321{
1322 int ret = 0;
1323
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001324 wl1271_notice("power down");
1325
1326 if (wl->state != WL1271_STATE_PLT) {
1327 wl1271_error("cannot power down because not in PLT "
1328 "state: %d", wl->state);
1329 ret = -EBUSY;
1330 goto out;
1331 }
1332
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001333 wl1271_power_off(wl);
1334
1335 wl->state = WL1271_STATE_OFF;
Luciano Coelhobd5ea182009-10-13 12:47:58 +03001336 wl->rx_counter = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001337
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001338 mutex_unlock(&wl->mutex);
Ido Yariva6208652011-03-01 15:14:41 +02001339 wl1271_disable_interrupts(wl);
1340 wl1271_flush_deferred_work(wl);
1341 cancel_work_sync(&wl->netstack_work);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001342 cancel_work_sync(&wl->recovery_work);
Juuso Oikarinen4ae3fa82011-01-14 12:48:46 +01001343 mutex_lock(&wl->mutex);
1344out:
1345 return ret;
1346}
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +02001347
Juuso Oikarinen4ae3fa82011-01-14 12:48:46 +01001348int wl1271_plt_stop(struct wl1271 *wl)
1349{
1350 int ret;
1351
1352 mutex_lock(&wl->mutex);
1353 ret = __wl1271_plt_stop(wl);
1354 mutex_unlock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001355 return ret;
1356}
1357
Johannes Berg7bb45682011-02-24 14:42:06 +01001358static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001359{
1360 struct wl1271 *wl = hw->priv;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001361 unsigned long flags;
Juuso Oikarinen6742f552010-12-13 09:52:37 +02001362 int q;
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02001363 u8 hlid = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001364
Ido Yarivb07d4032011-03-01 15:14:43 +02001365 q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
1366
1367 if (wl->bss_type == BSS_TYPE_AP_BSS)
1368 hlid = wl1271_tx_get_hlid(skb);
1369
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001370 spin_lock_irqsave(&wl->wl_lock, flags);
Ido Yarivb07d4032011-03-01 15:14:43 +02001371
Juuso Oikarinen6742f552010-12-13 09:52:37 +02001372 wl->tx_queue_count++;
Arik Nemtsovf4d08dd2011-02-23 00:22:24 +02001373
1374 /*
1375 * The workqueue is slow to process the tx_queue and we need stop
1376 * the queue here, otherwise the queue will get too long.
1377 */
1378 if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
1379 wl1271_debug(DEBUG_TX, "op_tx: stopping queues");
1380 ieee80211_stop_queues(wl->hw);
1381 set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
1382 }
1383
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001384 /* queue the packet */
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02001385 if (wl->bss_type == BSS_TYPE_AP_BSS) {
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02001386 wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q);
1387 skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
1388 } else {
1389 skb_queue_tail(&wl->tx_queue[q], skb);
1390 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001391
1392 /*
1393 * The chip specific setup must run before the first TX packet -
1394 * before that, the tx_work will not be initialized!
1395 */
1396
Ido Yarivb07d4032011-03-01 15:14:43 +02001397 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
1398 !test_bit(WL1271_FLAG_TX_PENDING, &wl->flags))
Ido Yariva5225502010-10-12 14:49:10 +02001399 ieee80211_queue_work(wl->hw, &wl->tx_work);
Ido Yarivb07d4032011-03-01 15:14:43 +02001400
1401 spin_unlock_irqrestore(&wl->wl_lock, flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001402}
1403
Shahar Leviae47c452011-03-06 16:32:14 +02001404int wl1271_tx_dummy_packet(struct wl1271 *wl)
1405{
Ido Yariv990f5de2011-03-31 10:06:59 +02001406 unsigned long flags;
Shahar Leviae47c452011-03-06 16:32:14 +02001407
Ido Yariv990f5de2011-03-31 10:06:59 +02001408 spin_lock_irqsave(&wl->wl_lock, flags);
1409 set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
1410 wl->tx_queue_count++;
1411 spin_unlock_irqrestore(&wl->wl_lock, flags);
1412
1413 /* The FW is low on RX memory blocks, so send the dummy packet asap */
1414 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
1415 wl1271_tx_work_locked(wl);
1416
1417 /*
1418 * If the FW TX is busy, TX work will be scheduled by the threaded
1419 * interrupt handler function
1420 */
1421 return 0;
1422}
1423
1424/*
1425 * The size of the dummy packet should be at least 1400 bytes. However, in
1426 * order to minimize the number of bus transactions, aligning it to 512 bytes
1427 * boundaries could be beneficial, performance wise
1428 */
1429#define TOTAL_TX_DUMMY_PACKET_SIZE (ALIGN(1400, 512))
1430
Luciano Coelhocf27d862011-04-01 21:08:23 +03001431static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
Ido Yariv990f5de2011-03-31 10:06:59 +02001432{
1433 struct sk_buff *skb;
1434 struct ieee80211_hdr_3addr *hdr;
1435 unsigned int dummy_packet_size;
1436
1437 dummy_packet_size = TOTAL_TX_DUMMY_PACKET_SIZE -
1438 sizeof(struct wl1271_tx_hw_descr) - sizeof(*hdr);
1439
1440 skb = dev_alloc_skb(TOTAL_TX_DUMMY_PACKET_SIZE);
Shahar Leviae47c452011-03-06 16:32:14 +02001441 if (!skb) {
Ido Yariv990f5de2011-03-31 10:06:59 +02001442 wl1271_warning("Failed to allocate a dummy packet skb");
1443 return NULL;
Shahar Leviae47c452011-03-06 16:32:14 +02001444 }
1445
1446 skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr));
1447
1448 hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr));
1449 memset(hdr, 0, sizeof(*hdr));
1450 hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
Ido Yariv990f5de2011-03-31 10:06:59 +02001451 IEEE80211_STYPE_NULLFUNC |
1452 IEEE80211_FCTL_TODS);
Shahar Leviae47c452011-03-06 16:32:14 +02001453
Ido Yariv990f5de2011-03-31 10:06:59 +02001454 memset(skb_put(skb, dummy_packet_size), 0, dummy_packet_size);
Shahar Leviae47c452011-03-06 16:32:14 +02001455
Luciano Coelho18b92ff2011-03-21 16:35:21 +02001456 /* Dummy packets require the TID to be management */
1457 skb->priority = WL1271_TID_MGMT;
Ido Yariv990f5de2011-03-31 10:06:59 +02001458
1459 /* Initialize all fields that might be used */
Hauke Mehrtens86c438f2011-04-26 23:27:44 +02001460 skb_set_queue_mapping(skb, 0);
Ido Yariv990f5de2011-03-31 10:06:59 +02001461 memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info));
Shahar Leviae47c452011-03-06 16:32:14 +02001462
Ido Yariv990f5de2011-03-31 10:06:59 +02001463 return skb;
Shahar Leviae47c452011-03-06 16:32:14 +02001464}
1465
Ido Yariv990f5de2011-03-31 10:06:59 +02001466
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03001467static struct notifier_block wl1271_dev_notifier = {
1468 .notifier_call = wl1271_dev_notify,
1469};
1470
Luciano Coelhof634a4e2011-05-18 16:51:26 -04001471#ifdef CONFIG_PM
Eliad Peller94390642011-05-13 11:57:13 +03001472static int wl1271_configure_suspend(struct wl1271 *wl)
1473{
1474 int ret;
1475
1476 if (wl->bss_type != BSS_TYPE_STA_BSS)
1477 return 0;
1478
1479 mutex_lock(&wl->mutex);
1480
1481 ret = wl1271_ps_elp_wakeup(wl);
1482 if (ret < 0)
1483 goto out_unlock;
1484
1485 /* enter psm if needed*/
1486 if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) {
1487 DECLARE_COMPLETION_ONSTACK(compl);
1488
1489 wl->ps_compl = &compl;
1490 ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
1491 wl->basic_rate, true);
1492 if (ret < 0)
1493 goto out_sleep;
1494
1495 /* we must unlock here so we will be able to get events */
1496 wl1271_ps_elp_sleep(wl);
1497 mutex_unlock(&wl->mutex);
1498
1499 ret = wait_for_completion_timeout(
1500 &compl, msecs_to_jiffies(WL1271_PS_COMPLETE_TIMEOUT));
1501 if (ret <= 0) {
1502 wl1271_warning("couldn't enter ps mode!");
1503 ret = -EBUSY;
1504 goto out;
1505 }
1506
1507 /* take mutex again, and wakeup */
1508 mutex_lock(&wl->mutex);
1509
1510 ret = wl1271_ps_elp_wakeup(wl);
1511 if (ret < 0)
1512 goto out_unlock;
1513 }
1514out_sleep:
1515 wl1271_ps_elp_sleep(wl);
1516out_unlock:
1517 mutex_unlock(&wl->mutex);
1518out:
1519 return ret;
1520
1521}
1522
1523static void wl1271_configure_resume(struct wl1271 *wl)
1524{
1525 int ret;
1526
1527 if (wl->bss_type != BSS_TYPE_STA_BSS)
1528 return;
1529
1530 mutex_lock(&wl->mutex);
1531 ret = wl1271_ps_elp_wakeup(wl);
1532 if (ret < 0)
1533 goto out;
1534
1535 /* exit psm if it wasn't configured */
1536 if (!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags))
1537 wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
1538 wl->basic_rate, true);
1539
1540 wl1271_ps_elp_sleep(wl);
1541out:
1542 mutex_unlock(&wl->mutex);
1543}
1544
Eliad Peller402e48612011-05-13 11:57:09 +03001545static int wl1271_op_suspend(struct ieee80211_hw *hw,
1546 struct cfg80211_wowlan *wow)
1547{
1548 struct wl1271 *wl = hw->priv;
1549 wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
1550 wl->wow_enabled = !!wow;
Eliad Pellerf44e5862011-05-13 11:57:11 +03001551 if (wl->wow_enabled) {
Eliad Peller94390642011-05-13 11:57:13 +03001552 int ret;
1553 ret = wl1271_configure_suspend(wl);
1554 if (ret < 0) {
1555 wl1271_warning("couldn't prepare device to suspend");
1556 return ret;
1557 }
Eliad Pellerf44e5862011-05-13 11:57:11 +03001558 /* flush any remaining work */
1559 wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
1560 flush_delayed_work(&wl->scan_complete_work);
1561
1562 /*
1563 * disable and re-enable interrupts in order to flush
1564 * the threaded_irq
1565 */
1566 wl1271_disable_interrupts(wl);
1567
1568 /*
1569 * set suspended flag to avoid triggering a new threaded_irq
1570 * work. no need for spinlock as interrupts are disabled.
1571 */
1572 set_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
1573
1574 wl1271_enable_interrupts(wl);
1575 flush_work(&wl->tx_work);
1576 flush_delayed_work(&wl->pspoll_work);
1577 flush_delayed_work(&wl->elp_work);
1578 }
Eliad Peller402e48612011-05-13 11:57:09 +03001579 return 0;
1580}
1581
1582static int wl1271_op_resume(struct ieee80211_hw *hw)
1583{
1584 struct wl1271 *wl = hw->priv;
1585 wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d",
1586 wl->wow_enabled);
Eliad Pellerf44e5862011-05-13 11:57:11 +03001587
1588 /*
1589 * re-enable irq_work enqueuing, and call irq_work directly if
1590 * there is a pending work.
1591 */
1592 if (wl->wow_enabled) {
1593 struct wl1271 *wl = hw->priv;
1594 unsigned long flags;
1595 bool run_irq_work = false;
1596
1597 spin_lock_irqsave(&wl->wl_lock, flags);
1598 clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
1599 if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags))
1600 run_irq_work = true;
1601 spin_unlock_irqrestore(&wl->wl_lock, flags);
1602
1603 if (run_irq_work) {
1604 wl1271_debug(DEBUG_MAC80211,
1605 "run postponed irq_work directly");
1606 wl1271_irq(0, wl);
1607 wl1271_enable_interrupts(wl);
1608 }
Eliad Peller94390642011-05-13 11:57:13 +03001609
1610 wl1271_configure_resume(wl);
Eliad Pellerf44e5862011-05-13 11:57:11 +03001611 }
1612
Eliad Peller402e48612011-05-13 11:57:09 +03001613 return 0;
1614}
Luciano Coelhof634a4e2011-05-18 16:51:26 -04001615#endif
Eliad Peller402e48612011-05-13 11:57:09 +03001616
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001617static int wl1271_op_start(struct ieee80211_hw *hw)
1618{
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001619 wl1271_debug(DEBUG_MAC80211, "mac80211 start");
1620
1621 /*
1622 * We have to delay the booting of the hardware because
1623 * we need to know the local MAC address before downloading and
1624 * initializing the firmware. The MAC address cannot be changed
1625 * after boot, and without the proper MAC address, the firmware
1626 * will not function properly.
1627 *
1628 * The MAC address is first known when the corresponding interface
1629 * is added. That is where we will initialize the hardware.
Arik Nemtsov166d5042010-10-16 21:44:57 +02001630 *
1631 * In addition, we currently have different firmwares for AP and managed
1632 * operation. We will know which to boot according to interface type.
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001633 */
1634
1635 return 0;
1636}
1637
1638static void wl1271_op_stop(struct ieee80211_hw *hw)
1639{
1640 wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
1641}
1642
1643static int wl1271_op_add_interface(struct ieee80211_hw *hw,
1644 struct ieee80211_vif *vif)
1645{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001646 struct wl1271 *wl = hw->priv;
John W. Linvilleac01e942010-07-28 17:09:41 -04001647 struct wiphy *wiphy = hw->wiphy;
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001648 int retries = WL1271_BOOT_RETRIES;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001649 int ret = 0;
Eliad Peller71125ab2010-10-28 21:46:43 +02001650 bool booted = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001651
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001652 wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
1653 vif->type, vif->addr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001654
1655 mutex_lock(&wl->mutex);
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001656 if (wl->vif) {
Eliad Peller71125ab2010-10-28 21:46:43 +02001657 wl1271_debug(DEBUG_MAC80211,
1658 "multiple vifs are not supported yet");
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001659 ret = -EBUSY;
1660 goto out;
1661 }
1662
Juuso Oikarinen13026de2011-03-29 16:43:50 +03001663 /*
1664 * in some very corner case HW recovery scenarios its possible to
1665 * get here before __wl1271_op_remove_interface is complete, so
1666 * opt out if that is the case.
1667 */
1668 if (test_bit(WL1271_FLAG_IF_INITIALIZED, &wl->flags)) {
1669 ret = -EBUSY;
1670 goto out;
1671 }
1672
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001673 switch (vif->type) {
1674 case NL80211_IFTYPE_STATION:
1675 wl->bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001676 wl->set_bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001677 break;
1678 case NL80211_IFTYPE_ADHOC:
1679 wl->bss_type = BSS_TYPE_IBSS;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001680 wl->set_bss_type = BSS_TYPE_STA_BSS;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001681 break;
Arik Nemtsov038d9252010-10-16 21:53:24 +02001682 case NL80211_IFTYPE_AP:
1683 wl->bss_type = BSS_TYPE_AP_BSS;
1684 break;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001685 default:
1686 ret = -EOPNOTSUPP;
1687 goto out;
1688 }
1689
1690 memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001691
1692 if (wl->state != WL1271_STATE_OFF) {
1693 wl1271_error("cannot start because not in off state: %d",
1694 wl->state);
1695 ret = -EBUSY;
1696 goto out;
1697 }
1698
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001699 while (retries) {
1700 retries--;
1701 ret = wl1271_chip_wakeup(wl);
1702 if (ret < 0)
1703 goto power_off;
1704
1705 ret = wl1271_boot(wl);
1706 if (ret < 0)
1707 goto power_off;
1708
1709 ret = wl1271_hw_init(wl);
1710 if (ret < 0)
1711 goto irq_disable;
1712
Eliad Peller71125ab2010-10-28 21:46:43 +02001713 booted = true;
1714 break;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001715
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001716irq_disable:
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001717 mutex_unlock(&wl->mutex);
1718 /* Unlocking the mutex in the middle of handling is
1719 inherently unsafe. In this case we deem it safe to do,
1720 because we need to let any possibly pending IRQ out of
1721 the system (and while we are WL1271_STATE_OFF the IRQ
1722 work function will not do anything.) Also, any other
1723 possible concurrent operations will fail due to the
1724 current state, hence the wl1271 struct should be safe. */
Ido Yariva6208652011-03-01 15:14:41 +02001725 wl1271_disable_interrupts(wl);
1726 wl1271_flush_deferred_work(wl);
1727 cancel_work_sync(&wl->netstack_work);
Juuso Oikarinen9ccd9212009-12-11 15:41:01 +02001728 mutex_lock(&wl->mutex);
1729power_off:
1730 wl1271_power_off(wl);
1731 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001732
Eliad Peller71125ab2010-10-28 21:46:43 +02001733 if (!booted) {
1734 wl1271_error("firmware boot failed despite %d retries",
1735 WL1271_BOOT_RETRIES);
1736 goto out;
1737 }
1738
1739 wl->vif = vif;
1740 wl->state = WL1271_STATE_ON;
Juuso Oikarinen13026de2011-03-29 16:43:50 +03001741 set_bit(WL1271_FLAG_IF_INITIALIZED, &wl->flags);
Levi, Shahar4b7fac72011-01-23 07:27:22 +01001742 wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str);
Eliad Peller71125ab2010-10-28 21:46:43 +02001743
1744 /* update hw/fw version info in wiphy struct */
1745 wiphy->hw_version = wl->chip.id;
Levi, Shahar4b7fac72011-01-23 07:27:22 +01001746 strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
Eliad Peller71125ab2010-10-28 21:46:43 +02001747 sizeof(wiphy->fw_version));
1748
Luciano Coelhoe7ddf542011-03-10 15:24:57 +02001749 /* Check if any quirks are needed with older fw versions */
1750 wl->quirks |= wl1271_get_fw_ver_quirks(wl);
1751
Luciano Coelhofb6a6812010-12-03 17:05:40 +02001752 /*
1753 * Now we know if 11a is supported (info from the NVS), so disable
1754 * 11a channels if not supported
1755 */
1756 if (!wl->enable_11a)
1757 wiphy->bands[IEEE80211_BAND_5GHZ]->n_channels = 0;
1758
1759 wl1271_debug(DEBUG_MAC80211, "11a is %ssupported",
1760 wl->enable_11a ? "" : "not ");
1761
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +03001762out:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001763 mutex_unlock(&wl->mutex);
1764
Juuso Oikarinenf9f774c2011-03-21 10:43:36 +02001765 mutex_lock(&wl_list_mutex);
Juuso Oikarineneb887df2010-07-08 17:49:58 +03001766 if (!ret)
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001767 list_add(&wl->list, &wl_list);
Juuso Oikarinenf9f774c2011-03-21 10:43:36 +02001768 mutex_unlock(&wl_list_mutex);
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001769
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001770 return ret;
1771}
1772
Arik Nemtsov7dece1c2011-04-18 14:15:28 +03001773static void __wl1271_op_remove_interface(struct wl1271 *wl,
1774 bool reset_tx_queues)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001775{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001776 int i;
1777
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001778 wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001779
Juuso Oikarinen13026de2011-03-29 16:43:50 +03001780 /* because of hardware recovery, we may get here twice */
1781 if (wl->state != WL1271_STATE_ON)
1782 return;
1783
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001784 wl1271_info("down");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001785
Juuso Oikarinenf9f774c2011-03-21 10:43:36 +02001786 mutex_lock(&wl_list_mutex);
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001787 list_del(&wl->list);
Juuso Oikarinenf9f774c2011-03-21 10:43:36 +02001788 mutex_unlock(&wl_list_mutex);
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001789
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001790 /* enable dyn ps just in case (if left on due to fw crash etc) */
Juuso Oikarinen9a547bf2010-07-08 17:50:04 +03001791 if (wl->bss_type == BSS_TYPE_STA_BSS)
Juuso Oikarinenf532be62010-07-08 17:50:05 +03001792 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03001793
Luciano Coelho08688d62010-07-08 17:50:07 +03001794 if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
Luciano Coelho08688d62010-07-08 17:50:07 +03001795 wl->scan.state = WL1271_SCAN_STATE_IDLE;
Luciano Coelho4a31c112011-03-21 23:16:14 +02001796 memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
Juuso Oikarinenb739a422010-10-26 13:24:38 +02001797 wl->scan.req = NULL;
Juuso Oikarinen76a029f2010-07-29 04:54:45 +03001798 ieee80211_scan_completed(wl->hw, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001799 }
1800
Juuso Oikarinen13026de2011-03-29 16:43:50 +03001801 /*
1802 * this must be before the cancel_work calls below, so that the work
1803 * functions don't perform further work.
1804 */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001805 wl->state = WL1271_STATE_OFF;
1806
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001807 mutex_unlock(&wl->mutex);
1808
Ido Yariva6208652011-03-01 15:14:41 +02001809 wl1271_disable_interrupts(wl);
1810 wl1271_flush_deferred_work(wl);
Juuso Oikarinen78abd322010-09-21 06:23:32 +02001811 cancel_delayed_work_sync(&wl->scan_complete_work);
Ido Yariva6208652011-03-01 15:14:41 +02001812 cancel_work_sync(&wl->netstack_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001813 cancel_work_sync(&wl->tx_work);
Eliad Peller77ddaa12011-05-15 11:10:29 +03001814 del_timer_sync(&wl->rx_streaming_timer);
1815 cancel_work_sync(&wl->rx_streaming_enable_work);
1816 cancel_work_sync(&wl->rx_streaming_disable_work);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03001817 cancel_delayed_work_sync(&wl->pspoll_work);
Juuso Oikarinen8c7f4f32010-09-21 06:23:29 +02001818 cancel_delayed_work_sync(&wl->elp_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001819
1820 mutex_lock(&wl->mutex);
1821
1822 /* let's notify MAC80211 about the remaining pending TX frames */
Arik Nemtsov7dece1c2011-04-18 14:15:28 +03001823 wl1271_tx_reset(wl, reset_tx_queues);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001824 wl1271_power_off(wl);
1825
1826 memset(wl->bssid, 0, ETH_ALEN);
1827 memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1);
1828 wl->ssid_len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001829 wl->bss_type = MAX_BSS_TYPE;
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001830 wl->set_bss_type = MAX_BSS_TYPE;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001831 wl->band = IEEE80211_BAND_2GHZ;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001832
1833 wl->rx_counter = 0;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02001834 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001835 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
1836 wl->tx_blocks_available = 0;
Ido Yarivd2f4d472011-03-31 10:07:00 +02001837 wl->tx_allocated_blocks = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001838 wl->tx_results_count = 0;
1839 wl->tx_packets_count = 0;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001840 wl->tx_security_last_seq = 0;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02001841 wl->tx_security_seq = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001842 wl->time_offset = 0;
1843 wl->session_counter = 0;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02001844 wl->rate_set = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinen1b72aec2010-03-18 12:26:39 +02001845 wl->vif = NULL;
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001846 wl->filters = 0;
Arik Nemtsov7f179b42010-10-16 21:39:06 +02001847 wl1271_free_ap_keys(wl);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02001848 memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));
Arik Nemtsovb622d992011-02-23 00:22:31 +02001849 wl->ap_fw_ps_map = 0;
1850 wl->ap_ps_map = 0;
Luciano Coelho33c2c062011-05-10 14:46:02 +03001851 wl->sched_scanning = false;
Luciano Coelhod6e19d132009-10-12 15:08:43 +03001852
Juuso Oikarinen13026de2011-03-29 16:43:50 +03001853 /*
1854 * this is performed after the cancel_work calls and the associated
1855 * mutex_lock, so that wl1271_op_add_interface does not accidentally
1856 * get executed before all these vars have been reset.
1857 */
1858 wl->flags = 0;
1859
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001860 for (i = 0; i < NUM_TX_QUEUES; i++)
1861 wl->tx_blocks_freed[i] = 0;
1862
1863 wl1271_debugfs_reset(wl);
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03001864
1865 kfree(wl->fw_status);
1866 wl->fw_status = NULL;
1867 kfree(wl->tx_res_if);
1868 wl->tx_res_if = NULL;
1869 kfree(wl->target_mem_map);
1870 wl->target_mem_map = NULL;
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001871}
Juuso Oikarinenbd9dc492010-04-09 11:07:26 +03001872
Juuso Oikarinen52a2a372010-09-21 06:23:30 +02001873static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
1874 struct ieee80211_vif *vif)
1875{
1876 struct wl1271 *wl = hw->priv;
1877
1878 mutex_lock(&wl->mutex);
Juuso Oikarinen67353292010-11-18 15:19:02 +02001879 /*
1880 * wl->vif can be null here if someone shuts down the interface
1881 * just when hardware recovery has been started.
1882 */
1883 if (wl->vif) {
1884 WARN_ON(wl->vif != vif);
Arik Nemtsov7dece1c2011-04-18 14:15:28 +03001885 __wl1271_op_remove_interface(wl, true);
Juuso Oikarinen67353292010-11-18 15:19:02 +02001886 }
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001887
Juuso Oikarinen67353292010-11-18 15:19:02 +02001888 mutex_unlock(&wl->mutex);
Juuso Oikarinen52b0e7a2010-09-21 06:23:31 +02001889 cancel_work_sync(&wl->recovery_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001890}
1891
Ohad Ben-Cohenc5745182011-03-30 16:35:59 +02001892void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001893{
Arik Nemtsovae113b52010-10-16 18:45:07 +02001894 wl1271_set_default_filters(wl);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001895
1896 /* combine requested filters with current filter config */
1897 filters = wl->filters | filters;
1898
1899 wl1271_debug(DEBUG_FILTERS, "RX filters set: ");
1900
1901 if (filters & FIF_PROMISC_IN_BSS) {
1902 wl1271_debug(DEBUG_FILTERS, " - FIF_PROMISC_IN_BSS");
1903 wl->rx_config &= ~CFG_UNI_FILTER_EN;
1904 wl->rx_config |= CFG_BSSID_FILTER_EN;
1905 }
1906 if (filters & FIF_BCN_PRBRESP_PROMISC) {
1907 wl1271_debug(DEBUG_FILTERS, " - FIF_BCN_PRBRESP_PROMISC");
1908 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1909 wl->rx_config &= ~CFG_SSID_FILTER_EN;
1910 }
1911 if (filters & FIF_OTHER_BSS) {
1912 wl1271_debug(DEBUG_FILTERS, " - FIF_OTHER_BSS");
1913 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1914 }
1915 if (filters & FIF_CONTROL) {
1916 wl1271_debug(DEBUG_FILTERS, " - FIF_CONTROL");
1917 wl->rx_filter |= CFG_RX_CTL_EN;
1918 }
1919 if (filters & FIF_FCSFAIL) {
1920 wl1271_debug(DEBUG_FILTERS, " - FIF_FCSFAIL");
1921 wl->rx_filter |= CFG_RX_FCS_ERROR;
1922 }
1923}
1924
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001925static int wl1271_dummy_join(struct wl1271 *wl)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001926{
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02001927 int ret = 0;
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001928 /* we need to use a dummy BSSID for now */
1929 static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
1930 0xad, 0xbe, 0xef };
1931
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001932 memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
1933
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02001934 /* pass through frames from all BSS */
1935 wl1271_configure_filters(wl, FIF_OTHER_BSS);
1936
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02001937 ret = wl1271_cmd_join(wl, wl->set_bss_type);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001938 if (ret < 0)
1939 goto out;
1940
Juuso Oikarinen71449f82009-12-11 15:41:07 +02001941 set_bit(WL1271_FLAG_JOINED, &wl->flags);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001942
1943out:
1944 return ret;
1945}
1946
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001947static int wl1271_join(struct wl1271 *wl, bool set_assoc)
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001948{
1949 int ret;
1950
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001951 /*
1952 * One of the side effects of the JOIN command is that is clears
1953 * WPA/WPA2 keys from the chipset. Performing a JOIN while associated
1954 * to a WPA/WPA2 access point will therefore kill the data-path.
Ohad Ben-Cohen8bf69aa2011-03-30 19:18:31 +02001955 * Currently the only valid scenario for JOIN during association
1956 * is on roaming, in which case we will also be given new keys.
1957 * Keep the below message for now, unless it starts bothering
1958 * users who really like to roam a lot :)
Juuso Oikarinen69e54342010-05-07 11:39:00 +03001959 */
1960 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1961 wl1271_info("JOIN while associated.");
1962
1963 if (set_assoc)
1964 set_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
1965
Juuso Oikarinen82429d32010-04-28 09:50:01 +03001966 ret = wl1271_cmd_join(wl, wl->set_bss_type);
1967 if (ret < 0)
1968 goto out;
1969
1970 set_bit(WL1271_FLAG_JOINED, &wl->flags);
1971
1972 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
1973 goto out;
1974
1975 /*
1976 * The join command disable the keep-alive mode, shut down its process,
1977 * and also clear the template config, so we need to reset it all after
1978 * the join. The acx_aid starts the keep-alive process, and the order
1979 * of the commands below is relevant.
1980 */
1981 ret = wl1271_acx_keep_alive_mode(wl, true);
1982 if (ret < 0)
1983 goto out;
1984
1985 ret = wl1271_acx_aid(wl, wl->aid);
1986 if (ret < 0)
1987 goto out;
1988
1989 ret = wl1271_cmd_build_klv_null_data(wl);
1990 if (ret < 0)
1991 goto out;
1992
1993 ret = wl1271_acx_keep_alive_config(wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
1994 ACX_KEEP_ALIVE_TPL_VALID);
1995 if (ret < 0)
1996 goto out;
1997
1998out:
1999 return ret;
2000}
2001
2002static int wl1271_unjoin(struct wl1271 *wl)
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02002003{
2004 int ret;
2005
2006 /* to stop listening to a channel, we disconnect */
2007 ret = wl1271_cmd_disconnect(wl);
2008 if (ret < 0)
2009 goto out;
2010
Juuso Oikarinen71449f82009-12-11 15:41:07 +02002011 clear_bit(WL1271_FLAG_JOINED, &wl->flags);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02002012 memset(wl->bssid, 0, ETH_ALEN);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02002013
Ohad Ben-Cohenc5745182011-03-30 16:35:59 +02002014 /* stop filtering packets based on bssid */
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02002015 wl1271_configure_filters(wl, FIF_OTHER_BSS);
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02002016
2017out:
2018 return ret;
2019}
2020
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002021static void wl1271_set_band_rate(struct wl1271 *wl)
2022{
2023 if (wl->band == IEEE80211_BAND_2GHZ)
2024 wl->basic_rate_set = wl->conf.tx.basic_rate;
2025 else
2026 wl->basic_rate_set = wl->conf.tx.basic_rate_5;
2027}
2028
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002029static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle)
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002030{
2031 int ret;
2032
2033 if (idle) {
2034 if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
2035 ret = wl1271_unjoin(wl);
2036 if (ret < 0)
2037 goto out;
2038 }
Arik Nemtsove0fe3712010-10-16 18:19:53 +02002039 wl->rate_set = wl1271_tx_min_rate_get(wl);
Arik Nemtsov79b223f2010-10-16 17:52:59 +02002040 ret = wl1271_acx_sta_rate_policies(wl);
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002041 if (ret < 0)
2042 goto out;
2043 ret = wl1271_acx_keep_alive_config(
2044 wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
2045 ACX_KEEP_ALIVE_TPL_INVALID);
2046 if (ret < 0)
2047 goto out;
2048 set_bit(WL1271_FLAG_IDLE, &wl->flags);
2049 } else {
2050 /* increment the session counter */
2051 wl->session_counter++;
2052 if (wl->session_counter >= SESSION_COUNTER_MAX)
2053 wl->session_counter = 0;
Luciano Coelho33c2c062011-05-10 14:46:02 +03002054
2055 /* The current firmware only supports sched_scan in idle */
2056 if (wl->sched_scanning) {
2057 wl1271_scan_sched_scan_stop(wl);
2058 ieee80211_sched_scan_stopped(wl->hw);
2059 }
2060
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002061 ret = wl1271_dummy_join(wl);
2062 if (ret < 0)
2063 goto out;
2064 clear_bit(WL1271_FLAG_IDLE, &wl->flags);
2065 }
2066
2067out:
2068 return ret;
2069}
2070
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002071static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
2072{
2073 struct wl1271 *wl = hw->priv;
2074 struct ieee80211_conf *conf = &hw->conf;
2075 int channel, ret = 0;
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002076 bool is_ap;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002077
2078 channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
2079
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002080 wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s"
2081 " changed 0x%x",
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002082 channel,
2083 conf->flags & IEEE80211_CONF_PS ? "on" : "off",
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02002084 conf->power_level,
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002085 conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
2086 changed);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002087
Juuso Oikarinen781608c2010-05-24 11:18:17 +03002088 /*
2089 * mac80211 will go to idle nearly immediately after transmitting some
2090 * frames, such as the deauth. To make sure those frames reach the air,
2091 * wait here until the TX queue is fully flushed.
2092 */
2093 if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
2094 (conf->flags & IEEE80211_CONF_IDLE))
2095 wl1271_tx_flush(wl);
2096
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002097 mutex_lock(&wl->mutex);
2098
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002099 if (unlikely(wl->state == WL1271_STATE_OFF)) {
Arik Nemtsov17e672d2011-03-22 10:07:47 +02002100 /* we support configuring the channel and band while off */
2101 if ((changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
2102 wl->band = conf->channel->band;
2103 wl->channel = channel;
2104 }
2105
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03002106 goto out;
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002107 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03002108
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002109 is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
2110
Ido Yariva6208652011-03-01 15:14:41 +02002111 ret = wl1271_ps_elp_wakeup(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002112 if (ret < 0)
2113 goto out;
2114
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002115 /* if the channel changes while joined, join again */
Juuso Oikarinen69e54342010-05-07 11:39:00 +03002116 if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
2117 ((wl->band != conf->channel->band) ||
2118 (wl->channel != channel))) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002119 wl->band = conf->channel->band;
2120 wl->channel = channel;
2121
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002122 if (!is_ap) {
2123 /*
2124 * FIXME: the mac80211 should really provide a fixed
2125 * rate to use here. for now, just use the smallest
2126 * possible rate for the band as a fixed rate for
2127 * association frames and other control messages.
2128 */
2129 if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
2130 wl1271_set_band_rate(wl);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002131
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002132 wl->basic_rate = wl1271_tx_min_rate_get(wl);
2133 ret = wl1271_acx_sta_rate_policies(wl);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002134 if (ret < 0)
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002135 wl1271_warning("rate policy for channel "
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002136 "failed %d", ret);
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002137
2138 if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
2139 ret = wl1271_join(wl, false);
2140 if (ret < 0)
2141 wl1271_warning("cmd join on channel "
2142 "failed %d", ret);
2143 }
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03002144 }
2145 }
2146
Arik Nemtsovbee0ffe2010-10-16 19:17:02 +02002147 if (changed & IEEE80211_CONF_CHANGE_IDLE && !is_ap) {
2148 ret = wl1271_sta_handle_idle(wl,
2149 conf->flags & IEEE80211_CONF_IDLE);
Juuso Oikarinen0d58cbf2010-05-24 11:18:16 +03002150 if (ret < 0)
2151 wl1271_warning("idle mode change failed %d", ret);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002152 }
2153
Juuso Oikarinen90494a92010-07-08 17:50:00 +03002154 /*
2155 * if mac80211 changes the PSM mode, make sure the mode is not
2156 * incorrectly changed after the pspoll failure active window.
2157 */
2158 if (changed & IEEE80211_CONF_CHANGE_PS)
2159 clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
2160
Juuso Oikarinen71449f82009-12-11 15:41:07 +02002161 if (conf->flags & IEEE80211_CONF_PS &&
2162 !test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
2163 set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002164
2165 /*
2166 * We enter PSM only if we're already associated.
2167 * If we're not, we'll enter it when joining an SSID,
2168 * through the bss_info_changed() hook.
2169 */
Juuso Oikarinen830fb672009-12-11 15:41:06 +02002170 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02002171 wl1271_debug(DEBUG_PSM, "psm enabled");
Juuso Oikarinend8c42c02010-02-18 13:25:36 +02002172 ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02002173 wl->basic_rate, true);
Juuso Oikarinenaf5e0842009-12-11 15:41:00 +02002174 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002175 } else if (!(conf->flags & IEEE80211_CONF_PS) &&
Juuso Oikarinen71449f82009-12-11 15:41:07 +02002176 test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
Juuso Oikarinen18f8d462010-02-22 08:38:34 +02002177 wl1271_debug(DEBUG_PSM, "psm disabled");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002178
Juuso Oikarinen71449f82009-12-11 15:41:07 +02002179 clear_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002180
Juuso Oikarinen71449f82009-12-11 15:41:07 +02002181 if (test_bit(WL1271_FLAG_PSM, &wl->flags))
Juuso Oikarinend8c42c02010-02-18 13:25:36 +02002182 ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02002183 wl->basic_rate, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002184 }
2185
2186 if (conf->power_level != wl->power_level) {
2187 ret = wl1271_acx_tx_power(wl, conf->power_level);
2188 if (ret < 0)
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02002189 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002190
2191 wl->power_level = conf->power_level;
2192 }
2193
2194out_sleep:
2195 wl1271_ps_elp_sleep(wl);
2196
2197out:
2198 mutex_unlock(&wl->mutex);
2199
2200 return ret;
2201}
2202
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002203struct wl1271_filter_params {
2204 bool enabled;
2205 int mc_list_length;
2206 u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
2207};
2208
Jiri Pirko22bedad32010-04-01 21:22:57 +00002209static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw,
2210 struct netdev_hw_addr_list *mc_list)
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002211{
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002212 struct wl1271_filter_params *fp;
Jiri Pirko22bedad32010-04-01 21:22:57 +00002213 struct netdev_hw_addr *ha;
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03002214 struct wl1271 *wl = hw->priv;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002215
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03002216 if (unlikely(wl->state == WL1271_STATE_OFF))
2217 return 0;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002218
Juuso Oikarinen74441132009-10-13 12:47:53 +03002219 fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002220 if (!fp) {
2221 wl1271_error("Out of memory setting filters.");
2222 return 0;
2223 }
2224
2225 /* update multicast filtering parameters */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002226 fp->mc_list_length = 0;
Jiri Pirko22bedad32010-04-01 21:22:57 +00002227 if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) {
2228 fp->enabled = false;
2229 } else {
2230 fp->enabled = true;
2231 netdev_hw_addr_list_for_each(ha, mc_list) {
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002232 memcpy(fp->mc_list[fp->mc_list_length],
Jiri Pirko22bedad32010-04-01 21:22:57 +00002233 ha->addr, ETH_ALEN);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002234 fp->mc_list_length++;
Jiri Pirko22bedad32010-04-01 21:22:57 +00002235 }
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002236 }
2237
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002238 return (u64)(unsigned long)fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002239}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002240
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002241#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
2242 FIF_ALLMULTI | \
2243 FIF_FCSFAIL | \
2244 FIF_BCN_PRBRESP_PROMISC | \
2245 FIF_CONTROL | \
2246 FIF_OTHER_BSS)
2247
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002248static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
2249 unsigned int changed,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03002250 unsigned int *total, u64 multicast)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002251{
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002252 struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002253 struct wl1271 *wl = hw->priv;
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002254 int ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002255
Arik Nemtsov7d057862010-10-16 19:25:35 +02002256 wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter changed %x"
2257 " total %x", changed, *total);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002258
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002259 mutex_lock(&wl->mutex);
2260
Saravanan Dhanabal2c10bb92010-04-09 11:07:27 +03002261 *total &= WL1271_SUPPORTED_FILTERS;
2262 changed &= WL1271_SUPPORTED_FILTERS;
2263
2264 if (unlikely(wl->state == WL1271_STATE_OFF))
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002265 goto out;
2266
Ido Yariva6208652011-03-01 15:14:41 +02002267 ret = wl1271_ps_elp_wakeup(wl);
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002268 if (ret < 0)
2269 goto out;
2270
Arik Nemtsov7d057862010-10-16 19:25:35 +02002271 if (wl->bss_type != BSS_TYPE_AP_BSS) {
2272 if (*total & FIF_ALLMULTI)
2273 ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
2274 else if (fp)
2275 ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
2276 fp->mc_list,
2277 fp->mc_list_length);
2278 if (ret < 0)
2279 goto out_sleep;
2280 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002281
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002282 /* determine, whether supported filter values have changed */
2283 if (changed == 0)
2284 goto out_sleep;
2285
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02002286 /* configure filters */
2287 wl->filters = *total;
2288 wl1271_configure_filters(wl, 0);
2289
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03002290 /* apply configured filters */
2291 ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
2292 if (ret < 0)
2293 goto out_sleep;
2294
2295out_sleep:
2296 wl1271_ps_elp_sleep(wl);
2297
2298out:
2299 mutex_unlock(&wl->mutex);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02002300 kfree(fp);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002301}
2302
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002303static int wl1271_record_ap_key(struct wl1271 *wl, u8 id, u8 key_type,
2304 u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
2305 u16 tx_seq_16)
2306{
2307 struct wl1271_ap_key *ap_key;
2308 int i;
2309
2310 wl1271_debug(DEBUG_CRYPT, "record ap key id %d", (int)id);
2311
2312 if (key_size > MAX_KEY_SIZE)
2313 return -EINVAL;
2314
2315 /*
2316 * Find next free entry in ap_keys. Also check we are not replacing
2317 * an existing key.
2318 */
2319 for (i = 0; i < MAX_NUM_KEYS; i++) {
2320 if (wl->recorded_ap_keys[i] == NULL)
2321 break;
2322
2323 if (wl->recorded_ap_keys[i]->id == id) {
2324 wl1271_warning("trying to record key replacement");
2325 return -EINVAL;
2326 }
2327 }
2328
2329 if (i == MAX_NUM_KEYS)
2330 return -EBUSY;
2331
2332 ap_key = kzalloc(sizeof(*ap_key), GFP_KERNEL);
2333 if (!ap_key)
2334 return -ENOMEM;
2335
2336 ap_key->id = id;
2337 ap_key->key_type = key_type;
2338 ap_key->key_size = key_size;
2339 memcpy(ap_key->key, key, key_size);
2340 ap_key->hlid = hlid;
2341 ap_key->tx_seq_32 = tx_seq_32;
2342 ap_key->tx_seq_16 = tx_seq_16;
2343
2344 wl->recorded_ap_keys[i] = ap_key;
2345 return 0;
2346}
2347
2348static void wl1271_free_ap_keys(struct wl1271 *wl)
2349{
2350 int i;
2351
2352 for (i = 0; i < MAX_NUM_KEYS; i++) {
2353 kfree(wl->recorded_ap_keys[i]);
2354 wl->recorded_ap_keys[i] = NULL;
2355 }
2356}
2357
2358static int wl1271_ap_init_hwenc(struct wl1271 *wl)
2359{
2360 int i, ret = 0;
2361 struct wl1271_ap_key *key;
2362 bool wep_key_added = false;
2363
2364 for (i = 0; i < MAX_NUM_KEYS; i++) {
2365 if (wl->recorded_ap_keys[i] == NULL)
2366 break;
2367
2368 key = wl->recorded_ap_keys[i];
2369 ret = wl1271_cmd_set_ap_key(wl, KEY_ADD_OR_REPLACE,
2370 key->id, key->key_type,
2371 key->key_size, key->key,
2372 key->hlid, key->tx_seq_32,
2373 key->tx_seq_16);
2374 if (ret < 0)
2375 goto out;
2376
2377 if (key->key_type == KEY_WEP)
2378 wep_key_added = true;
2379 }
2380
2381 if (wep_key_added) {
2382 ret = wl1271_cmd_set_ap_default_wep_key(wl, wl->default_key);
2383 if (ret < 0)
2384 goto out;
2385 }
2386
2387out:
2388 wl1271_free_ap_keys(wl);
2389 return ret;
2390}
2391
2392static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
2393 u8 key_size, const u8 *key, u32 tx_seq_32,
2394 u16 tx_seq_16, struct ieee80211_sta *sta)
2395{
2396 int ret;
2397 bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
2398
2399 if (is_ap) {
2400 struct wl1271_station *wl_sta;
2401 u8 hlid;
2402
2403 if (sta) {
2404 wl_sta = (struct wl1271_station *)sta->drv_priv;
2405 hlid = wl_sta->hlid;
2406 } else {
2407 hlid = WL1271_AP_BROADCAST_HLID;
2408 }
2409
2410 if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
2411 /*
2412 * We do not support removing keys after AP shutdown.
2413 * Pretend we do to make mac80211 happy.
2414 */
2415 if (action != KEY_ADD_OR_REPLACE)
2416 return 0;
2417
2418 ret = wl1271_record_ap_key(wl, id,
2419 key_type, key_size,
2420 key, hlid, tx_seq_32,
2421 tx_seq_16);
2422 } else {
2423 ret = wl1271_cmd_set_ap_key(wl, action,
2424 id, key_type, key_size,
2425 key, hlid, tx_seq_32,
2426 tx_seq_16);
2427 }
2428
2429 if (ret < 0)
2430 return ret;
2431 } else {
2432 const u8 *addr;
2433 static const u8 bcast_addr[ETH_ALEN] = {
2434 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
2435 };
2436
2437 addr = sta ? sta->addr : bcast_addr;
2438
2439 if (is_zero_ether_addr(addr)) {
2440 /* We dont support TX only encryption */
2441 return -EOPNOTSUPP;
2442 }
2443
2444 /* The wl1271 does not allow to remove unicast keys - they
2445 will be cleared automatically on next CMD_JOIN. Ignore the
2446 request silently, as we dont want the mac80211 to emit
2447 an error message. */
2448 if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr))
2449 return 0;
2450
2451 ret = wl1271_cmd_set_sta_key(wl, action,
2452 id, key_type, key_size,
2453 key, addr, tx_seq_32,
2454 tx_seq_16);
2455 if (ret < 0)
2456 return ret;
2457
2458 /* the default WEP key needs to be configured at least once */
2459 if (key_type == KEY_WEP) {
2460 ret = wl1271_cmd_set_sta_default_wep_key(wl,
2461 wl->default_key);
2462 if (ret < 0)
2463 return ret;
2464 }
2465 }
2466
2467 return 0;
2468}
2469
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002470static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
2471 struct ieee80211_vif *vif,
2472 struct ieee80211_sta *sta,
2473 struct ieee80211_key_conf *key_conf)
2474{
2475 struct wl1271 *wl = hw->priv;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002476 int ret;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03002477 u32 tx_seq_32 = 0;
2478 u16 tx_seq_16 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002479 u8 key_type;
2480
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002481 wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
2482
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002483 wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002484 wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
Johannes Berg97359d12010-08-10 09:46:38 +02002485 key_conf->cipher, key_conf->keyidx,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002486 key_conf->keylen, key_conf->flags);
2487 wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
2488
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002489 mutex_lock(&wl->mutex);
2490
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002491 if (unlikely(wl->state == WL1271_STATE_OFF)) {
2492 ret = -EAGAIN;
2493 goto out_unlock;
2494 }
2495
Ido Yariva6208652011-03-01 15:14:41 +02002496 ret = wl1271_ps_elp_wakeup(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002497 if (ret < 0)
2498 goto out_unlock;
2499
Johannes Berg97359d12010-08-10 09:46:38 +02002500 switch (key_conf->cipher) {
2501 case WLAN_CIPHER_SUITE_WEP40:
2502 case WLAN_CIPHER_SUITE_WEP104:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002503 key_type = KEY_WEP;
2504
2505 key_conf->hw_key_idx = key_conf->keyidx;
2506 break;
Johannes Berg97359d12010-08-10 09:46:38 +02002507 case WLAN_CIPHER_SUITE_TKIP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002508 key_type = KEY_TKIP;
2509
2510 key_conf->hw_key_idx = key_conf->keyidx;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02002511 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
2512 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002513 break;
Johannes Berg97359d12010-08-10 09:46:38 +02002514 case WLAN_CIPHER_SUITE_CCMP:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002515 key_type = KEY_AES;
2516
2517 key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Juuso Oikarinen04e36fc2010-02-22 08:38:40 +02002518 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
2519 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002520 break;
Juuso Oikarinen7a557242010-09-27 12:42:07 +02002521 case WL1271_CIPHER_SUITE_GEM:
2522 key_type = KEY_GEM;
2523 tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
2524 tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
2525 break;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002526 default:
Johannes Berg97359d12010-08-10 09:46:38 +02002527 wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002528
2529 ret = -EOPNOTSUPP;
2530 goto out_sleep;
2531 }
2532
2533 switch (cmd) {
2534 case SET_KEY:
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002535 ret = wl1271_set_key(wl, KEY_ADD_OR_REPLACE,
2536 key_conf->keyidx, key_type,
2537 key_conf->keylen, key_conf->key,
2538 tx_seq_32, tx_seq_16, sta);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002539 if (ret < 0) {
2540 wl1271_error("Could not add or replace key");
2541 goto out_sleep;
2542 }
2543 break;
2544
2545 case DISABLE_KEY:
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002546 ret = wl1271_set_key(wl, KEY_REMOVE,
2547 key_conf->keyidx, key_type,
2548 key_conf->keylen, key_conf->key,
2549 0, 0, sta);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002550 if (ret < 0) {
2551 wl1271_error("Could not remove key");
2552 goto out_sleep;
2553 }
2554 break;
2555
2556 default:
2557 wl1271_error("Unsupported key cmd 0x%x", cmd);
2558 ret = -EOPNOTSUPP;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002559 break;
2560 }
2561
2562out_sleep:
2563 wl1271_ps_elp_sleep(wl);
2564
2565out_unlock:
2566 mutex_unlock(&wl->mutex);
2567
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002568 return ret;
2569}
2570
2571static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
Johannes Berga060bbf2010-04-27 11:59:34 +02002572 struct ieee80211_vif *vif,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002573 struct cfg80211_scan_request *req)
2574{
2575 struct wl1271 *wl = hw->priv;
2576 int ret;
2577 u8 *ssid = NULL;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03002578 size_t len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002579
2580 wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
2581
2582 if (req->n_ssids) {
2583 ssid = req->ssids[0].ssid;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03002584 len = req->ssids[0].ssid_len;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002585 }
2586
2587 mutex_lock(&wl->mutex);
2588
Juuso Oikarinenb739a422010-10-26 13:24:38 +02002589 if (wl->state == WL1271_STATE_OFF) {
2590 /*
2591 * We cannot return -EBUSY here because cfg80211 will expect
2592 * a call to ieee80211_scan_completed if we do - in this case
2593 * there won't be any call.
2594 */
2595 ret = -EAGAIN;
2596 goto out;
2597 }
2598
Ido Yariva6208652011-03-01 15:14:41 +02002599 ret = wl1271_ps_elp_wakeup(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002600 if (ret < 0)
2601 goto out;
2602
Luciano Coelho5924f892010-08-04 03:46:22 +03002603 ret = wl1271_scan(hw->priv, ssid, len, req);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002604
2605 wl1271_ps_elp_sleep(wl);
2606
2607out:
2608 mutex_unlock(&wl->mutex);
2609
2610 return ret;
2611}
2612
Luciano Coelho33c2c062011-05-10 14:46:02 +03002613static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
2614 struct ieee80211_vif *vif,
2615 struct cfg80211_sched_scan_request *req,
2616 struct ieee80211_sched_scan_ies *ies)
2617{
2618 struct wl1271 *wl = hw->priv;
2619 int ret;
2620
2621 wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_start");
2622
2623 mutex_lock(&wl->mutex);
2624
2625 ret = wl1271_ps_elp_wakeup(wl);
2626 if (ret < 0)
2627 goto out;
2628
2629 ret = wl1271_scan_sched_scan_config(wl, req, ies);
2630 if (ret < 0)
2631 goto out_sleep;
2632
2633 ret = wl1271_scan_sched_scan_start(wl);
2634 if (ret < 0)
2635 goto out_sleep;
2636
2637 wl->sched_scanning = true;
2638
2639out_sleep:
2640 wl1271_ps_elp_sleep(wl);
2641out:
2642 mutex_unlock(&wl->mutex);
2643 return ret;
2644}
2645
2646static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
2647 struct ieee80211_vif *vif)
2648{
2649 struct wl1271 *wl = hw->priv;
2650 int ret;
2651
2652 wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_stop");
2653
2654 mutex_lock(&wl->mutex);
2655
2656 ret = wl1271_ps_elp_wakeup(wl);
2657 if (ret < 0)
2658 goto out;
2659
2660 wl1271_scan_sched_scan_stop(wl);
2661
2662 wl1271_ps_elp_sleep(wl);
2663out:
2664 mutex_unlock(&wl->mutex);
2665}
2666
Arik Nemtsov68d069c2010-11-08 10:51:07 +01002667static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
2668{
2669 struct wl1271 *wl = hw->priv;
2670 int ret = 0;
2671
2672 mutex_lock(&wl->mutex);
2673
2674 if (unlikely(wl->state == WL1271_STATE_OFF)) {
2675 ret = -EAGAIN;
2676 goto out;
2677 }
2678
Ido Yariva6208652011-03-01 15:14:41 +02002679 ret = wl1271_ps_elp_wakeup(wl);
Arik Nemtsov68d069c2010-11-08 10:51:07 +01002680 if (ret < 0)
2681 goto out;
2682
Arik Nemtsov5f704d12011-04-18 14:15:21 +03002683 ret = wl1271_acx_frag_threshold(wl, value);
Arik Nemtsov68d069c2010-11-08 10:51:07 +01002684 if (ret < 0)
2685 wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret);
2686
2687 wl1271_ps_elp_sleep(wl);
2688
2689out:
2690 mutex_unlock(&wl->mutex);
2691
2692 return ret;
2693}
2694
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002695static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
2696{
2697 struct wl1271 *wl = hw->priv;
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03002698 int ret = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002699
2700 mutex_lock(&wl->mutex);
2701
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002702 if (unlikely(wl->state == WL1271_STATE_OFF)) {
2703 ret = -EAGAIN;
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03002704 goto out;
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02002705 }
Saravanan Dhanabalaecb0562010-04-09 11:07:28 +03002706
Ido Yariva6208652011-03-01 15:14:41 +02002707 ret = wl1271_ps_elp_wakeup(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002708 if (ret < 0)
2709 goto out;
2710
Arik Nemtsov5f704d12011-04-18 14:15:21 +03002711 ret = wl1271_acx_rts_threshold(wl, value);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002712 if (ret < 0)
2713 wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret);
2714
2715 wl1271_ps_elp_sleep(wl);
2716
2717out:
2718 mutex_unlock(&wl->mutex);
2719
2720 return ret;
2721}
2722
Arik Nemtsove78a2872010-10-16 19:07:21 +02002723static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb,
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02002724 int offset)
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002725{
Eliad Peller889cb362011-05-01 09:56:45 +03002726 u8 ssid_len;
2727 const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
2728 skb->len - offset);
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002729
Eliad Peller889cb362011-05-01 09:56:45 +03002730 if (!ptr) {
2731 wl1271_error("No SSID in IEs!");
2732 return -ENOENT;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002733 }
Arik Nemtsove78a2872010-10-16 19:07:21 +02002734
Eliad Peller889cb362011-05-01 09:56:45 +03002735 ssid_len = ptr[1];
2736 if (ssid_len > IEEE80211_MAX_SSID_LEN) {
2737 wl1271_error("SSID is too long!");
2738 return -EINVAL;
2739 }
2740
2741 wl->ssid_len = ssid_len;
2742 memcpy(wl->ssid, ptr+2, ssid_len);
2743 return 0;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002744}
2745
Arik Nemtsove78a2872010-10-16 19:07:21 +02002746static int wl1271_bss_erp_info_changed(struct wl1271 *wl,
2747 struct ieee80211_bss_conf *bss_conf,
2748 u32 changed)
2749{
2750 int ret = 0;
2751
2752 if (changed & BSS_CHANGED_ERP_SLOT) {
2753 if (bss_conf->use_short_slot)
2754 ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
2755 else
2756 ret = wl1271_acx_slot(wl, SLOT_TIME_LONG);
2757 if (ret < 0) {
2758 wl1271_warning("Set slot time failed %d", ret);
2759 goto out;
2760 }
2761 }
2762
2763 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
2764 if (bss_conf->use_short_preamble)
2765 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT);
2766 else
2767 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG);
2768 }
2769
2770 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
2771 if (bss_conf->use_cts_prot)
2772 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE);
2773 else
2774 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE);
2775 if (ret < 0) {
2776 wl1271_warning("Set ctsprotect failed %d", ret);
2777 goto out;
2778 }
2779 }
2780
2781out:
2782 return ret;
2783}
2784
2785static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
2786 struct ieee80211_vif *vif,
2787 struct ieee80211_bss_conf *bss_conf,
2788 u32 changed)
2789{
2790 bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
2791 int ret = 0;
2792
2793 if ((changed & BSS_CHANGED_BEACON_INT)) {
2794 wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d",
2795 bss_conf->beacon_int);
2796
2797 wl->beacon_int = bss_conf->beacon_int;
2798 }
2799
2800 if ((changed & BSS_CHANGED_BEACON)) {
2801 struct ieee80211_hdr *hdr;
2802 int ieoffset = offsetof(struct ieee80211_mgmt,
2803 u.beacon.variable);
2804 struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
2805 u16 tmpl_id;
2806
2807 if (!beacon)
2808 goto out;
2809
2810 wl1271_debug(DEBUG_MASTER, "beacon updated");
2811
2812 ret = wl1271_ssid_set(wl, beacon, ieoffset);
2813 if (ret < 0) {
2814 dev_kfree_skb(beacon);
2815 goto out;
2816 }
2817 tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
2818 CMD_TEMPL_BEACON;
2819 ret = wl1271_cmd_template_set(wl, tmpl_id,
2820 beacon->data,
2821 beacon->len, 0,
2822 wl1271_tx_min_rate_get(wl));
2823 if (ret < 0) {
2824 dev_kfree_skb(beacon);
2825 goto out;
2826 }
2827
2828 hdr = (struct ieee80211_hdr *) beacon->data;
2829 hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
2830 IEEE80211_STYPE_PROBE_RESP);
2831
2832 tmpl_id = is_ap ? CMD_TEMPL_AP_PROBE_RESPONSE :
2833 CMD_TEMPL_PROBE_RESPONSE;
2834 ret = wl1271_cmd_template_set(wl,
2835 tmpl_id,
2836 beacon->data,
2837 beacon->len, 0,
2838 wl1271_tx_min_rate_get(wl));
2839 dev_kfree_skb(beacon);
2840 if (ret < 0)
2841 goto out;
2842 }
2843
2844out:
2845 return ret;
2846}
2847
2848/* AP mode changes */
2849static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002850 struct ieee80211_vif *vif,
2851 struct ieee80211_bss_conf *bss_conf,
2852 u32 changed)
2853{
Arik Nemtsove78a2872010-10-16 19:07:21 +02002854 int ret = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002855
Arik Nemtsove78a2872010-10-16 19:07:21 +02002856 if ((changed & BSS_CHANGED_BASIC_RATES)) {
2857 u32 rates = bss_conf->basic_rates;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002858
Arik Nemtsove78a2872010-10-16 19:07:21 +02002859 wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates);
2860 wl->basic_rate = wl1271_tx_min_rate_get(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002861
Arik Nemtsov70f47422011-04-18 14:15:25 +03002862 ret = wl1271_init_ap_rates(wl);
Arik Nemtsove78a2872010-10-16 19:07:21 +02002863 if (ret < 0) {
Arik Nemtsov70f47422011-04-18 14:15:25 +03002864 wl1271_error("AP rate policy change failed %d", ret);
Arik Nemtsove78a2872010-10-16 19:07:21 +02002865 goto out;
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02002866 }
Arik Nemtsovc45a85b2011-04-18 14:15:26 +03002867
2868 ret = wl1271_ap_init_templates(wl);
2869 if (ret < 0)
2870 goto out;
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02002871 }
2872
Arik Nemtsove78a2872010-10-16 19:07:21 +02002873 ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed);
2874 if (ret < 0)
2875 goto out;
2876
2877 if ((changed & BSS_CHANGED_BEACON_ENABLED)) {
2878 if (bss_conf->enable_beacon) {
2879 if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
2880 ret = wl1271_cmd_start_bss(wl);
2881 if (ret < 0)
2882 goto out;
2883
2884 set_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
2885 wl1271_debug(DEBUG_AP, "started AP");
Arik Nemtsov7f179b42010-10-16 21:39:06 +02002886
2887 ret = wl1271_ap_init_hwenc(wl);
2888 if (ret < 0)
2889 goto out;
Arik Nemtsove78a2872010-10-16 19:07:21 +02002890 }
2891 } else {
2892 if (test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
2893 ret = wl1271_cmd_stop_bss(wl);
2894 if (ret < 0)
2895 goto out;
2896
2897 clear_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
2898 wl1271_debug(DEBUG_AP, "stopped AP");
2899 }
2900 }
2901 }
2902
Eliad Pellercb5ae052011-04-07 15:52:05 +03002903 if (changed & BSS_CHANGED_IBSS) {
2904 wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d",
2905 bss_conf->ibss_joined);
2906
2907 if (bss_conf->ibss_joined) {
2908 u32 rates = bss_conf->basic_rates;
2909 wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
2910 rates);
2911 wl->basic_rate = wl1271_tx_min_rate_get(wl);
2912
2913 /* by default, use 11b rates */
2914 wl->rate_set = CONF_TX_IBSS_DEFAULT_RATES;
2915 ret = wl1271_acx_sta_rate_policies(wl);
2916 if (ret < 0)
2917 goto out;
2918 }
2919 }
2920
Arik Nemtsove78a2872010-10-16 19:07:21 +02002921 ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
2922 if (ret < 0)
2923 goto out;
2924out:
2925 return;
2926}
2927
2928/* STA/IBSS mode changes */
2929static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
2930 struct ieee80211_vif *vif,
2931 struct ieee80211_bss_conf *bss_conf,
2932 u32 changed)
2933{
2934 bool do_join = false, set_assoc = false;
2935 bool is_ibss = (wl->bss_type == BSS_TYPE_IBSS);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02002936 u32 sta_rate_set = 0;
Arik Nemtsove78a2872010-10-16 19:07:21 +02002937 int ret;
Luciano Coelho2d6e4e762011-01-11 19:07:21 +01002938 struct ieee80211_sta *sta;
Arik Nemtsova1008852011-02-12 23:24:20 +02002939 bool sta_exists = false;
2940 struct ieee80211_sta_ht_cap sta_ht_cap;
Arik Nemtsove78a2872010-10-16 19:07:21 +02002941
2942 if (is_ibss) {
2943 ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf,
2944 changed);
2945 if (ret < 0)
2946 goto out;
2947 }
2948
2949 if ((changed & BSS_CHANGED_BEACON_INT) && is_ibss)
2950 do_join = true;
2951
2952 /* Need to update the SSID (for filtering etc) */
2953 if ((changed & BSS_CHANGED_BEACON) && is_ibss)
2954 do_join = true;
2955
2956 if ((changed & BSS_CHANGED_BEACON_ENABLED) && is_ibss) {
Juuso Oikarinen5da11dc2010-03-26 12:53:24 +02002957 wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
2958 bss_conf->enable_beacon ? "enabled" : "disabled");
2959
2960 if (bss_conf->enable_beacon)
2961 wl->set_bss_type = BSS_TYPE_IBSS;
2962 else
2963 wl->set_bss_type = BSS_TYPE_STA_BSS;
2964 do_join = true;
2965 }
2966
Arik Nemtsove78a2872010-10-16 19:07:21 +02002967 if ((changed & BSS_CHANGED_CQM)) {
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03002968 bool enable = false;
2969 if (bss_conf->cqm_rssi_thold)
2970 enable = true;
2971 ret = wl1271_acx_rssi_snr_trigger(wl, enable,
2972 bss_conf->cqm_rssi_thold,
2973 bss_conf->cqm_rssi_hyst);
2974 if (ret < 0)
2975 goto out;
2976 wl->rssi_thold = bss_conf->cqm_rssi_thold;
2977 }
2978
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002979 if ((changed & BSS_CHANGED_BSSID) &&
2980 /*
2981 * Now we know the correct bssid, so we send a new join command
2982 * and enable the BSSID filter
2983 */
2984 memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
Arik Nemtsove78a2872010-10-16 19:07:21 +02002985 memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
Juuso Oikarinena0cb7be2010-03-18 12:26:44 +02002986
Eliad Pellerfa287b82010-12-26 09:27:50 +01002987 if (!is_zero_ether_addr(wl->bssid)) {
2988 ret = wl1271_cmd_build_null_data(wl);
2989 if (ret < 0)
2990 goto out;
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02002991
Eliad Pellerfa287b82010-12-26 09:27:50 +01002992 ret = wl1271_build_qos_null_data(wl);
2993 if (ret < 0)
2994 goto out;
Saravanan Dhanabal141418c2010-04-28 09:50:00 +03002995
Eliad Pellerfa287b82010-12-26 09:27:50 +01002996 /* filter out all packets not from this BSSID */
2997 wl1271_configure_filters(wl, 0);
Juuso Oikarinen14b228a2010-03-18 12:26:43 +02002998
Eliad Pellerfa287b82010-12-26 09:27:50 +01002999 /* Need to update the BSSID (for filtering etc) */
3000 do_join = true;
3001 }
Juuso Oikarinen30240fc2010-02-18 13:25:38 +02003002 }
3003
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003004 rcu_read_lock();
3005 sta = ieee80211_find_sta(vif, bss_conf->bssid);
3006 if (sta) {
3007 /* save the supp_rates of the ap */
3008 sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band];
3009 if (sta->ht_cap.ht_supported)
3010 sta_rate_set |=
3011 (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET);
Arik Nemtsova1008852011-02-12 23:24:20 +02003012 sta_ht_cap = sta->ht_cap;
3013 sta_exists = true;
3014 }
3015 rcu_read_unlock();
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003016
Arik Nemtsova1008852011-02-12 23:24:20 +02003017 if (sta_exists) {
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003018 /* handle new association with HT and HT information change */
3019 if ((changed & BSS_CHANGED_HT) &&
3020 (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
Arik Nemtsova1008852011-02-12 23:24:20 +02003021 ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap,
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003022 true);
3023 if (ret < 0) {
3024 wl1271_warning("Set ht cap true failed %d",
3025 ret);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003026 goto out;
3027 }
3028 ret = wl1271_acx_set_ht_information(wl,
3029 bss_conf->ht_operation_mode);
3030 if (ret < 0) {
3031 wl1271_warning("Set ht information failed %d",
3032 ret);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003033 goto out;
3034 }
3035 }
3036 /* handle new association without HT and disassociation */
3037 else if (changed & BSS_CHANGED_ASSOC) {
Arik Nemtsova1008852011-02-12 23:24:20 +02003038 ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap,
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003039 false);
3040 if (ret < 0) {
3041 wl1271_warning("Set ht cap false failed %d",
3042 ret);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003043 goto out;
3044 }
3045 }
3046 }
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003047
Arik Nemtsove78a2872010-10-16 19:07:21 +02003048 if ((changed & BSS_CHANGED_ASSOC)) {
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003049 if (bss_conf->assoc) {
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003050 u32 rates;
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003051 int ieoffset;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003052 wl->aid = bss_conf->aid;
Juuso Oikarinen69e54342010-05-07 11:39:00 +03003053 set_assoc = true;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003054
Juuso Oikarinen90494a92010-07-08 17:50:00 +03003055 wl->ps_poll_failures = 0;
3056
Luciano Coelhoae751ba2009-10-12 15:08:57 +03003057 /*
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003058 * use basic rates from AP, and determine lowest rate
3059 * to use with control frames.
3060 */
3061 rates = bss_conf->basic_rates;
3062 wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
3063 rates);
Arik Nemtsove0fe3712010-10-16 18:19:53 +02003064 wl->basic_rate = wl1271_tx_min_rate_get(wl);
Eliad Peller72c2d9e2011-02-02 09:59:37 +02003065 if (sta_rate_set)
3066 wl->rate_set = wl1271_tx_enabled_rates_get(wl,
3067 sta_rate_set);
Arik Nemtsov79b223f2010-10-16 17:52:59 +02003068 ret = wl1271_acx_sta_rate_policies(wl);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003069 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003070 goto out;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003071
3072 /*
Luciano Coelhoae751ba2009-10-12 15:08:57 +03003073 * with wl1271, we don't need to update the
3074 * beacon_int and dtim_period, because the firmware
3075 * updates it by itself when the first beacon is
3076 * received after a join.
3077 */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003078 ret = wl1271_cmd_build_ps_poll(wl, wl->aid);
3079 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003080 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003081
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02003082 /*
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003083 * Get a template for hardware connection maintenance
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02003084 */
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003085 dev_kfree_skb(wl->probereq);
3086 wl->probereq = wl1271_cmd_build_ap_probe_req(wl, NULL);
3087 ieoffset = offsetof(struct ieee80211_mgmt,
3088 u.probe_req.variable);
3089 wl1271_ssid_set(wl, wl->probereq, ieoffset);
Juuso Oikarinenc2b2d992010-03-26 12:53:28 +02003090
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02003091 /* enable the connection monitoring feature */
3092 ret = wl1271_acx_conn_monit_params(wl, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003093 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003094 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003095
3096 /* If we want to go in PSM but we're not there yet */
Juuso Oikarinen71449f82009-12-11 15:41:07 +02003097 if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) &&
3098 !test_bit(WL1271_FLAG_PSM, &wl->flags)) {
Arik Nemtsove78a2872010-10-16 19:07:21 +02003099 enum wl1271_cmd_ps_mode mode;
3100
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003101 mode = STATION_POWER_SAVE_MODE;
Juuso Oikarinen65cddbf2010-08-24 06:28:03 +03003102 ret = wl1271_ps_set_mode(wl, mode,
Juuso Oikarinen8eab7b42010-09-24 03:10:11 +02003103 wl->basic_rate,
Juuso Oikarinen65cddbf2010-08-24 06:28:03 +03003104 true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003105 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003106 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003107 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03003108 } else {
3109 /* use defaults when not associated */
Eliad Peller30df14d2011-04-05 19:13:28 +03003110 bool was_assoc =
3111 !!test_and_clear_bit(WL1271_FLAG_STA_ASSOCIATED,
3112 &wl->flags);
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03003113 clear_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags);
Juuso Oikarinend94cd292009-10-08 21:56:25 +03003114 wl->aid = 0;
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02003115
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +02003116 /* free probe-request template */
3117 dev_kfree_skb(wl->probereq);
3118 wl->probereq = NULL;
3119
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03003120 /* re-enable dynamic ps - just in case */
Juuso Oikarinenf532be62010-07-08 17:50:05 +03003121 ieee80211_enable_dyn_ps(wl->vif);
Juuso Oikarinen8d2ef7b2010-07-08 17:50:03 +03003122
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003123 /* revert back to minimum rates for the current band */
3124 wl1271_set_band_rate(wl);
Arik Nemtsove0fe3712010-10-16 18:19:53 +02003125 wl->basic_rate = wl1271_tx_min_rate_get(wl);
Arik Nemtsov79b223f2010-10-16 17:52:59 +02003126 ret = wl1271_acx_sta_rate_policies(wl);
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003127 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003128 goto out;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03003129
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02003130 /* disable connection monitor features */
3131 ret = wl1271_acx_conn_monit_params(wl, false);
Juuso Oikarinenc1899552010-03-26 12:53:32 +02003132
3133 /* Disable the keep-alive feature */
3134 ret = wl1271_acx_keep_alive_mode(wl, false);
Juuso Oikarinen6ccbb922010-03-26 12:53:23 +02003135 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003136 goto out;
Juuso Oikarinenb84a7d32010-11-22 12:59:08 +02003137
3138 /* restore the bssid filter and go to dummy bssid */
Eliad Peller30df14d2011-04-05 19:13:28 +03003139 if (was_assoc) {
3140 wl1271_unjoin(wl);
3141 wl1271_dummy_join(wl);
3142 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003143 }
3144 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03003145
Arik Nemtsove78a2872010-10-16 19:07:21 +02003146 ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
3147 if (ret < 0)
3148 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003149
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03003150 if (changed & BSS_CHANGED_ARP_FILTER) {
3151 __be32 addr = bss_conf->arp_addr_list[0];
3152 WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
3153
Eliad Pellerc5312772010-12-09 11:31:27 +02003154 if (bss_conf->arp_addr_cnt == 1 &&
3155 bss_conf->arp_filter_enabled) {
3156 /*
3157 * The template should have been configured only upon
3158 * association. however, it seems that the correct ip
3159 * isn't being set (when sending), so we have to
3160 * reconfigure the template upon every ip change.
3161 */
3162 ret = wl1271_cmd_build_arp_rsp(wl, addr);
3163 if (ret < 0) {
3164 wl1271_warning("build arp rsp failed: %d", ret);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003165 goto out;
Eliad Pellerc5312772010-12-09 11:31:27 +02003166 }
3167
3168 ret = wl1271_acx_arp_ip_filter(wl,
Eliad Pellere5e2f242011-01-24 19:19:03 +01003169 ACX_ARP_FILTER_ARP_FILTERING,
Eliad Pellerc5312772010-12-09 11:31:27 +02003170 addr);
3171 } else
3172 ret = wl1271_acx_arp_ip_filter(wl, 0, addr);
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03003173
3174 if (ret < 0)
Arik Nemtsove78a2872010-10-16 19:07:21 +02003175 goto out;
Juuso Oikarinenca52a5e2010-07-08 17:50:02 +03003176 }
3177
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02003178 if (do_join) {
Juuso Oikarinen69e54342010-05-07 11:39:00 +03003179 ret = wl1271_join(wl, set_assoc);
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02003180 if (ret < 0) {
3181 wl1271_warning("cmd join failed %d", ret);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003182 goto out;
Juuso Oikarinen8bf29b02010-02-18 13:25:51 +02003183 }
Juuso Oikarinenc1899552010-03-26 12:53:32 +02003184 }
3185
Arik Nemtsove78a2872010-10-16 19:07:21 +02003186out:
3187 return;
3188}
3189
3190static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
3191 struct ieee80211_vif *vif,
3192 struct ieee80211_bss_conf *bss_conf,
3193 u32 changed)
3194{
3195 struct wl1271 *wl = hw->priv;
3196 bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
3197 int ret;
3198
3199 wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
3200 (int)changed);
3201
3202 mutex_lock(&wl->mutex);
3203
3204 if (unlikely(wl->state == WL1271_STATE_OFF))
3205 goto out;
3206
Ido Yariva6208652011-03-01 15:14:41 +02003207 ret = wl1271_ps_elp_wakeup(wl);
Arik Nemtsove78a2872010-10-16 19:07:21 +02003208 if (ret < 0)
3209 goto out;
3210
3211 if (is_ap)
3212 wl1271_bss_info_changed_ap(wl, vif, bss_conf, changed);
3213 else
3214 wl1271_bss_info_changed_sta(wl, vif, bss_conf, changed);
3215
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003216 wl1271_ps_elp_sleep(wl);
3217
3218out:
3219 mutex_unlock(&wl->mutex);
3220}
3221
Kalle Valoc6999d82010-02-18 13:25:41 +02003222static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
3223 const struct ieee80211_tx_queue_params *params)
3224{
3225 struct wl1271 *wl = hw->priv;
Kalle Valo4695dc92010-03-18 12:26:38 +02003226 u8 ps_scheme;
Arik Nemtsov488fc542010-10-16 20:33:45 +02003227 int ret = 0;
Kalle Valoc6999d82010-02-18 13:25:41 +02003228
3229 mutex_lock(&wl->mutex);
3230
3231 wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
3232
Kalle Valo4695dc92010-03-18 12:26:38 +02003233 if (params->uapsd)
3234 ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
3235 else
3236 ps_scheme = CONF_PS_SCHEME_LEGACY;
3237
Arik Nemtsov488fc542010-10-16 20:33:45 +02003238 if (wl->state == WL1271_STATE_OFF) {
3239 /*
3240 * If the state is off, the parameters will be recorded and
3241 * configured on init. This happens in AP-mode.
3242 */
3243 struct conf_tx_ac_category *conf_ac =
3244 &wl->conf.tx.ac_conf[wl1271_tx_get_queue(queue)];
3245 struct conf_tx_tid *conf_tid =
3246 &wl->conf.tx.tid_conf[wl1271_tx_get_queue(queue)];
3247
3248 conf_ac->ac = wl1271_tx_get_queue(queue);
3249 conf_ac->cw_min = (u8)params->cw_min;
3250 conf_ac->cw_max = params->cw_max;
3251 conf_ac->aifsn = params->aifs;
3252 conf_ac->tx_op_limit = params->txop << 5;
3253
3254 conf_tid->queue_id = wl1271_tx_get_queue(queue);
3255 conf_tid->channel_type = CONF_CHANNEL_TYPE_EDCF;
3256 conf_tid->tsid = wl1271_tx_get_queue(queue);
3257 conf_tid->ps_scheme = ps_scheme;
3258 conf_tid->ack_policy = CONF_ACK_POLICY_LEGACY;
3259 conf_tid->apsd_conf[0] = 0;
3260 conf_tid->apsd_conf[1] = 0;
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003261 goto out;
3262 }
Arik Nemtsov488fc542010-10-16 20:33:45 +02003263
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003264 ret = wl1271_ps_elp_wakeup(wl);
3265 if (ret < 0)
3266 goto out;
Arik Nemtsov488fc542010-10-16 20:33:45 +02003267
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003268 /*
3269 * the txop is confed in units of 32us by the mac80211,
3270 * we need us
3271 */
3272 ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
3273 params->cw_min, params->cw_max,
3274 params->aifs, params->txop << 5);
3275 if (ret < 0)
3276 goto out_sleep;
3277
3278 ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
3279 CONF_CHANNEL_TYPE_EDCF,
3280 wl1271_tx_get_queue(queue),
3281 ps_scheme, CONF_ACK_POLICY_LEGACY,
3282 0, 0);
Kalle Valoc82c1dd2010-02-18 13:25:47 +02003283
3284out_sleep:
Eliad Pellerc1b193e2011-03-23 22:22:15 +02003285 wl1271_ps_elp_sleep(wl);
Kalle Valoc6999d82010-02-18 13:25:41 +02003286
3287out:
3288 mutex_unlock(&wl->mutex);
3289
3290 return ret;
3291}
3292
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03003293static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw)
3294{
3295
3296 struct wl1271 *wl = hw->priv;
3297 u64 mactime = ULLONG_MAX;
3298 int ret;
3299
3300 wl1271_debug(DEBUG_MAC80211, "mac80211 get tsf");
3301
3302 mutex_lock(&wl->mutex);
3303
Juuso Oikarinenf8d98022010-10-26 13:24:39 +02003304 if (unlikely(wl->state == WL1271_STATE_OFF))
3305 goto out;
3306
Ido Yariva6208652011-03-01 15:14:41 +02003307 ret = wl1271_ps_elp_wakeup(wl);
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03003308 if (ret < 0)
3309 goto out;
3310
3311 ret = wl1271_acx_tsf_info(wl, &mactime);
3312 if (ret < 0)
3313 goto out_sleep;
3314
3315out_sleep:
3316 wl1271_ps_elp_sleep(wl);
3317
3318out:
3319 mutex_unlock(&wl->mutex);
3320 return mactime;
3321}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003322
John W. Linvilleece550d2010-07-28 16:41:06 -04003323static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
3324 struct survey_info *survey)
3325{
3326 struct wl1271 *wl = hw->priv;
3327 struct ieee80211_conf *conf = &hw->conf;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02003328
John W. Linvilleece550d2010-07-28 16:41:06 -04003329 if (idx != 0)
3330 return -ENOENT;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02003331
John W. Linvilleece550d2010-07-28 16:41:06 -04003332 survey->channel = conf->channel;
3333 survey->filled = SURVEY_INFO_NOISE_DBM;
3334 survey->noise = wl->noise;
Juuso Oikarinenb739a422010-10-26 13:24:38 +02003335
John W. Linvilleece550d2010-07-28 16:41:06 -04003336 return 0;
3337}
3338
Arik Nemtsov409622e2011-02-23 00:22:29 +02003339static int wl1271_allocate_sta(struct wl1271 *wl,
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003340 struct ieee80211_sta *sta,
3341 u8 *hlid)
3342{
3343 struct wl1271_station *wl_sta;
3344 int id;
3345
3346 id = find_first_zero_bit(wl->ap_hlid_map, AP_MAX_STATIONS);
3347 if (id >= AP_MAX_STATIONS) {
3348 wl1271_warning("could not allocate HLID - too much stations");
3349 return -EBUSY;
3350 }
3351
3352 wl_sta = (struct wl1271_station *)sta->drv_priv;
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003353 __set_bit(id, wl->ap_hlid_map);
3354 wl_sta->hlid = WL1271_AP_STA_HLID_START + id;
3355 *hlid = wl_sta->hlid;
Arik Nemtsovb622d992011-02-23 00:22:31 +02003356 memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003357 return 0;
3358}
3359
Arik Nemtsov409622e2011-02-23 00:22:29 +02003360static void wl1271_free_sta(struct wl1271 *wl, u8 hlid)
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003361{
3362 int id = hlid - WL1271_AP_STA_HLID_START;
3363
Arik Nemtsov409622e2011-02-23 00:22:29 +02003364 if (WARN_ON(!test_bit(id, wl->ap_hlid_map)))
3365 return;
3366
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003367 __clear_bit(id, wl->ap_hlid_map);
Arik Nemtsovb622d992011-02-23 00:22:31 +02003368 memset(wl->links[hlid].addr, 0, ETH_ALEN);
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02003369 wl1271_tx_reset_link_queues(wl, hlid);
Arik Nemtsovb622d992011-02-23 00:22:31 +02003370 __clear_bit(hlid, &wl->ap_ps_map);
3371 __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003372}
3373
3374static int wl1271_op_sta_add(struct ieee80211_hw *hw,
3375 struct ieee80211_vif *vif,
3376 struct ieee80211_sta *sta)
3377{
3378 struct wl1271 *wl = hw->priv;
3379 int ret = 0;
3380 u8 hlid;
3381
3382 mutex_lock(&wl->mutex);
3383
3384 if (unlikely(wl->state == WL1271_STATE_OFF))
3385 goto out;
3386
3387 if (wl->bss_type != BSS_TYPE_AP_BSS)
3388 goto out;
3389
3390 wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);
3391
Arik Nemtsov409622e2011-02-23 00:22:29 +02003392 ret = wl1271_allocate_sta(wl, sta, &hlid);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003393 if (ret < 0)
3394 goto out;
3395
Ido Yariva6208652011-03-01 15:14:41 +02003396 ret = wl1271_ps_elp_wakeup(wl);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003397 if (ret < 0)
Arik Nemtsov409622e2011-02-23 00:22:29 +02003398 goto out_free_sta;
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003399
3400 ret = wl1271_cmd_add_sta(wl, sta, hlid);
3401 if (ret < 0)
3402 goto out_sleep;
3403
3404out_sleep:
3405 wl1271_ps_elp_sleep(wl);
3406
Arik Nemtsov409622e2011-02-23 00:22:29 +02003407out_free_sta:
3408 if (ret < 0)
3409 wl1271_free_sta(wl, hlid);
3410
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003411out:
3412 mutex_unlock(&wl->mutex);
3413 return ret;
3414}
3415
3416static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
3417 struct ieee80211_vif *vif,
3418 struct ieee80211_sta *sta)
3419{
3420 struct wl1271 *wl = hw->priv;
3421 struct wl1271_station *wl_sta;
3422 int ret = 0, id;
3423
3424 mutex_lock(&wl->mutex);
3425
3426 if (unlikely(wl->state == WL1271_STATE_OFF))
3427 goto out;
3428
3429 if (wl->bss_type != BSS_TYPE_AP_BSS)
3430 goto out;
3431
3432 wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
3433
3434 wl_sta = (struct wl1271_station *)sta->drv_priv;
3435 id = wl_sta->hlid - WL1271_AP_STA_HLID_START;
3436 if (WARN_ON(!test_bit(id, wl->ap_hlid_map)))
3437 goto out;
3438
Ido Yariva6208652011-03-01 15:14:41 +02003439 ret = wl1271_ps_elp_wakeup(wl);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003440 if (ret < 0)
3441 goto out;
3442
3443 ret = wl1271_cmd_remove_sta(wl, wl_sta->hlid);
3444 if (ret < 0)
3445 goto out_sleep;
3446
Arik Nemtsov409622e2011-02-23 00:22:29 +02003447 wl1271_free_sta(wl, wl_sta->hlid);
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003448
3449out_sleep:
3450 wl1271_ps_elp_sleep(wl);
3451
3452out:
3453 mutex_unlock(&wl->mutex);
3454 return ret;
3455}
3456
Luciano Coelho4623ec72011-03-21 19:26:41 +02003457static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
3458 struct ieee80211_vif *vif,
3459 enum ieee80211_ampdu_mlme_action action,
3460 struct ieee80211_sta *sta, u16 tid, u16 *ssn,
3461 u8 buf_size)
Levi, Shaharbbba3e62011-01-23 07:27:23 +01003462{
3463 struct wl1271 *wl = hw->priv;
3464 int ret;
3465
3466 mutex_lock(&wl->mutex);
3467
3468 if (unlikely(wl->state == WL1271_STATE_OFF)) {
3469 ret = -EAGAIN;
3470 goto out;
3471 }
3472
Ido Yariva6208652011-03-01 15:14:41 +02003473 ret = wl1271_ps_elp_wakeup(wl);
Levi, Shaharbbba3e62011-01-23 07:27:23 +01003474 if (ret < 0)
3475 goto out;
3476
Shahar Levi70559a02011-05-22 16:10:22 +03003477 wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu: Rx tid %d action %d",
3478 tid, action);
3479
Levi, Shaharbbba3e62011-01-23 07:27:23 +01003480 switch (action) {
3481 case IEEE80211_AMPDU_RX_START:
Shahar Levi70559a02011-05-22 16:10:22 +03003482 if ((wl->ba_support) && (wl->ba_allowed)) {
Levi, Shaharbbba3e62011-01-23 07:27:23 +01003483 ret = wl1271_acx_set_ba_receiver_session(wl, tid, *ssn,
3484 true);
3485 if (!ret)
3486 wl->ba_rx_bitmap |= BIT(tid);
3487 } else {
3488 ret = -ENOTSUPP;
3489 }
3490 break;
3491
3492 case IEEE80211_AMPDU_RX_STOP:
3493 ret = wl1271_acx_set_ba_receiver_session(wl, tid, 0, false);
3494 if (!ret)
3495 wl->ba_rx_bitmap &= ~BIT(tid);
3496 break;
3497
3498 /*
3499 * The BA initiator session management in FW independently.
3500 * Falling break here on purpose for all TX APDU commands.
3501 */
3502 case IEEE80211_AMPDU_TX_START:
3503 case IEEE80211_AMPDU_TX_STOP:
3504 case IEEE80211_AMPDU_TX_OPERATIONAL:
3505 ret = -EINVAL;
3506 break;
3507
3508 default:
3509 wl1271_error("Incorrect ampdu action id=%x\n", action);
3510 ret = -EINVAL;
3511 }
3512
3513 wl1271_ps_elp_sleep(wl);
3514
3515out:
3516 mutex_unlock(&wl->mutex);
3517
3518 return ret;
3519}
3520
Arik Nemtsov33437892011-04-26 23:35:39 +03003521static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
3522{
3523 struct wl1271 *wl = hw->priv;
3524 bool ret = false;
3525
3526 mutex_lock(&wl->mutex);
3527
3528 if (unlikely(wl->state == WL1271_STATE_OFF))
3529 goto out;
3530
3531 /* packets are considered pending if in the TX queue or the FW */
3532 ret = (wl->tx_queue_count > 0) || (wl->tx_frames_cnt > 0);
3533
3534 /* the above is appropriate for STA mode for PS purposes */
3535 WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
3536
3537out:
3538 mutex_unlock(&wl->mutex);
3539
3540 return ret;
3541}
3542
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003543/* can't be const, mac80211 writes to this */
3544static struct ieee80211_rate wl1271_rates[] = {
3545 { .bitrate = 10,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03003546 .hw_value = CONF_HW_BIT_RATE_1MBPS,
3547 .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003548 { .bitrate = 20,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03003549 .hw_value = CONF_HW_BIT_RATE_2MBPS,
3550 .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003551 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
3552 { .bitrate = 55,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03003553 .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
3554 .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003555 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
3556 { .bitrate = 110,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03003557 .hw_value = CONF_HW_BIT_RATE_11MBPS,
3558 .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003559 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
3560 { .bitrate = 60,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03003561 .hw_value = CONF_HW_BIT_RATE_6MBPS,
3562 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003563 { .bitrate = 90,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03003564 .hw_value = CONF_HW_BIT_RATE_9MBPS,
3565 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003566 { .bitrate = 120,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03003567 .hw_value = CONF_HW_BIT_RATE_12MBPS,
3568 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003569 { .bitrate = 180,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03003570 .hw_value = CONF_HW_BIT_RATE_18MBPS,
3571 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003572 { .bitrate = 240,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03003573 .hw_value = CONF_HW_BIT_RATE_24MBPS,
3574 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003575 { .bitrate = 360,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03003576 .hw_value = CONF_HW_BIT_RATE_36MBPS,
3577 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003578 { .bitrate = 480,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03003579 .hw_value = CONF_HW_BIT_RATE_48MBPS,
3580 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003581 { .bitrate = 540,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03003582 .hw_value = CONF_HW_BIT_RATE_54MBPS,
3583 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003584};
3585
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01003586/* can't be const, mac80211 writes to this */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003587static struct ieee80211_channel wl1271_channels[] = {
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02003588 { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02003589 { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01003590 { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
3591 { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
3592 { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02003593 { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01003594 { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
3595 { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
3596 { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02003597 { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01003598 { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
3599 { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
3600 { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
Arik Nemtsov6c89b7b2011-01-18 20:39:52 +01003601 { .hw_value = 14, .center_freq = 2484, .max_power = 25 },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003602};
3603
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003604/* mapping to indexes for wl1271_rates */
Tobias Klausera0ea9492010-05-20 10:38:11 +02003605static const u8 wl1271_rate_to_idx_2ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003606 /* MCS rates are used only with 11n */
Shahar Levi18357852010-10-13 16:09:41 +02003607 7, /* CONF_HW_RXTX_RATE_MCS7 */
3608 6, /* CONF_HW_RXTX_RATE_MCS6 */
3609 5, /* CONF_HW_RXTX_RATE_MCS5 */
3610 4, /* CONF_HW_RXTX_RATE_MCS4 */
3611 3, /* CONF_HW_RXTX_RATE_MCS3 */
3612 2, /* CONF_HW_RXTX_RATE_MCS2 */
3613 1, /* CONF_HW_RXTX_RATE_MCS1 */
3614 0, /* CONF_HW_RXTX_RATE_MCS0 */
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003615
3616 11, /* CONF_HW_RXTX_RATE_54 */
3617 10, /* CONF_HW_RXTX_RATE_48 */
3618 9, /* CONF_HW_RXTX_RATE_36 */
3619 8, /* CONF_HW_RXTX_RATE_24 */
3620
3621 /* TI-specific rate */
3622 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
3623
3624 7, /* CONF_HW_RXTX_RATE_18 */
3625 6, /* CONF_HW_RXTX_RATE_12 */
3626 3, /* CONF_HW_RXTX_RATE_11 */
3627 5, /* CONF_HW_RXTX_RATE_9 */
3628 4, /* CONF_HW_RXTX_RATE_6 */
3629 2, /* CONF_HW_RXTX_RATE_5_5 */
3630 1, /* CONF_HW_RXTX_RATE_2 */
3631 0 /* CONF_HW_RXTX_RATE_1 */
3632};
3633
Shahar Levie8b03a22010-10-13 16:09:39 +02003634/* 11n STA capabilities */
3635#define HW_RX_HIGHEST_RATE 72
3636
Shahar Levi00d20102010-11-08 11:20:10 +00003637#ifdef CONFIG_WL12XX_HT
3638#define WL12XX_HT_CAP { \
Shahar Levi871d0c32011-03-13 11:24:40 +02003639 .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | \
3640 (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), \
Shahar Levie8b03a22010-10-13 16:09:39 +02003641 .ht_supported = true, \
3642 .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, \
3643 .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \
3644 .mcs = { \
3645 .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \
3646 .rx_highest = cpu_to_le16(HW_RX_HIGHEST_RATE), \
3647 .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \
3648 }, \
3649}
Shahar Levi18357852010-10-13 16:09:41 +02003650#else
Shahar Levi00d20102010-11-08 11:20:10 +00003651#define WL12XX_HT_CAP { \
Shahar Levi18357852010-10-13 16:09:41 +02003652 .ht_supported = false, \
3653}
3654#endif
Shahar Levie8b03a22010-10-13 16:09:39 +02003655
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003656/* can't be const, mac80211 writes to this */
3657static struct ieee80211_supported_band wl1271_band_2ghz = {
3658 .channels = wl1271_channels,
3659 .n_channels = ARRAY_SIZE(wl1271_channels),
3660 .bitrates = wl1271_rates,
3661 .n_bitrates = ARRAY_SIZE(wl1271_rates),
Shahar Levi00d20102010-11-08 11:20:10 +00003662 .ht_cap = WL12XX_HT_CAP,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003663};
3664
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03003665/* 5 GHz data rates for WL1273 */
3666static struct ieee80211_rate wl1271_rates_5ghz[] = {
3667 { .bitrate = 60,
3668 .hw_value = CONF_HW_BIT_RATE_6MBPS,
3669 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
3670 { .bitrate = 90,
3671 .hw_value = CONF_HW_BIT_RATE_9MBPS,
3672 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
3673 { .bitrate = 120,
3674 .hw_value = CONF_HW_BIT_RATE_12MBPS,
3675 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
3676 { .bitrate = 180,
3677 .hw_value = CONF_HW_BIT_RATE_18MBPS,
3678 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
3679 { .bitrate = 240,
3680 .hw_value = CONF_HW_BIT_RATE_24MBPS,
3681 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
3682 { .bitrate = 360,
3683 .hw_value = CONF_HW_BIT_RATE_36MBPS,
3684 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
3685 { .bitrate = 480,
3686 .hw_value = CONF_HW_BIT_RATE_48MBPS,
3687 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
3688 { .bitrate = 540,
3689 .hw_value = CONF_HW_BIT_RATE_54MBPS,
3690 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
3691};
3692
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01003693/* 5 GHz band channels for WL1273 */
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03003694static struct ieee80211_channel wl1271_channels_5ghz[] = {
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02003695 { .hw_value = 7, .center_freq = 5035},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01003696 { .hw_value = 8, .center_freq = 5040},
3697 { .hw_value = 9, .center_freq = 5045},
3698 { .hw_value = 11, .center_freq = 5055},
3699 { .hw_value = 12, .center_freq = 5060},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02003700 { .hw_value = 16, .center_freq = 5080},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01003701 { .hw_value = 34, .center_freq = 5170},
3702 { .hw_value = 36, .center_freq = 5180},
3703 { .hw_value = 38, .center_freq = 5190},
3704 { .hw_value = 40, .center_freq = 5200},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02003705 { .hw_value = 42, .center_freq = 5210},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01003706 { .hw_value = 44, .center_freq = 5220},
3707 { .hw_value = 46, .center_freq = 5230},
3708 { .hw_value = 48, .center_freq = 5240},
3709 { .hw_value = 52, .center_freq = 5260},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02003710 { .hw_value = 56, .center_freq = 5280},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01003711 { .hw_value = 60, .center_freq = 5300},
3712 { .hw_value = 64, .center_freq = 5320},
3713 { .hw_value = 100, .center_freq = 5500},
3714 { .hw_value = 104, .center_freq = 5520},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02003715 { .hw_value = 108, .center_freq = 5540},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01003716 { .hw_value = 112, .center_freq = 5560},
3717 { .hw_value = 116, .center_freq = 5580},
3718 { .hw_value = 120, .center_freq = 5600},
3719 { .hw_value = 124, .center_freq = 5620},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02003720 { .hw_value = 128, .center_freq = 5640},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01003721 { .hw_value = 132, .center_freq = 5660},
3722 { .hw_value = 136, .center_freq = 5680},
3723 { .hw_value = 140, .center_freq = 5700},
3724 { .hw_value = 149, .center_freq = 5745},
Juuso Oikarinenfa21c7a2010-08-10 08:22:02 +02003725 { .hw_value = 153, .center_freq = 5765},
Juuso Oikarinenfa97f462010-11-10 11:27:20 +01003726 { .hw_value = 157, .center_freq = 5785},
3727 { .hw_value = 161, .center_freq = 5805},
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03003728 { .hw_value = 165, .center_freq = 5825},
3729};
3730
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003731/* mapping to indexes for wl1271_rates_5ghz */
Tobias Klausera0ea9492010-05-20 10:38:11 +02003732static const u8 wl1271_rate_to_idx_5ghz[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003733 /* MCS rates are used only with 11n */
Shahar Levi18357852010-10-13 16:09:41 +02003734 7, /* CONF_HW_RXTX_RATE_MCS7 */
3735 6, /* CONF_HW_RXTX_RATE_MCS6 */
3736 5, /* CONF_HW_RXTX_RATE_MCS5 */
3737 4, /* CONF_HW_RXTX_RATE_MCS4 */
3738 3, /* CONF_HW_RXTX_RATE_MCS3 */
3739 2, /* CONF_HW_RXTX_RATE_MCS2 */
3740 1, /* CONF_HW_RXTX_RATE_MCS1 */
3741 0, /* CONF_HW_RXTX_RATE_MCS0 */
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003742
3743 7, /* CONF_HW_RXTX_RATE_54 */
3744 6, /* CONF_HW_RXTX_RATE_48 */
3745 5, /* CONF_HW_RXTX_RATE_36 */
3746 4, /* CONF_HW_RXTX_RATE_24 */
3747
3748 /* TI-specific rate */
3749 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
3750
3751 3, /* CONF_HW_RXTX_RATE_18 */
3752 2, /* CONF_HW_RXTX_RATE_12 */
3753 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_11 */
3754 1, /* CONF_HW_RXTX_RATE_9 */
3755 0, /* CONF_HW_RXTX_RATE_6 */
3756 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_5_5 */
3757 CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_2 */
3758 CONF_HW_RXTX_RATE_UNSUPPORTED /* CONF_HW_RXTX_RATE_1 */
3759};
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03003760
3761static struct ieee80211_supported_band wl1271_band_5ghz = {
3762 .channels = wl1271_channels_5ghz,
3763 .n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
3764 .bitrates = wl1271_rates_5ghz,
3765 .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
Shahar Levi00d20102010-11-08 11:20:10 +00003766 .ht_cap = WL12XX_HT_CAP,
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03003767};
3768
Tobias Klausera0ea9492010-05-20 10:38:11 +02003769static const u8 *wl1271_band_rate_to_idx[] = {
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003770 [IEEE80211_BAND_2GHZ] = wl1271_rate_to_idx_2ghz,
3771 [IEEE80211_BAND_5GHZ] = wl1271_rate_to_idx_5ghz
3772};
3773
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003774static const struct ieee80211_ops wl1271_ops = {
3775 .start = wl1271_op_start,
3776 .stop = wl1271_op_stop,
3777 .add_interface = wl1271_op_add_interface,
3778 .remove_interface = wl1271_op_remove_interface,
Luciano Coelhof634a4e2011-05-18 16:51:26 -04003779#ifdef CONFIG_PM
Eliad Peller402e48612011-05-13 11:57:09 +03003780 .suspend = wl1271_op_suspend,
3781 .resume = wl1271_op_resume,
Luciano Coelhof634a4e2011-05-18 16:51:26 -04003782#endif
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003783 .config = wl1271_op_config,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03003784 .prepare_multicast = wl1271_op_prepare_multicast,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003785 .configure_filter = wl1271_op_configure_filter,
3786 .tx = wl1271_op_tx,
3787 .set_key = wl1271_op_set_key,
3788 .hw_scan = wl1271_op_hw_scan,
Luciano Coelho33c2c062011-05-10 14:46:02 +03003789 .sched_scan_start = wl1271_op_sched_scan_start,
3790 .sched_scan_stop = wl1271_op_sched_scan_stop,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003791 .bss_info_changed = wl1271_op_bss_info_changed,
Arik Nemtsov68d069c2010-11-08 10:51:07 +01003792 .set_frag_threshold = wl1271_op_set_frag_threshold,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003793 .set_rts_threshold = wl1271_op_set_rts_threshold,
Kalle Valoc6999d82010-02-18 13:25:41 +02003794 .conf_tx = wl1271_op_conf_tx,
Juuso Oikarinenbbbb5382010-07-08 17:49:57 +03003795 .get_tsf = wl1271_op_get_tsf,
John W. Linvilleece550d2010-07-28 16:41:06 -04003796 .get_survey = wl1271_op_get_survey,
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02003797 .sta_add = wl1271_op_sta_add,
3798 .sta_remove = wl1271_op_sta_remove,
Levi, Shaharbbba3e62011-01-23 07:27:23 +01003799 .ampdu_action = wl1271_op_ampdu_action,
Arik Nemtsov33437892011-04-26 23:35:39 +03003800 .tx_frames_pending = wl1271_tx_frames_pending,
Kalle Valoc8c90872010-02-18 13:25:53 +02003801 CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003802};
3803
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003804
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02003805u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band)
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003806{
3807 u8 idx;
3808
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02003809 BUG_ON(band >= sizeof(wl1271_band_rate_to_idx)/sizeof(u8 *));
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003810
3811 if (unlikely(rate >= CONF_HW_RXTX_RATE_MAX)) {
3812 wl1271_error("Illegal RX rate from HW: %d", rate);
3813 return 0;
3814 }
3815
Teemu Paasikivi6a2de932010-10-14 11:00:04 +02003816 idx = wl1271_band_rate_to_idx[band][rate];
Juuso Oikarinenf876bb92010-03-26 12:53:11 +02003817 if (unlikely(idx == CONF_HW_RXTX_RATE_UNSUPPORTED)) {
3818 wl1271_error("Unsupported RX rate from HW: %d", rate);
3819 return 0;
3820 }
3821
3822 return idx;
3823}
3824
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003825static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
3826 struct device_attribute *attr,
3827 char *buf)
3828{
3829 struct wl1271 *wl = dev_get_drvdata(dev);
3830 ssize_t len;
3831
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02003832 len = PAGE_SIZE;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003833
3834 mutex_lock(&wl->mutex);
3835 len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
3836 wl->sg_enabled);
3837 mutex_unlock(&wl->mutex);
3838
3839 return len;
3840
3841}
3842
3843static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
3844 struct device_attribute *attr,
3845 const char *buf, size_t count)
3846{
3847 struct wl1271 *wl = dev_get_drvdata(dev);
3848 unsigned long res;
3849 int ret;
3850
Luciano Coelho6277ed62011-04-01 17:49:54 +03003851 ret = kstrtoul(buf, 10, &res);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003852 if (ret < 0) {
3853 wl1271_warning("incorrect value written to bt_coex_mode");
3854 return count;
3855 }
3856
3857 mutex_lock(&wl->mutex);
3858
3859 res = !!res;
3860
3861 if (res == wl->sg_enabled)
3862 goto out;
3863
3864 wl->sg_enabled = res;
3865
3866 if (wl->state == WL1271_STATE_OFF)
3867 goto out;
3868
Ido Yariva6208652011-03-01 15:14:41 +02003869 ret = wl1271_ps_elp_wakeup(wl);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02003870 if (ret < 0)
3871 goto out;
3872
3873 wl1271_acx_sg_enable(wl, wl->sg_enabled);
3874 wl1271_ps_elp_sleep(wl);
3875
3876 out:
3877 mutex_unlock(&wl->mutex);
3878 return count;
3879}
3880
3881static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
3882 wl1271_sysfs_show_bt_coex_state,
3883 wl1271_sysfs_store_bt_coex_state);
3884
Juuso Oikarinend717fd62010-05-07 11:38:58 +03003885static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
3886 struct device_attribute *attr,
3887 char *buf)
3888{
3889 struct wl1271 *wl = dev_get_drvdata(dev);
3890 ssize_t len;
3891
Juuso Oikarinen2f63b012010-08-10 06:38:35 +02003892 len = PAGE_SIZE;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03003893
3894 mutex_lock(&wl->mutex);
3895 if (wl->hw_pg_ver >= 0)
3896 len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
3897 else
3898 len = snprintf(buf, len, "n/a\n");
3899 mutex_unlock(&wl->mutex);
3900
3901 return len;
3902}
3903
3904static DEVICE_ATTR(hw_pg_ver, S_IRUGO | S_IWUSR,
3905 wl1271_sysfs_show_hw_pg_ver, NULL);
3906
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02003907int wl1271_register_hw(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003908{
3909 int ret;
3910
3911 if (wl->mac80211_registered)
3912 return 0;
3913
Arik Nemtsov31d26ec2010-10-16 21:49:52 +02003914 ret = wl1271_fetch_nvs(wl);
3915 if (ret == 0) {
Shahar Levibc765bf2011-03-06 16:32:10 +02003916 /* NOTE: The wl->nvs->nvs element must be first, in
3917 * order to simplify the casting, we assume it is at
3918 * the beginning of the wl->nvs structure.
3919 */
3920 u8 *nvs_ptr = (u8 *)wl->nvs;
Arik Nemtsov31d26ec2010-10-16 21:49:52 +02003921
3922 wl->mac_addr[0] = nvs_ptr[11];
3923 wl->mac_addr[1] = nvs_ptr[10];
3924 wl->mac_addr[2] = nvs_ptr[6];
3925 wl->mac_addr[3] = nvs_ptr[5];
3926 wl->mac_addr[4] = nvs_ptr[4];
3927 wl->mac_addr[5] = nvs_ptr[3];
3928 }
3929
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003930 SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
3931
3932 ret = ieee80211_register_hw(wl->hw);
3933 if (ret < 0) {
3934 wl1271_error("unable to register mac80211 hw: %d", ret);
3935 return ret;
3936 }
3937
3938 wl->mac80211_registered = true;
3939
Eliad Pellerd60080a2010-11-24 12:53:16 +02003940 wl1271_debugfs_init(wl);
3941
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03003942 register_netdevice_notifier(&wl1271_dev_notifier);
3943
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003944 wl1271_notice("loaded");
3945
3946 return 0;
3947}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02003948EXPORT_SYMBOL_GPL(wl1271_register_hw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003949
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003950void wl1271_unregister_hw(struct wl1271 *wl)
3951{
Juuso Oikarinen4ae3fa82011-01-14 12:48:46 +01003952 if (wl->state == WL1271_STATE_PLT)
3953 __wl1271_plt_stop(wl);
3954
Juuso Oikarinenc2c192a2010-07-27 03:30:09 +03003955 unregister_netdevice_notifier(&wl1271_dev_notifier);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02003956 ieee80211_unregister_hw(wl->hw);
3957 wl->mac80211_registered = false;
3958
3959}
3960EXPORT_SYMBOL_GPL(wl1271_unregister_hw);
3961
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02003962int wl1271_init_ieee80211(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003963{
Juuso Oikarinen7a557242010-09-27 12:42:07 +02003964 static const u32 cipher_suites[] = {
3965 WLAN_CIPHER_SUITE_WEP40,
3966 WLAN_CIPHER_SUITE_WEP104,
3967 WLAN_CIPHER_SUITE_TKIP,
3968 WLAN_CIPHER_SUITE_CCMP,
3969 WL1271_CIPHER_SUITE_GEM,
3970 };
3971
Juuso Oikarinen1e2b7972009-10-08 21:56:20 +03003972 /* The tx descriptor buffer and the TKIP space. */
3973 wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
3974 sizeof(struct wl1271_tx_hw_descr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003975
3976 /* unit us */
3977 /* FIXME: find a proper value */
3978 wl->hw->channel_change_time = 10000;
Juuso Oikarinen50c500a2010-04-01 11:38:22 +03003979 wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003980
3981 wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
Juuso Oikarinen03442a32009-11-23 23:22:14 +02003982 IEEE80211_HW_BEACON_FILTER |
Juuso Oikarinen0a343322010-02-22 08:38:41 +02003983 IEEE80211_HW_SUPPORTS_PS |
Kalle Valo4695dc92010-03-18 12:26:38 +02003984 IEEE80211_HW_SUPPORTS_UAPSD |
Juuso Oikarinena9af0922010-03-26 12:53:30 +02003985 IEEE80211_HW_HAS_RATE_CONTROL |
Juuso Oikarinen00236aed2010-04-09 11:07:30 +03003986 IEEE80211_HW_CONNECTION_MONITOR |
Eliad Peller62c07402011-02-02 11:20:05 +02003987 IEEE80211_HW_SUPPORTS_CQM_RSSI |
Luciano Coelho25eaea302011-05-02 12:37:33 +03003988 IEEE80211_HW_REPORTS_TX_ACK_STATUS |
Shahar Levifcd23b62011-05-11 12:12:56 +03003989 IEEE80211_HW_SPECTRUM_MGMT |
Arik Nemtsovba7c0822011-02-23 00:22:28 +02003990 IEEE80211_HW_AP_LINK_PS;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003991
Juuso Oikarinen7a557242010-09-27 12:42:07 +02003992 wl->hw->wiphy->cipher_suites = cipher_suites;
3993 wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
3994
Juuso Oikarinene0d8bbf2009-12-11 15:41:04 +02003995 wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
Arik Nemtsov038d9252010-10-16 21:53:24 +02003996 BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03003997 wl->hw->wiphy->max_scan_ssids = 1;
Guy Eilamea559b42010-12-09 16:54:59 +02003998 /*
3999 * Maximum length of elements in scanning probe request templates
4000 * should be the maximum length possible for a template, without
4001 * the IEEE80211 header of the template
4002 */
4003 wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
4004 sizeof(struct ieee80211_header);
Luciano Coelhoa8aaaf52011-01-11 18:25:18 +01004005
Luciano Coelho4a31c112011-03-21 23:16:14 +02004006 /* make sure all our channels fit in the scanned_ch bitmask */
4007 BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
4008 ARRAY_SIZE(wl1271_channels_5ghz) >
4009 WL1271_MAX_CHANNELS);
Luciano Coelhoa8aaaf52011-01-11 18:25:18 +01004010 /*
4011 * We keep local copies of the band structs because we need to
4012 * modify them on a per-device basis.
4013 */
4014 memcpy(&wl->bands[IEEE80211_BAND_2GHZ], &wl1271_band_2ghz,
4015 sizeof(wl1271_band_2ghz));
4016 memcpy(&wl->bands[IEEE80211_BAND_5GHZ], &wl1271_band_5ghz,
4017 sizeof(wl1271_band_5ghz));
4018
4019 wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
4020 &wl->bands[IEEE80211_BAND_2GHZ];
4021 wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
4022 &wl->bands[IEEE80211_BAND_5GHZ];
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03004023
Kalle Valo12bd8942010-03-18 12:26:33 +02004024 wl->hw->queues = 4;
Juuso Oikarinen31627dc2010-03-26 12:53:12 +02004025 wl->hw->max_rates = 1;
Kalle Valo12bd8942010-03-18 12:26:33 +02004026
Juuso Oikarinenb7417d92010-11-10 11:27:19 +01004027 wl->hw->wiphy->reg_notifier = wl1271_reg_notify;
4028
Teemu Paasikivi8197b712010-02-22 08:38:23 +02004029 SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004030
Arik Nemtsovf84f7d72010-10-16 20:21:23 +02004031 wl->hw->sta_data_size = sizeof(struct wl1271_station);
4032
Luciano Coelho4c9cfa72011-01-12 14:27:03 +01004033 wl->hw->max_rx_aggregation_subframes = 8;
4034
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004035 return 0;
4036}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02004037EXPORT_SYMBOL_GPL(wl1271_init_ieee80211);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004038
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004039#define WL1271_DEFAULT_CHANNEL 0
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02004040
Teemu Paasikivi2d5e82b2010-02-22 08:38:21 +02004041struct ieee80211_hw *wl1271_alloc_hw(void)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004042{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004043 struct ieee80211_hw *hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004044 struct platform_device *plat_dev = NULL;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004045 struct wl1271 *wl;
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02004046 int i, j, ret;
Ido Yariv1f37cbc2010-09-30 13:28:27 +02004047 unsigned int order;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004048
4049 hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
4050 if (!hw) {
4051 wl1271_error("could not alloc ieee80211_hw");
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02004052 ret = -ENOMEM;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004053 goto err_hw_alloc;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004054 }
4055
Julia Lawall929ebd32010-05-15 23:16:39 +02004056 plat_dev = kmemdup(&wl1271_device, sizeof(wl1271_device), GFP_KERNEL);
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004057 if (!plat_dev) {
4058 wl1271_error("could not allocate platform_device");
4059 ret = -ENOMEM;
4060 goto err_plat_alloc;
4061 }
4062
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004063 wl = hw->priv;
4064 memset(wl, 0, sizeof(*wl));
4065
Juuso Oikarinen01c09162009-10-13 12:47:55 +03004066 INIT_LIST_HEAD(&wl->list);
4067
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004068 wl->hw = hw;
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004069 wl->plat_dev = plat_dev;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004070
Juuso Oikarinen6742f552010-12-13 09:52:37 +02004071 for (i = 0; i < NUM_TX_QUEUES; i++)
4072 skb_queue_head_init(&wl->tx_queue[i]);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004073
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02004074 for (i = 0; i < NUM_TX_QUEUES; i++)
4075 for (j = 0; j < AP_MAX_LINKS; j++)
4076 skb_queue_head_init(&wl->links[j].tx_queue[i]);
4077
Ido Yariva6208652011-03-01 15:14:41 +02004078 skb_queue_head_init(&wl->deferred_rx_queue);
4079 skb_queue_head_init(&wl->deferred_tx_queue);
4080
Juuso Oikarinen37b70a82009-10-08 21:56:21 +03004081 INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
Juuso Oikarinen90494a92010-07-08 17:50:00 +03004082 INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work);
Ido Yariva6208652011-03-01 15:14:41 +02004083 INIT_WORK(&wl->netstack_work, wl1271_netstack_work);
Juuso Oikarinen117b38d2010-09-30 10:43:28 +02004084 INIT_WORK(&wl->tx_work, wl1271_tx_work);
4085 INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
4086 INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
Eliad Peller77ddaa12011-05-15 11:10:29 +03004087 INIT_WORK(&wl->rx_streaming_enable_work,
4088 wl1271_rx_streaming_enable_work);
4089 INIT_WORK(&wl->rx_streaming_disable_work,
4090 wl1271_rx_streaming_disable_work);
4091
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004092 wl->channel = WL1271_DEFAULT_CHANNEL;
Juuso Oikarinen60e84c22010-03-26 12:53:25 +02004093 wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004094 wl->default_key = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004095 wl->rx_counter = 0;
Arik Nemtsovae113b52010-10-16 18:45:07 +02004096 wl->rx_config = WL1271_DEFAULT_STA_RX_CONFIG;
4097 wl->rx_filter = WL1271_DEFAULT_STA_RX_FILTER;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02004098 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004099 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
Juuso Oikarinena6fe2312009-12-11 15:40:58 +02004100 wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinenebba60c2010-04-01 11:38:20 +03004101 wl->basic_rate = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02004102 wl->rate_set = CONF_TX_RATE_MASK_BASIC;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03004103 wl->band = IEEE80211_BAND_2GHZ;
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03004104 wl->vif = NULL;
Juuso Oikarinen830fb672009-12-11 15:41:06 +02004105 wl->flags = 0;
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02004106 wl->sg_enabled = true;
Juuso Oikarinend717fd62010-05-07 11:38:58 +03004107 wl->hw_pg_ver = -1;
Arik Nemtsov166d5042010-10-16 21:44:57 +02004108 wl->bss_type = MAX_BSS_TYPE;
4109 wl->set_bss_type = MAX_BSS_TYPE;
4110 wl->fw_bss_type = MAX_BSS_TYPE;
Arik Nemtsova8c0ddb2011-02-23 00:22:26 +02004111 wl->last_tx_hlid = 0;
Arik Nemtsovb622d992011-02-23 00:22:31 +02004112 wl->ap_ps_map = 0;
4113 wl->ap_fw_ps_map = 0;
Ido Yariv606ea9f2011-03-01 15:14:39 +02004114 wl->quirks = 0;
Ido Yariv341b7cd2011-03-31 10:07:01 +02004115 wl->platform_quirks = 0;
Luciano Coelho33c2c062011-05-10 14:46:02 +03004116 wl->sched_scanning = false;
Eliad Peller77ddaa12011-05-15 11:10:29 +03004117 setup_timer(&wl->rx_streaming_timer, wl1271_rx_streaming_timer,
4118 (unsigned long) wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004119
Ido Yariv25eeb9e2010-10-12 16:20:06 +02004120 memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
Juuso Oikarinenbe7078c2009-10-08 21:56:26 +03004121 for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004122 wl->tx_frames[i] = NULL;
4123
4124 spin_lock_init(&wl->wl_lock);
4125
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03004126 wl->state = WL1271_STATE_OFF;
4127 mutex_init(&wl->mutex);
4128
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02004129 /* Apply default driver configuration. */
4130 wl1271_conf_init(wl);
4131
Ido Yariv1f37cbc2010-09-30 13:28:27 +02004132 order = get_order(WL1271_AGGR_BUFFER_SIZE);
4133 wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
4134 if (!wl->aggr_buf) {
4135 ret = -ENOMEM;
4136 goto err_hw;
4137 }
4138
Ido Yariv990f5de2011-03-31 10:06:59 +02004139 wl->dummy_packet = wl12xx_alloc_dummy_packet(wl);
4140 if (!wl->dummy_packet) {
4141 ret = -ENOMEM;
4142 goto err_aggr;
4143 }
4144
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02004145 /* Register platform device */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004146 ret = platform_device_register(wl->plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02004147 if (ret) {
4148 wl1271_error("couldn't register platform device");
Ido Yariv990f5de2011-03-31 10:06:59 +02004149 goto err_dummy_packet;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02004150 }
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004151 dev_set_drvdata(&wl->plat_dev->dev, wl);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02004152
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02004153 /* Create sysfs file to control bt coex state */
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004154 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02004155 if (ret < 0) {
4156 wl1271_error("failed to create sysfs file bt_coex_state");
4157 goto err_platform;
4158 }
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02004159
Juuso Oikarinend717fd62010-05-07 11:38:58 +03004160 /* Create sysfs file to get HW PG version */
4161 ret = device_create_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
4162 if (ret < 0) {
4163 wl1271_error("failed to create sysfs file hw_pg_ver");
4164 goto err_bt_coex_state;
4165 }
4166
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02004167 return hw;
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02004168
Juuso Oikarinend717fd62010-05-07 11:38:58 +03004169err_bt_coex_state:
4170 device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
4171
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02004172err_platform:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004173 platform_device_unregister(wl->plat_dev);
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +02004174
Ido Yariv990f5de2011-03-31 10:06:59 +02004175err_dummy_packet:
4176 dev_kfree_skb(wl->dummy_packet);
4177
Ido Yariv1f37cbc2010-09-30 13:28:27 +02004178err_aggr:
4179 free_pages((unsigned long)wl->aggr_buf, order);
4180
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02004181err_hw:
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004182 wl1271_debugfs_exit(wl);
4183 kfree(plat_dev);
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02004184
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004185err_plat_alloc:
4186 ieee80211_free_hw(hw);
4187
4188err_hw_alloc:
4189
Juuso Oikarinena1dd8182010-03-18 12:26:31 +02004190 return ERR_PTR(ret);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02004191}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02004192EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02004193
4194int wl1271_free_hw(struct wl1271 *wl)
4195{
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004196 platform_device_unregister(wl->plat_dev);
Ido Yariv990f5de2011-03-31 10:06:59 +02004197 dev_kfree_skb(wl->dummy_packet);
Ido Yariv1f37cbc2010-09-30 13:28:27 +02004198 free_pages((unsigned long)wl->aggr_buf,
4199 get_order(WL1271_AGGR_BUFFER_SIZE));
Teemu Paasikivi3b56dd62010-03-18 12:26:46 +02004200 kfree(wl->plat_dev);
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02004201
4202 wl1271_debugfs_exit(wl);
4203
Teemu Paasikivic332a4b2010-02-18 13:25:57 +02004204 vfree(wl->fw);
4205 wl->fw = NULL;
4206 kfree(wl->nvs);
4207 wl->nvs = NULL;
4208
4209 kfree(wl->fw_status);
4210 kfree(wl->tx_res_if);
4211
4212 ieee80211_free_hw(wl->hw);
4213
4214 return 0;
4215}
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02004216EXPORT_SYMBOL_GPL(wl1271_free_hw);
4217
Guy Eilam491bbd62011-01-12 10:33:29 +01004218u32 wl12xx_debug_level = DEBUG_NONE;
Eliad Peller17c17552010-12-12 12:15:35 +02004219EXPORT_SYMBOL_GPL(wl12xx_debug_level);
Guy Eilam491bbd62011-01-12 10:33:29 +01004220module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
Eliad Peller17c17552010-12-12 12:15:35 +02004221MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
4222
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02004223MODULE_LICENSE("GPL");
Luciano Coelhob1a48ca2011-02-22 14:19:28 +02004224MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
Teemu Paasikivi50b3eb42010-02-22 08:38:26 +02004225MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");