blob: bf1d0fd93bcc8ead14a1e5c0c6fdf31912771ba1 [file] [log] [blame]
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001/*
2 * This file is part of wl1271
3 *
4 * Copyright (C) 2008-2009 Nokia Corporation
5 *
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>
25#include <linux/platform_device.h>
26#include <linux/interrupt.h>
27#include <linux/firmware.h>
28#include <linux/delay.h>
29#include <linux/irq.h>
30#include <linux/spi/spi.h>
31#include <linux/crc32.h>
32#include <linux/etherdevice.h>
Juuso Oikarinen1fba4972009-10-08 21:56:32 +030033#include <linux/vmalloc.h>
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030034#include <linux/spi/wl12xx.h>
Juuso Oikarinen01c09162009-10-13 12:47:55 +030035#include <linux/inetdevice.h>
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030036
37#include "wl1271.h"
38#include "wl12xx_80211.h"
39#include "wl1271_reg.h"
40#include "wl1271_spi.h"
41#include "wl1271_event.h"
42#include "wl1271_tx.h"
43#include "wl1271_rx.h"
44#include "wl1271_ps.h"
45#include "wl1271_init.h"
46#include "wl1271_debugfs.h"
47#include "wl1271_cmd.h"
48#include "wl1271_boot.h"
49
Juuso Oikarinen8a080482009-10-13 12:47:44 +030050static struct conf_drv_settings default_conf = {
51 .sg = {
52 .per_threshold = 7500,
53 .max_scan_compensation_time = 120000,
54 .nfs_sample_interval = 400,
55 .load_ratio = 50,
56 .auto_ps_mode = 0,
57 .probe_req_compensation = 170,
58 .scan_window_compensation = 50,
59 .antenna_config = 0,
60 .beacon_miss_threshold = 60,
61 .rate_adaptation_threshold = CONF_HW_BIT_RATE_12MBPS,
62 .rate_adaptation_snr = 0
63 },
64 .rx = {
65 .rx_msdu_life_time = 512000,
66 .packet_detection_threshold = 0,
67 .ps_poll_timeout = 15,
68 .upsd_timeout = 15,
69 .rts_threshold = 2347,
70 .rx_cca_threshold = 0xFFEF,
71 .irq_blk_threshold = 0,
72 .irq_pkt_threshold = USHORT_MAX,
73 .irq_timeout = 5,
74 .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY,
75 },
76 .tx = {
77 .tx_energy_detection = 0,
78 .rc_conf = {
79 .enabled_rates = CONF_TX_RATE_MASK_UNSPECIFIED,
80 .short_retry_limit = 10,
81 .long_retry_limit = 10,
82 .aflags = 0
83 },
84 .ac_conf_count = 4,
85 .ac_conf = {
86 [0] = {
87 .ac = CONF_TX_AC_BE,
88 .cw_min = 15,
89 .cw_max = 63,
90 .aifsn = 3,
91 .tx_op_limit = 0,
92 },
93 [1] = {
94 .ac = CONF_TX_AC_BK,
95 .cw_min = 15,
96 .cw_max = 63,
97 .aifsn = 7,
98 .tx_op_limit = 0,
99 },
100 [2] = {
101 .ac = CONF_TX_AC_VI,
102 .cw_min = 15,
103 .cw_max = 63,
104 .aifsn = CONF_TX_AIFS_PIFS,
105 .tx_op_limit = 3008,
106 },
107 [3] = {
108 .ac = CONF_TX_AC_VO,
109 .cw_min = 15,
110 .cw_max = 63,
111 .aifsn = CONF_TX_AIFS_PIFS,
112 .tx_op_limit = 1504,
113 },
114 },
115 .tid_conf_count = 7,
116 .tid_conf = {
117 [0] = {
118 .queue_id = 0,
119 .channel_type = CONF_CHANNEL_TYPE_DCF,
120 .tsid = CONF_TX_AC_BE,
121 .ps_scheme = CONF_PS_SCHEME_LEGACY,
122 .ack_policy = CONF_ACK_POLICY_LEGACY,
123 .apsd_conf = {0, 0},
124 },
125 [1] = {
126 .queue_id = 1,
127 .channel_type = CONF_CHANNEL_TYPE_DCF,
128 .tsid = CONF_TX_AC_BE,
129 .ps_scheme = CONF_PS_SCHEME_LEGACY,
130 .ack_policy = CONF_ACK_POLICY_LEGACY,
131 .apsd_conf = {0, 0},
132 },
133 [2] = {
134 .queue_id = 2,
135 .channel_type = CONF_CHANNEL_TYPE_DCF,
136 .tsid = CONF_TX_AC_BE,
137 .ps_scheme = CONF_PS_SCHEME_LEGACY,
138 .ack_policy = CONF_ACK_POLICY_LEGACY,
139 .apsd_conf = {0, 0},
140 },
141 [3] = {
142 .queue_id = 3,
143 .channel_type = CONF_CHANNEL_TYPE_DCF,
144 .tsid = CONF_TX_AC_BE,
145 .ps_scheme = CONF_PS_SCHEME_LEGACY,
146 .ack_policy = CONF_ACK_POLICY_LEGACY,
147 .apsd_conf = {0, 0},
148 },
149 [4] = {
150 .queue_id = 4,
151 .channel_type = CONF_CHANNEL_TYPE_DCF,
152 .tsid = CONF_TX_AC_BE,
153 .ps_scheme = CONF_PS_SCHEME_LEGACY,
154 .ack_policy = CONF_ACK_POLICY_LEGACY,
155 .apsd_conf = {0, 0},
156 },
157 [5] = {
158 .queue_id = 5,
159 .channel_type = CONF_CHANNEL_TYPE_DCF,
160 .tsid = CONF_TX_AC_BE,
161 .ps_scheme = CONF_PS_SCHEME_LEGACY,
162 .ack_policy = CONF_ACK_POLICY_LEGACY,
163 .apsd_conf = {0, 0},
164 },
165 [6] = {
166 .queue_id = 6,
167 .channel_type = CONF_CHANNEL_TYPE_DCF,
168 .tsid = CONF_TX_AC_BE,
169 .ps_scheme = CONF_PS_SCHEME_LEGACY,
170 .ack_policy = CONF_ACK_POLICY_LEGACY,
171 .apsd_conf = {0, 0},
172 }
173 },
174 .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD,
175 .tx_compl_timeout = 5,
176 .tx_compl_threshold = 5
177 },
178 .conn = {
179 .wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
180 .listen_interval = 0,
181 .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED,
182 .bcn_filt_ie_count = 1,
183 .bcn_filt_ie = {
184 [0] = {
185 .ie = WLAN_EID_CHANNEL_SWITCH,
186 .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE,
187 }
188 },
189 .synch_fail_thold = 5,
190 .bss_lose_timeout = 100,
191 .beacon_rx_timeout = 10000,
192 .broadcast_timeout = 20000,
193 .rx_broadcast_in_ps = 1,
194 .ps_poll_threshold = 4,
195 .sig_trigger_count = 2,
196 .sig_trigger = {
197 [0] = {
198 .threshold = -75,
199 .pacing = 500,
200 .metric = CONF_TRIG_METRIC_RSSI_BEACON,
201 .type = CONF_TRIG_EVENT_TYPE_EDGE,
202 .direction = CONF_TRIG_EVENT_DIR_LOW,
203 .hysteresis = 2,
204 .index = 0,
205 .enable = 1
206 },
207 [1] = {
208 .threshold = -75,
209 .pacing = 500,
210 .metric = CONF_TRIG_METRIC_RSSI_BEACON,
211 .type = CONF_TRIG_EVENT_TYPE_EDGE,
212 .direction = CONF_TRIG_EVENT_DIR_HIGH,
213 .hysteresis = 2,
214 .index = 1,
215 .enable = 1
216 }
217 },
218 .sig_weights = {
219 .rssi_bcn_avg_weight = 10,
220 .rssi_pkt_avg_weight = 10,
221 .snr_bcn_avg_weight = 10,
222 .snr_pkt_avg_weight = 10
Juuso Oikarinen11f70f92009-10-13 12:47:46 +0300223 },
224 .bet_enable = CONF_BET_MODE_ENABLE,
Juuso Oikarinen84502562009-11-23 23:22:12 +0200225 .bet_max_consecutive = 10,
Juuso Oikarinen19ad0712009-11-02 20:22:11 +0200226 .psm_entry_retries = 3
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300227 },
228 .init = {
229 .sr_err_tbl = {
230 [0] = {
231 .len = 7,
232 .upper_limit = 0x03,
233 .values = {
234 0x18, 0x10, 0x05, 0xfb, 0xf0, 0xe8,
235 0x00 }
236 },
237 [1] = {
238 .len = 7,
239 .upper_limit = 0x03,
240 .values = {
241 0x18, 0x10, 0x05, 0xf6, 0xf0, 0xe8,
242 0x00 }
243 },
244 [2] = {
245 .len = 7,
246 .upper_limit = 0x03,
247 .values = {
248 0x18, 0x10, 0x05, 0xfb, 0xf0, 0xe8,
249 0x00 }
250 }
251 },
252 .sr_enable = 1,
253 .genparam = {
Luciano Coelhoc7c8adb2009-11-23 23:22:19 +0200254 .ref_clk = CONF_REF_CLK_38_4_E,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300255 .settling_time = 5,
256 .clk_valid_on_wakeup = 0,
257 .dc2dcmode = 0,
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300258 .single_dual_band = CONF_SINGLE_BAND,
Luciano Coelhoa3e84842009-12-11 15:40:42 +0200259 .tx_bip_fem_autodetect = 1,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300260 .tx_bip_fem_manufacturer = 1,
261 .settings = 1,
Luciano Coelho76c0f8d2009-12-11 15:40:41 +0200262 .sr_state = 1,
263 .srf1 = { 0, 0, 0, 0, 0, 0, 0, 0,
264 0, 0, 0, 0, 0, 0, 0, 0 },
265 .srf2 = { 0, 0, 0, 0, 0, 0, 0, 0,
266 0, 0, 0, 0, 0, 0, 0, 0 },
267 .srf3 = { 0, 0, 0, 0, 0, 0, 0, 0,
268 0, 0, 0, 0, 0, 0, 0, 0 },
269 .sr_debug_table = { 0, 0, 0, 0, 0, 0, 0, 0,
270 0, 0, 0, 0, 0, 0, 0, 0 },
271 .sr_sen_n_p = 0,
272 .sr_sen_n_p_gain = 0,
273 .sr_sen_nrn = 0,
274 .sr_sen_prn = 0,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300275 },
276 .radioparam = {
Luciano Coelhoa3e84842009-12-11 15:40:42 +0200277 .rx_trace_loss = 0x24,
278 .tx_trace_loss = 0x0,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300279 .rx_rssi_and_proc_compens = {
280 0xec, 0xf6, 0x00, 0x0c, 0x18, 0xf8,
281 0xfc, 0x00, 0x08, 0x10, 0xf0, 0xf8,
282 0x00, 0x0a, 0x14 },
283 .rx_trace_loss_5 = { 0, 0, 0, 0, 0, 0, 0 },
284 .tx_trace_loss_5 = { 0, 0, 0, 0, 0, 0, 0 },
285 .rx_rssi_and_proc_compens_5 = {
286 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
287 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
288 0x00, 0x00, 0x00 },
Luciano Coelhoa3e84842009-12-11 15:40:42 +0200289 .tx_ref_pd_voltage = 0x1a9,
290 .tx_ref_power = 0x80,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300291 .tx_offset_db = 0x0,
292 .tx_rate_limits_normal = {
Luciano Coelhoa3e84842009-12-11 15:40:42 +0200293 0x1d, 0x1f, 0x24, 0x28, 0x28, 0x29 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300294 .tx_rate_limits_degraded = {
Luciano Coelhoa3e84842009-12-11 15:40:42 +0200295 0x19, 0x1f, 0x22, 0x23, 0x27, 0x28 },
Luciano Coelhocf18be42009-12-11 15:40:43 +0200296 .tx_rate_limits_extreme = {
297 0x19, 0x1c, 0x1e, 0x20, 0x24, 0x25 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300298 .tx_channel_limits_11b = {
299 0x22, 0x50, 0x50, 0x50, 0x50, 0x50,
300 0x50, 0x50, 0x50, 0x50, 0x22, 0x50,
301 0x22, 0x50 },
302 .tx_channel_limits_ofdm = {
303 0x20, 0x50, 0x50, 0x50, 0x50, 0x50,
304 0x50, 0x50, 0x50, 0x50, 0x20, 0x50,
305 0x20, 0x50 },
306 .tx_pdv_rate_offsets = {
Luciano Coelhoa3e84842009-12-11 15:40:42 +0200307 0x07, 0x08, 0x04, 0x02, 0x02, 0x00 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300308 .tx_ibias = {
Luciano Coelhoa3e84842009-12-11 15:40:42 +0200309 0x11, 0x11, 0x15, 0x11, 0x15, 0x0f },
310 .rx_fem_insertion_loss = 0x0e,
Luciano Coelhocf18be42009-12-11 15:40:43 +0200311 .degraded_low_to_normal_threshold = 0x1e,
312 .degraded_normal_to_high_threshold = 0x2d,
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300313 .tx_ref_pd_voltage_5 = {
314 0x0190, 0x01a4, 0x01c3, 0x01d8,
315 0x020a, 0x021c },
316 .tx_ref_power_5 = {
317 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 },
318 .tx_offset_db_5 = {
319 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300320 .tx_rate_limits_normal_5 = {
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300321 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300322 .tx_rate_limits_degraded_5 = {
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300323 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 },
Luciano Coelhocf18be42009-12-11 15:40:43 +0200324 .tx_rate_limits_extreme_5 = {
325 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300326 .tx_channel_limits_ofdm_5 = {
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300327 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
328 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
329 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
330 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
331 0x50, 0x50, 0x50 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300332 .tx_pdv_rate_offsets_5 = {
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300333 0x01, 0x02, 0x02, 0x02, 0x02, 0x00 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300334 .tx_ibias_5 = {
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300335 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 },
336 .rx_fem_insertion_loss_5 = {
Luciano Coelhocf18be42009-12-11 15:40:43 +0200337 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 },
338 .degraded_low_to_normal_threshold_5 = 0x00,
339 .degraded_normal_to_high_threshold_5 = 0x00
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300340 }
341 }
342};
343
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300344static LIST_HEAD(wl_list);
345
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300346static void wl1271_conf_init(struct wl1271 *wl)
347{
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300348
349 /*
350 * This function applies the default configuration to the driver. This
351 * function is invoked upon driver load (spi probe.)
352 *
353 * The configuration is stored in a run-time structure in order to
354 * facilitate for run-time adjustment of any of the parameters. Making
355 * changes to the configuration structure will apply the new values on
356 * the next interface up (wl1271_op_start.)
357 */
358
359 /* apply driver default configuration */
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300360 memcpy(&wl->conf, &default_conf, sizeof(default_conf));
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300361
362 if (wl1271_11a_enabled())
363 wl->conf.init.genparam.single_dual_band = CONF_DUAL_BAND;
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300364}
365
366
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300367static int wl1271_plt_init(struct wl1271 *wl)
368{
369 int ret;
370
Luciano Coelho98b5dd52009-11-23 23:22:17 +0200371 ret = wl1271_cmd_general_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200372 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200373 return ret;
374
Luciano Coelho98b5dd52009-11-23 23:22:17 +0200375 ret = wl1271_cmd_radio_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200376 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200377 return ret;
378
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300379 ret = wl1271_acx_init_mem_config(wl);
380 if (ret < 0)
381 return ret;
382
383 ret = wl1271_cmd_data_path(wl, wl->channel, 1);
384 if (ret < 0)
385 return ret;
386
387 return 0;
388}
389
390static void wl1271_disable_interrupts(struct wl1271 *wl)
391{
392 disable_irq(wl->irq);
393}
394
395static void wl1271_power_off(struct wl1271 *wl)
396{
397 wl->set_power(false);
398}
399
400static void wl1271_power_on(struct wl1271 *wl)
401{
402 wl->set_power(true);
403}
404
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300405static void wl1271_fw_status(struct wl1271 *wl,
406 struct wl1271_fw_status *status)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300407{
408 u32 total = 0;
409 int i;
410
Juuso Oikarinen74621412009-10-12 15:08:54 +0300411 wl1271_spi_read(wl, FW_STATUS_ADDR, status,
412 sizeof(*status), false);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300413
414 wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
415 "drv_rx_counter = %d, tx_results_counter = %d)",
416 status->intr,
417 status->fw_rx_counter,
418 status->drv_rx_counter,
419 status->tx_results_counter);
420
421 /* update number of available TX blocks */
422 for (i = 0; i < NUM_TX_QUEUES; i++) {
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300423 u32 cnt = le32_to_cpu(status->tx_released_blks[i]) -
424 wl->tx_blocks_freed[i];
425
426 wl->tx_blocks_freed[i] =
427 le32_to_cpu(status->tx_released_blks[i]);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300428 wl->tx_blocks_available += cnt;
429 total += cnt;
430 }
431
432 /* if more blocks are available now, schedule some tx work */
433 if (total && !skb_queue_empty(&wl->tx_queue))
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300434 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300435
436 /* update the host-chipset time offset */
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300437 wl->time_offset = jiffies_to_usecs(jiffies) -
438 le32_to_cpu(status->fw_localtime);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300439}
440
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300441static void wl1271_irq_work(struct work_struct *work)
442{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300443 int ret;
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300444 u32 intr;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300445 struct wl1271 *wl =
446 container_of(work, struct wl1271, irq_work);
447
448 mutex_lock(&wl->mutex);
449
450 wl1271_debug(DEBUG_IRQ, "IRQ work");
451
452 if (wl->state == WL1271_STATE_OFF)
453 goto out;
454
455 ret = wl1271_ps_elp_wakeup(wl, true);
456 if (ret < 0)
457 goto out;
458
Juuso Oikarinen74621412009-10-12 15:08:54 +0300459 wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300460
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300461 wl1271_fw_status(wl, wl->fw_status);
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300462 intr = le32_to_cpu(wl->fw_status->intr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300463 if (!intr) {
464 wl1271_debug(DEBUG_IRQ, "Zero interrupt received.");
465 goto out_sleep;
466 }
467
468 intr &= WL1271_INTR_MASK;
469
Juuso Oikarinen1fd27942009-10-13 12:47:54 +0300470 if (intr & WL1271_ACX_INTR_EVENT_A) {
471 bool do_ack = (intr & WL1271_ACX_INTR_EVENT_B) ? false : true;
472 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
473 wl1271_event_handle(wl, 0, do_ack);
474 }
475
476 if (intr & WL1271_ACX_INTR_EVENT_B) {
477 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
478 wl1271_event_handle(wl, 1, true);
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300479 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300480
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300481 if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
482 wl1271_debug(DEBUG_IRQ,
483 "WL1271_ACX_INTR_INIT_COMPLETE");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300484
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300485 if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
486 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300487
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300488 if (intr & WL1271_ACX_INTR_DATA) {
489 u8 tx_res_cnt = wl->fw_status->tx_results_counter -
490 wl->tx_results_count;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300491
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300492 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300493
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300494 /* check for tx results */
495 if (tx_res_cnt)
496 wl1271_tx_complete(wl, tx_res_cnt);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300497
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300498 wl1271_rx(wl, wl->fw_status);
499 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300500
501out_sleep:
Juuso Oikarinen74621412009-10-12 15:08:54 +0300502 wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK,
Luciano Coelho73d0a132009-08-11 11:58:27 +0300503 WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300504 wl1271_ps_elp_sleep(wl);
505
506out:
507 mutex_unlock(&wl->mutex);
508}
509
510static irqreturn_t wl1271_irq(int irq, void *cookie)
511{
512 struct wl1271 *wl;
513 unsigned long flags;
514
515 wl1271_debug(DEBUG_IRQ, "IRQ");
516
517 wl = cookie;
518
519 /* complete the ELP completion */
520 spin_lock_irqsave(&wl->wl_lock, flags);
521 if (wl->elp_compl) {
522 complete(wl->elp_compl);
523 wl->elp_compl = NULL;
524 }
525
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300526 ieee80211_queue_work(wl->hw, &wl->irq_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300527 spin_unlock_irqrestore(&wl->wl_lock, flags);
528
529 return IRQ_HANDLED;
530}
531
532static int wl1271_fetch_firmware(struct wl1271 *wl)
533{
534 const struct firmware *fw;
535 int ret;
536
537 ret = request_firmware(&fw, WL1271_FW_NAME, &wl->spi->dev);
538
539 if (ret < 0) {
540 wl1271_error("could not get firmware: %d", ret);
541 return ret;
542 }
543
544 if (fw->size % 4) {
545 wl1271_error("firmware size is not multiple of 32 bits: %zu",
546 fw->size);
547 ret = -EILSEQ;
548 goto out;
549 }
550
551 wl->fw_len = fw->size;
Juuso Oikarinen1fba4972009-10-08 21:56:32 +0300552 wl->fw = vmalloc(wl->fw_len);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300553
554 if (!wl->fw) {
555 wl1271_error("could not allocate memory for the firmware");
556 ret = -ENOMEM;
557 goto out;
558 }
559
560 memcpy(wl->fw, fw->data, wl->fw_len);
561
562 ret = 0;
563
564out:
565 release_firmware(fw);
566
567 return ret;
568}
569
570static int wl1271_fetch_nvs(struct wl1271 *wl)
571{
572 const struct firmware *fw;
573 int ret;
574
575 ret = request_firmware(&fw, WL1271_NVS_NAME, &wl->spi->dev);
576
577 if (ret < 0) {
578 wl1271_error("could not get nvs file: %d", ret);
579 return ret;
580 }
581
582 if (fw->size % 4) {
583 wl1271_error("nvs size is not multiple of 32 bits: %zu",
584 fw->size);
585 ret = -EILSEQ;
586 goto out;
587 }
588
589 wl->nvs_len = fw->size;
590 wl->nvs = kmalloc(wl->nvs_len, GFP_KERNEL);
591
592 if (!wl->nvs) {
593 wl1271_error("could not allocate memory for the nvs file");
594 ret = -ENOMEM;
595 goto out;
596 }
597
598 memcpy(wl->nvs, fw->data, wl->nvs_len);
599
600 ret = 0;
601
602out:
603 release_firmware(fw);
604
605 return ret;
606}
607
608static void wl1271_fw_wakeup(struct wl1271 *wl)
609{
610 u32 elp_reg;
611
612 elp_reg = ELPCTRL_WAKE_UP;
Juuso Oikarinen74621412009-10-12 15:08:54 +0300613 wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300614}
615
616static int wl1271_setup(struct wl1271 *wl)
617{
618 wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL);
619 if (!wl->fw_status)
620 return -ENOMEM;
621
622 wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
623 if (!wl->tx_res_if) {
624 kfree(wl->fw_status);
625 return -ENOMEM;
626 }
627
628 INIT_WORK(&wl->irq_work, wl1271_irq_work);
629 INIT_WORK(&wl->tx_work, wl1271_tx_work);
630 return 0;
631}
632
633static int wl1271_chip_wakeup(struct wl1271 *wl)
634{
Juuso Oikarinen451de972009-10-12 15:08:46 +0300635 struct wl1271_partition_set partition;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300636 int ret = 0;
637
638 wl1271_power_on(wl);
639 msleep(WL1271_POWER_ON_SLEEP);
640 wl1271_spi_reset(wl);
641 wl1271_spi_init(wl);
642
643 /* We don't need a real memory partition here, because we only want
644 * to use the registers at this point. */
Juuso Oikarinen451de972009-10-12 15:08:46 +0300645 memset(&partition, 0, sizeof(partition));
646 partition.reg.start = REGISTERS_BASE;
647 partition.reg.size = REGISTERS_DOWN_SIZE;
648 wl1271_set_partition(wl, &partition);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300649
650 /* ELP module wake up */
651 wl1271_fw_wakeup(wl);
652
653 /* whal_FwCtrl_BootSm() */
654
655 /* 0. read chip id from CHIP_ID */
Juuso Oikarinen74621412009-10-12 15:08:54 +0300656 wl->chip.id = wl1271_spi_read32(wl, CHIP_ID_B);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300657
658 /* 1. check if chip id is valid */
659
660 switch (wl->chip.id) {
661 case CHIP_ID_1271_PG10:
662 wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
663 wl->chip.id);
664
665 ret = wl1271_setup(wl);
666 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300667 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300668 break;
669 case CHIP_ID_1271_PG20:
670 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
671 wl->chip.id);
672
673 ret = wl1271_setup(wl);
674 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300675 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300676 break;
677 default:
678 wl1271_error("unsupported chip id: 0x%x", wl->chip.id);
679 ret = -ENODEV;
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300680 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300681 }
682
683 if (wl->fw == NULL) {
684 ret = wl1271_fetch_firmware(wl);
685 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300686 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300687 }
688
689 /* No NVS from netlink, try to get it from the filesystem */
690 if (wl->nvs == NULL) {
691 ret = wl1271_fetch_nvs(wl);
692 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300693 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300694 }
695
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300696 goto out;
697
698out_power_off:
699 wl1271_power_off(wl);
700
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300701out:
702 return ret;
703}
704
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300705int wl1271_plt_start(struct wl1271 *wl)
706{
707 int ret;
708
709 mutex_lock(&wl->mutex);
710
711 wl1271_notice("power up");
712
713 if (wl->state != WL1271_STATE_OFF) {
714 wl1271_error("cannot go into PLT state because not "
715 "in off state: %d", wl->state);
716 ret = -EBUSY;
717 goto out;
718 }
719
720 wl->state = WL1271_STATE_PLT;
721
722 ret = wl1271_chip_wakeup(wl);
723 if (ret < 0)
724 goto out;
725
726 ret = wl1271_boot(wl);
727 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300728 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300729
730 wl1271_notice("firmware booted in PLT mode (%s)", wl->chip.fw_ver);
731
732 ret = wl1271_plt_init(wl);
733 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300734 goto out_irq_disable;
735
Luciano Coelhobd5ea182009-10-13 12:47:58 +0300736 /* Make sure power saving is disabled */
737 ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
738 if (ret < 0)
739 goto out_irq_disable;
740
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300741 goto out;
742
743out_irq_disable:
744 wl1271_disable_interrupts(wl);
745
746out_power_off:
747 wl1271_power_off(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300748
749out:
750 mutex_unlock(&wl->mutex);
751
752 return ret;
753}
754
755int wl1271_plt_stop(struct wl1271 *wl)
756{
757 int ret = 0;
758
759 mutex_lock(&wl->mutex);
760
761 wl1271_notice("power down");
762
763 if (wl->state != WL1271_STATE_PLT) {
764 wl1271_error("cannot power down because not in PLT "
765 "state: %d", wl->state);
766 ret = -EBUSY;
767 goto out;
768 }
769
770 wl1271_disable_interrupts(wl);
771 wl1271_power_off(wl);
772
773 wl->state = WL1271_STATE_OFF;
Luciano Coelhobd5ea182009-10-13 12:47:58 +0300774 wl->rx_counter = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300775
776out:
777 mutex_unlock(&wl->mutex);
778
779 return ret;
780}
781
782
783static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
784{
785 struct wl1271 *wl = hw->priv;
786
787 skb_queue_tail(&wl->tx_queue, skb);
788
789 /*
790 * The chip specific setup must run before the first TX packet -
791 * before that, the tx_work will not be initialized!
792 */
793
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300794 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300795
796 /*
797 * The workqueue is slow to process the tx_queue and we need stop
798 * the queue here, otherwise the queue will get too long.
799 */
800 if (skb_queue_len(&wl->tx_queue) >= WL1271_TX_QUEUE_MAX_LENGTH) {
801 ieee80211_stop_queues(wl->hw);
802
803 /*
804 * FIXME: this is racy, the variable is not properly
805 * protected. Maybe fix this by removing the stupid
806 * variable altogether and checking the real queue state?
807 */
808 wl->tx_queue_stopped = true;
809 }
810
811 return NETDEV_TX_OK;
812}
813
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300814static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
815 void *arg)
816{
817 struct net_device *dev;
818 struct wireless_dev *wdev;
819 struct wiphy *wiphy;
820 struct ieee80211_hw *hw;
821 struct wl1271 *wl;
822 struct wl1271 *wl_temp;
823 struct in_device *idev;
824 struct in_ifaddr *ifa = arg;
825 int ret = 0;
826
827 /* FIXME: this ugly function should probably be implemented in the
828 * mac80211, and here should only be a simple callback handling actual
829 * setting of the filters. Now we need to dig up references to
830 * various structures to gain access to what we need.
831 * Also, because of this, there is no "initial" setting of the filter
832 * in "op_start", because we don't want to dig up struct net_device
833 * there - the filter will be set upon first change of the interface
834 * IP address. */
835
836 dev = ifa->ifa_dev->dev;
837
838 wdev = dev->ieee80211_ptr;
839 if (wdev == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200840 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300841
842 wiphy = wdev->wiphy;
843 if (wiphy == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200844 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300845
846 hw = wiphy_priv(wiphy);
847 if (hw == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200848 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300849
850 /* Check that the interface is one supported by this driver. */
851 wl_temp = hw->priv;
852 list_for_each_entry(wl, &wl_list, list) {
853 if (wl == wl_temp)
854 break;
855 }
856 if (wl == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200857 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300858
859 /* Get the interface IP address for the device. "ifa" will become
860 NULL if:
861 - there is no IPV4 protocol address configured
862 - there are multiple (virtual) IPV4 addresses configured
863 When "ifa" is NULL, filtering will be disabled.
864 */
865 ifa = NULL;
866 idev = dev->ip_ptr;
867 if (idev)
868 ifa = idev->ifa_list;
869
870 if (ifa && ifa->ifa_next)
871 ifa = NULL;
872
873 mutex_lock(&wl->mutex);
874
875 if (wl->state == WL1271_STATE_OFF)
876 goto out;
877
878 ret = wl1271_ps_elp_wakeup(wl, false);
879 if (ret < 0)
880 goto out;
881 if (ifa)
882 ret = wl1271_acx_arp_ip_filter(wl, true,
883 (u8 *)&ifa->ifa_address,
884 ACX_IPV4_VERSION);
885 else
886 ret = wl1271_acx_arp_ip_filter(wl, false, NULL,
887 ACX_IPV4_VERSION);
888 wl1271_ps_elp_sleep(wl);
889
890out:
891 mutex_unlock(&wl->mutex);
892
Luciano Coelho17d72652009-11-23 23:22:15 +0200893 return NOTIFY_OK;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300894}
895
896static struct notifier_block wl1271_dev_notifier = {
897 .notifier_call = wl1271_dev_notify,
898};
899
900
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300901static int wl1271_op_start(struct ieee80211_hw *hw)
902{
903 struct wl1271 *wl = hw->priv;
904 int ret = 0;
905
906 wl1271_debug(DEBUG_MAC80211, "mac80211 start");
907
908 mutex_lock(&wl->mutex);
909
910 if (wl->state != WL1271_STATE_OFF) {
911 wl1271_error("cannot start because not in off state: %d",
912 wl->state);
913 ret = -EBUSY;
914 goto out;
915 }
916
917 ret = wl1271_chip_wakeup(wl);
918 if (ret < 0)
919 goto out;
920
921 ret = wl1271_boot(wl);
922 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300923 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300924
925 ret = wl1271_hw_init(wl);
926 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300927 goto out_irq_disable;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300928
929 wl->state = WL1271_STATE_ON;
930
931 wl1271_info("firmware booted (%s)", wl->chip.fw_ver);
932
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300933 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300934
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300935out_irq_disable:
936 wl1271_disable_interrupts(wl);
937
938out_power_off:
939 wl1271_power_off(wl);
940
941out:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300942 mutex_unlock(&wl->mutex);
943
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300944 if (!ret) {
945 list_add(&wl->list, &wl_list);
946 register_inetaddr_notifier(&wl1271_dev_notifier);
947 }
948
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300949 return ret;
950}
951
952static void wl1271_op_stop(struct ieee80211_hw *hw)
953{
954 struct wl1271 *wl = hw->priv;
955 int i;
956
957 wl1271_info("down");
958
959 wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
960
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300961 unregister_inetaddr_notifier(&wl1271_dev_notifier);
962 list_del(&wl->list);
963
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300964 mutex_lock(&wl->mutex);
965
966 WARN_ON(wl->state != WL1271_STATE_ON);
967
968 if (wl->scanning) {
969 mutex_unlock(&wl->mutex);
970 ieee80211_scan_completed(wl->hw, true);
971 mutex_lock(&wl->mutex);
972 wl->scanning = false;
973 }
974
975 wl->state = WL1271_STATE_OFF;
976
977 wl1271_disable_interrupts(wl);
978
979 mutex_unlock(&wl->mutex);
980
981 cancel_work_sync(&wl->irq_work);
982 cancel_work_sync(&wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300983
984 mutex_lock(&wl->mutex);
985
986 /* let's notify MAC80211 about the remaining pending TX frames */
987 wl1271_tx_flush(wl);
988 wl1271_power_off(wl);
989
990 memset(wl->bssid, 0, ETH_ALEN);
991 memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1);
992 wl->ssid_len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300993 wl->bss_type = MAX_BSS_TYPE;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +0300994 wl->band = IEEE80211_BAND_2GHZ;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300995
996 wl->rx_counter = 0;
997 wl->elp = false;
998 wl->psm = 0;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +0200999 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001000 wl->tx_queue_stopped = false;
1001 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
1002 wl->tx_blocks_available = 0;
1003 wl->tx_results_count = 0;
1004 wl->tx_packets_count = 0;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001005 wl->tx_security_last_seq = 0;
1006 wl->tx_security_seq_16 = 0;
1007 wl->tx_security_seq_32 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001008 wl->time_offset = 0;
1009 wl->session_counter = 0;
Luciano Coelhod6e19d132009-10-12 15:08:43 +03001010 wl->joined = false;
1011
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001012 for (i = 0; i < NUM_TX_QUEUES; i++)
1013 wl->tx_blocks_freed[i] = 0;
1014
1015 wl1271_debugfs_reset(wl);
1016 mutex_unlock(&wl->mutex);
1017}
1018
1019static int wl1271_op_add_interface(struct ieee80211_hw *hw,
1020 struct ieee80211_if_init_conf *conf)
1021{
1022 struct wl1271 *wl = hw->priv;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001023 int ret = 0;
1024
John W. Linvillee5539bc2009-08-18 10:50:34 -04001025 wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
1026 conf->type, conf->mac_addr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001027
1028 mutex_lock(&wl->mutex);
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001029 if (wl->vif) {
1030 ret = -EBUSY;
1031 goto out;
1032 }
1033
1034 wl->vif = conf->vif;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001035
1036 switch (conf->type) {
1037 case NL80211_IFTYPE_STATION:
1038 wl->bss_type = BSS_TYPE_STA_BSS;
1039 break;
1040 case NL80211_IFTYPE_ADHOC:
1041 wl->bss_type = BSS_TYPE_IBSS;
1042 break;
1043 default:
1044 ret = -EOPNOTSUPP;
1045 goto out;
1046 }
1047
1048 /* FIXME: what if conf->mac_addr changes? */
1049
1050out:
1051 mutex_unlock(&wl->mutex);
1052 return ret;
1053}
1054
1055static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
1056 struct ieee80211_if_init_conf *conf)
1057{
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001058 struct wl1271 *wl = hw->priv;
1059
1060 mutex_lock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001061 wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001062 wl->vif = NULL;
1063 mutex_unlock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001064}
1065
1066#if 0
1067static int wl1271_op_config_interface(struct ieee80211_hw *hw,
1068 struct ieee80211_vif *vif,
1069 struct ieee80211_if_conf *conf)
1070{
1071 struct wl1271 *wl = hw->priv;
1072 struct sk_buff *beacon;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001073 int ret;
1074
David S. Miller32646902009-09-17 10:18:30 -07001075 wl1271_debug(DEBUG_MAC80211, "mac80211 config_interface bssid %pM",
1076 conf->bssid);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001077 wl1271_dump_ascii(DEBUG_MAC80211, "ssid: ", conf->ssid,
1078 conf->ssid_len);
1079
1080 mutex_lock(&wl->mutex);
1081
1082 ret = wl1271_ps_elp_wakeup(wl, false);
1083 if (ret < 0)
1084 goto out;
1085
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001086 if (memcmp(wl->bssid, conf->bssid, ETH_ALEN)) {
1087 wl1271_debug(DEBUG_MAC80211, "bssid changed");
1088
1089 memcpy(wl->bssid, conf->bssid, ETH_ALEN);
1090
1091 ret = wl1271_cmd_join(wl);
1092 if (ret < 0)
1093 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001094
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02001095 ret = wl1271_cmd_build_null_data(wl);
1096 if (ret < 0)
1097 goto out_sleep;
1098 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001099
1100 wl->ssid_len = conf->ssid_len;
1101 if (wl->ssid_len)
1102 memcpy(wl->ssid, conf->ssid, wl->ssid_len);
1103
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001104 if (conf->changed & IEEE80211_IFCC_BEACON) {
1105 beacon = ieee80211_beacon_get(hw, vif);
1106 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON,
1107 beacon->data, beacon->len);
1108
1109 if (ret < 0) {
1110 dev_kfree_skb(beacon);
1111 goto out_sleep;
1112 }
1113
1114 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE,
1115 beacon->data, beacon->len);
1116
1117 dev_kfree_skb(beacon);
1118
1119 if (ret < 0)
1120 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001121 }
1122
1123out_sleep:
1124 wl1271_ps_elp_sleep(wl);
1125
1126out:
1127 mutex_unlock(&wl->mutex);
1128
1129 return ret;
1130}
1131#endif
1132
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001133static int wl1271_join_channel(struct wl1271 *wl, int channel)
1134{
1135 int ret;
1136 /* we need to use a dummy BSSID for now */
1137 static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
1138 0xad, 0xbe, 0xef };
1139
1140 /* disable mac filter, so we hear everything */
1141 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1142
1143 wl->channel = channel;
1144 memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
1145
1146 ret = wl1271_cmd_join(wl);
1147 if (ret < 0)
1148 goto out;
1149
1150 wl->joined = true;
1151
1152out:
1153 return ret;
1154}
1155
1156static int wl1271_unjoin_channel(struct wl1271 *wl)
1157{
1158 int ret;
1159
1160 /* to stop listening to a channel, we disconnect */
1161 ret = wl1271_cmd_disconnect(wl);
1162 if (ret < 0)
1163 goto out;
1164
1165 wl->joined = false;
1166 wl->channel = 0;
1167 memset(wl->bssid, 0, ETH_ALEN);
1168 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
1169
1170out:
1171 return ret;
1172}
1173
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001174static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
1175{
1176 struct wl1271 *wl = hw->priv;
1177 struct ieee80211_conf *conf = &hw->conf;
1178 int channel, ret = 0;
1179
1180 channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
1181
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001182 wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s",
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001183 channel,
1184 conf->flags & IEEE80211_CONF_PS ? "on" : "off",
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001185 conf->power_level,
1186 conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001187
1188 mutex_lock(&wl->mutex);
1189
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001190 wl->band = conf->channel->band;
1191
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001192 ret = wl1271_ps_elp_wakeup(wl, false);
1193 if (ret < 0)
1194 goto out;
1195
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001196 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1197 if (conf->flags & IEEE80211_CONF_IDLE && wl->joined)
1198 wl1271_unjoin_channel(wl);
1199 else
1200 wl1271_join_channel(wl, channel);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001201 }
1202
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001203 /* if the channel changes while joined, join again */
1204 if (channel != wl->channel && wl->joined)
1205 wl1271_join_channel(wl, channel);
1206
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001207 if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) {
1208 wl1271_info("psm enabled");
1209
1210 wl->psm_requested = true;
1211
1212 /*
1213 * We enter PSM only if we're already associated.
1214 * If we're not, we'll enter it when joining an SSID,
1215 * through the bss_info_changed() hook.
1216 */
1217 ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
1218 } else if (!(conf->flags & IEEE80211_CONF_PS) &&
1219 wl->psm_requested) {
1220 wl1271_info("psm disabled");
1221
1222 wl->psm_requested = false;
1223
1224 if (wl->psm)
1225 ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE);
1226 }
1227
1228 if (conf->power_level != wl->power_level) {
1229 ret = wl1271_acx_tx_power(wl, conf->power_level);
1230 if (ret < 0)
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02001231 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001232
1233 wl->power_level = conf->power_level;
1234 }
1235
1236out_sleep:
1237 wl1271_ps_elp_sleep(wl);
1238
1239out:
1240 mutex_unlock(&wl->mutex);
1241
1242 return ret;
1243}
1244
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001245struct wl1271_filter_params {
1246 bool enabled;
1247 int mc_list_length;
1248 u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
1249};
1250
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001251static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count,
1252 struct dev_addr_list *mc_list)
1253{
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001254 struct wl1271_filter_params *fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001255 int i;
1256
Juuso Oikarinen74441132009-10-13 12:47:53 +03001257 fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001258 if (!fp) {
1259 wl1271_error("Out of memory setting filters.");
1260 return 0;
1261 }
1262
1263 /* update multicast filtering parameters */
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001264 fp->enabled = true;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001265 if (mc_count > ACX_MC_ADDRESS_GROUP_MAX) {
1266 mc_count = 0;
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001267 fp->enabled = false;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001268 }
1269
1270 fp->mc_list_length = 0;
1271 for (i = 0; i < mc_count; i++) {
1272 if (mc_list->da_addrlen == ETH_ALEN) {
1273 memcpy(fp->mc_list[fp->mc_list_length],
1274 mc_list->da_addr, ETH_ALEN);
1275 fp->mc_list_length++;
1276 } else
1277 wl1271_warning("Unknown mc address length.");
Juuso Oikarinen74441132009-10-13 12:47:53 +03001278 mc_list = mc_list->next;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001279 }
1280
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001281 return (u64)(unsigned long)fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001282}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001283
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001284#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
1285 FIF_ALLMULTI | \
1286 FIF_FCSFAIL | \
1287 FIF_BCN_PRBRESP_PROMISC | \
1288 FIF_CONTROL | \
1289 FIF_OTHER_BSS)
1290
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001291static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
1292 unsigned int changed,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001293 unsigned int *total, u64 multicast)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001294{
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001295 struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001296 struct wl1271 *wl = hw->priv;
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001297 int ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001298
1299 wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter");
1300
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001301 mutex_lock(&wl->mutex);
1302
1303 if (wl->state == WL1271_STATE_OFF)
1304 goto out;
1305
1306 ret = wl1271_ps_elp_wakeup(wl, false);
1307 if (ret < 0)
1308 goto out;
1309
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001310 *total &= WL1271_SUPPORTED_FILTERS;
1311 changed &= WL1271_SUPPORTED_FILTERS;
1312
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001313 if (*total & FIF_ALLMULTI)
1314 ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
1315 else if (fp)
1316 ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
1317 fp->mc_list,
1318 fp->mc_list_length);
1319 if (ret < 0)
1320 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001321
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001322 kfree(fp);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001323
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001324 /* FIXME: We still need to set our filters properly */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001325
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001326 /* determine, whether supported filter values have changed */
1327 if (changed == 0)
1328 goto out_sleep;
1329
1330 /* apply configured filters */
1331 ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
1332 if (ret < 0)
1333 goto out_sleep;
1334
1335out_sleep:
1336 wl1271_ps_elp_sleep(wl);
1337
1338out:
1339 mutex_unlock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001340}
1341
1342static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
1343 struct ieee80211_vif *vif,
1344 struct ieee80211_sta *sta,
1345 struct ieee80211_key_conf *key_conf)
1346{
1347 struct wl1271 *wl = hw->priv;
1348 const u8 *addr;
1349 int ret;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001350 u32 tx_seq_32 = 0;
1351 u16 tx_seq_16 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001352 u8 key_type;
1353
1354 static const u8 bcast_addr[ETH_ALEN] =
1355 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1356
1357 wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
1358
1359 addr = sta ? sta->addr : bcast_addr;
1360
1361 wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd);
1362 wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN);
1363 wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
1364 key_conf->alg, key_conf->keyidx,
1365 key_conf->keylen, key_conf->flags);
1366 wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
1367
1368 if (is_zero_ether_addr(addr)) {
1369 /* We dont support TX only encryption */
1370 ret = -EOPNOTSUPP;
1371 goto out;
1372 }
1373
1374 mutex_lock(&wl->mutex);
1375
1376 ret = wl1271_ps_elp_wakeup(wl, false);
1377 if (ret < 0)
1378 goto out_unlock;
1379
1380 switch (key_conf->alg) {
1381 case ALG_WEP:
1382 key_type = KEY_WEP;
1383
1384 key_conf->hw_key_idx = key_conf->keyidx;
1385 break;
1386 case ALG_TKIP:
1387 key_type = KEY_TKIP;
1388
1389 key_conf->hw_key_idx = key_conf->keyidx;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001390 tx_seq_32 = wl->tx_security_seq_32;
1391 tx_seq_16 = wl->tx_security_seq_16;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001392 break;
1393 case ALG_CCMP:
1394 key_type = KEY_AES;
1395
1396 key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001397 tx_seq_32 = wl->tx_security_seq_32;
1398 tx_seq_16 = wl->tx_security_seq_16;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001399 break;
1400 default:
1401 wl1271_error("Unknown key algo 0x%x", key_conf->alg);
1402
1403 ret = -EOPNOTSUPP;
1404 goto out_sleep;
1405 }
1406
1407 switch (cmd) {
1408 case SET_KEY:
1409 ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE,
1410 key_conf->keyidx, key_type,
1411 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001412 addr, tx_seq_32, tx_seq_16);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001413 if (ret < 0) {
1414 wl1271_error("Could not add or replace key");
1415 goto out_sleep;
1416 }
1417 break;
1418
1419 case DISABLE_KEY:
1420 ret = wl1271_cmd_set_key(wl, KEY_REMOVE,
1421 key_conf->keyidx, key_type,
1422 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001423 addr, 0, 0);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001424 if (ret < 0) {
1425 wl1271_error("Could not remove key");
1426 goto out_sleep;
1427 }
1428 break;
1429
1430 default:
1431 wl1271_error("Unsupported key cmd 0x%x", cmd);
1432 ret = -EOPNOTSUPP;
1433 goto out_sleep;
1434
1435 break;
1436 }
1437
1438out_sleep:
1439 wl1271_ps_elp_sleep(wl);
1440
1441out_unlock:
1442 mutex_unlock(&wl->mutex);
1443
1444out:
1445 return ret;
1446}
1447
1448static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
1449 struct cfg80211_scan_request *req)
1450{
1451 struct wl1271 *wl = hw->priv;
1452 int ret;
1453 u8 *ssid = NULL;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001454 size_t len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001455
1456 wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
1457
1458 if (req->n_ssids) {
1459 ssid = req->ssids[0].ssid;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001460 len = req->ssids[0].ssid_len;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001461 }
1462
1463 mutex_lock(&wl->mutex);
1464
1465 ret = wl1271_ps_elp_wakeup(wl, false);
1466 if (ret < 0)
1467 goto out;
1468
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001469 if (wl1271_11a_enabled())
1470 ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
1471 WL1271_SCAN_BAND_DUAL, 3);
1472 else
1473 ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
1474 WL1271_SCAN_BAND_2_4_GHZ, 3);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001475
1476 wl1271_ps_elp_sleep(wl);
1477
1478out:
1479 mutex_unlock(&wl->mutex);
1480
1481 return ret;
1482}
1483
1484static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1485{
1486 struct wl1271 *wl = hw->priv;
1487 int ret;
1488
1489 mutex_lock(&wl->mutex);
1490
1491 ret = wl1271_ps_elp_wakeup(wl, false);
1492 if (ret < 0)
1493 goto out;
1494
1495 ret = wl1271_acx_rts_threshold(wl, (u16) value);
1496 if (ret < 0)
1497 wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret);
1498
1499 wl1271_ps_elp_sleep(wl);
1500
1501out:
1502 mutex_unlock(&wl->mutex);
1503
1504 return ret;
1505}
1506
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001507static u32 wl1271_enabled_rates_get(struct wl1271 *wl, u64 basic_rate_set)
1508{
1509 struct ieee80211_supported_band *band;
1510 u32 enabled_rates = 0;
1511 int bit;
1512
1513 band = wl->hw->wiphy->bands[wl->band];
1514 for (bit = 0; bit < band->n_bitrates; bit++) {
1515 if (basic_rate_set & 0x1)
1516 enabled_rates |= band->bitrates[bit].hw_value;
1517 basic_rate_set >>= 1;
1518 }
1519
1520 return enabled_rates;
1521}
1522
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001523static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
1524 struct ieee80211_vif *vif,
1525 struct ieee80211_bss_conf *bss_conf,
1526 u32 changed)
1527{
1528 enum wl1271_cmd_ps_mode mode;
1529 struct wl1271 *wl = hw->priv;
1530 int ret;
1531
1532 wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed");
1533
1534 mutex_lock(&wl->mutex);
1535
1536 ret = wl1271_ps_elp_wakeup(wl, false);
1537 if (ret < 0)
1538 goto out;
1539
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001540 if ((changed & BSS_CHANGED_BSSID) &&
1541 /*
1542 * Now we know the correct bssid, so we send a new join command
1543 * and enable the BSSID filter
1544 */
1545 memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
1546 wl->rx_config |= CFG_BSSID_FILTER_EN;
1547 memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
Luciano Coelhocd264762009-12-11 15:40:47 +02001548 ret = wl1271_cmd_join(wl);
1549 if (ret < 0) {
1550 wl1271_warning("cmd join failed %d", ret);
1551 goto out_sleep;
1552 }
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001553 wl->joined = true;
1554 }
1555
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001556 if (changed & BSS_CHANGED_ASSOC) {
1557 if (bss_conf->assoc) {
1558 wl->aid = bss_conf->aid;
1559
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001560 /*
1561 * with wl1271, we don't need to update the
1562 * beacon_int and dtim_period, because the firmware
1563 * updates it by itself when the first beacon is
1564 * received after a join.
1565 */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001566 ret = wl1271_cmd_build_ps_poll(wl, wl->aid);
1567 if (ret < 0)
1568 goto out_sleep;
1569
1570 ret = wl1271_acx_aid(wl, wl->aid);
1571 if (ret < 0)
1572 goto out_sleep;
1573
1574 /* If we want to go in PSM but we're not there yet */
1575 if (wl->psm_requested && !wl->psm) {
1576 mode = STATION_POWER_SAVE_MODE;
1577 ret = wl1271_ps_set_mode(wl, mode);
1578 if (ret < 0)
1579 goto out_sleep;
1580 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001581 } else {
1582 /* use defaults when not associated */
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001583 wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
1584 wl->aid = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001585 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001586
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001587 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001588
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001589 if (changed & BSS_CHANGED_ERP_SLOT) {
1590 if (bss_conf->use_short_slot)
1591 ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
1592 else
1593 ret = wl1271_acx_slot(wl, SLOT_TIME_LONG);
1594 if (ret < 0) {
1595 wl1271_warning("Set slot time failed %d", ret);
1596 goto out_sleep;
1597 }
1598 }
1599
1600 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
1601 if (bss_conf->use_short_preamble)
1602 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT);
1603 else
1604 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG);
1605 }
1606
1607 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
1608 if (bss_conf->use_cts_prot)
1609 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE);
1610 else
1611 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE);
1612 if (ret < 0) {
1613 wl1271_warning("Set ctsprotect failed %d", ret);
1614 goto out_sleep;
1615 }
1616 }
1617
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001618 if (changed & BSS_CHANGED_BASIC_RATES) {
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001619 wl->basic_rate_set = wl1271_enabled_rates_get(
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001620 wl, bss_conf->basic_rates);
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001621
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001622 ret = wl1271_acx_rate_policies(wl, wl->basic_rate_set);
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001623 if (ret < 0) {
1624 wl1271_warning("Set rate policies failed %d", ret);
1625 goto out_sleep;
1626 }
1627 }
1628
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001629out_sleep:
1630 wl1271_ps_elp_sleep(wl);
1631
1632out:
1633 mutex_unlock(&wl->mutex);
1634}
1635
1636
1637/* can't be const, mac80211 writes to this */
1638static struct ieee80211_rate wl1271_rates[] = {
1639 { .bitrate = 10,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001640 .hw_value = CONF_HW_BIT_RATE_1MBPS,
1641 .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001642 { .bitrate = 20,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001643 .hw_value = CONF_HW_BIT_RATE_2MBPS,
1644 .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001645 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
1646 { .bitrate = 55,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001647 .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
1648 .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001649 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
1650 { .bitrate = 110,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001651 .hw_value = CONF_HW_BIT_RATE_11MBPS,
1652 .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001653 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
1654 { .bitrate = 60,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001655 .hw_value = CONF_HW_BIT_RATE_6MBPS,
1656 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001657 { .bitrate = 90,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001658 .hw_value = CONF_HW_BIT_RATE_9MBPS,
1659 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001660 { .bitrate = 120,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001661 .hw_value = CONF_HW_BIT_RATE_12MBPS,
1662 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001663 { .bitrate = 180,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001664 .hw_value = CONF_HW_BIT_RATE_18MBPS,
1665 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001666 { .bitrate = 240,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001667 .hw_value = CONF_HW_BIT_RATE_24MBPS,
1668 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001669 { .bitrate = 360,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001670 .hw_value = CONF_HW_BIT_RATE_36MBPS,
1671 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001672 { .bitrate = 480,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001673 .hw_value = CONF_HW_BIT_RATE_48MBPS,
1674 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001675 { .bitrate = 540,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001676 .hw_value = CONF_HW_BIT_RATE_54MBPS,
1677 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001678};
1679
1680/* can't be const, mac80211 writes to this */
1681static struct ieee80211_channel wl1271_channels[] = {
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02001682 { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
1683 { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
1684 { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
1685 { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
1686 { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
1687 { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
1688 { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
1689 { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
1690 { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
1691 { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
1692 { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
1693 { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
1694 { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001695};
1696
1697/* can't be const, mac80211 writes to this */
1698static struct ieee80211_supported_band wl1271_band_2ghz = {
1699 .channels = wl1271_channels,
1700 .n_channels = ARRAY_SIZE(wl1271_channels),
1701 .bitrates = wl1271_rates,
1702 .n_bitrates = ARRAY_SIZE(wl1271_rates),
1703};
1704
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03001705/* 5 GHz data rates for WL1273 */
1706static struct ieee80211_rate wl1271_rates_5ghz[] = {
1707 { .bitrate = 60,
1708 .hw_value = CONF_HW_BIT_RATE_6MBPS,
1709 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
1710 { .bitrate = 90,
1711 .hw_value = CONF_HW_BIT_RATE_9MBPS,
1712 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
1713 { .bitrate = 120,
1714 .hw_value = CONF_HW_BIT_RATE_12MBPS,
1715 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
1716 { .bitrate = 180,
1717 .hw_value = CONF_HW_BIT_RATE_18MBPS,
1718 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
1719 { .bitrate = 240,
1720 .hw_value = CONF_HW_BIT_RATE_24MBPS,
1721 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
1722 { .bitrate = 360,
1723 .hw_value = CONF_HW_BIT_RATE_36MBPS,
1724 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
1725 { .bitrate = 480,
1726 .hw_value = CONF_HW_BIT_RATE_48MBPS,
1727 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
1728 { .bitrate = 540,
1729 .hw_value = CONF_HW_BIT_RATE_54MBPS,
1730 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
1731};
1732
1733/* 5 GHz band channels for WL1273 */
1734static struct ieee80211_channel wl1271_channels_5ghz[] = {
1735 { .hw_value = 183, .center_freq = 4915},
1736 { .hw_value = 184, .center_freq = 4920},
1737 { .hw_value = 185, .center_freq = 4925},
1738 { .hw_value = 187, .center_freq = 4935},
1739 { .hw_value = 188, .center_freq = 4940},
1740 { .hw_value = 189, .center_freq = 4945},
1741 { .hw_value = 192, .center_freq = 4960},
1742 { .hw_value = 196, .center_freq = 4980},
1743 { .hw_value = 7, .center_freq = 5035},
1744 { .hw_value = 8, .center_freq = 5040},
1745 { .hw_value = 9, .center_freq = 5045},
1746 { .hw_value = 11, .center_freq = 5055},
1747 { .hw_value = 12, .center_freq = 5060},
1748 { .hw_value = 16, .center_freq = 5080},
1749 { .hw_value = 34, .center_freq = 5170},
1750 { .hw_value = 36, .center_freq = 5180},
1751 { .hw_value = 38, .center_freq = 5190},
1752 { .hw_value = 40, .center_freq = 5200},
1753 { .hw_value = 42, .center_freq = 5210},
1754 { .hw_value = 44, .center_freq = 5220},
1755 { .hw_value = 46, .center_freq = 5230},
1756 { .hw_value = 48, .center_freq = 5240},
1757 { .hw_value = 52, .center_freq = 5260},
1758 { .hw_value = 56, .center_freq = 5280},
1759 { .hw_value = 60, .center_freq = 5300},
1760 { .hw_value = 64, .center_freq = 5320},
1761 { .hw_value = 100, .center_freq = 5500},
1762 { .hw_value = 104, .center_freq = 5520},
1763 { .hw_value = 108, .center_freq = 5540},
1764 { .hw_value = 112, .center_freq = 5560},
1765 { .hw_value = 116, .center_freq = 5580},
1766 { .hw_value = 120, .center_freq = 5600},
1767 { .hw_value = 124, .center_freq = 5620},
1768 { .hw_value = 128, .center_freq = 5640},
1769 { .hw_value = 132, .center_freq = 5660},
1770 { .hw_value = 136, .center_freq = 5680},
1771 { .hw_value = 140, .center_freq = 5700},
1772 { .hw_value = 149, .center_freq = 5745},
1773 { .hw_value = 153, .center_freq = 5765},
1774 { .hw_value = 157, .center_freq = 5785},
1775 { .hw_value = 161, .center_freq = 5805},
1776 { .hw_value = 165, .center_freq = 5825},
1777};
1778
1779
1780static struct ieee80211_supported_band wl1271_band_5ghz = {
1781 .channels = wl1271_channels_5ghz,
1782 .n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
1783 .bitrates = wl1271_rates_5ghz,
1784 .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
1785};
1786
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001787static const struct ieee80211_ops wl1271_ops = {
1788 .start = wl1271_op_start,
1789 .stop = wl1271_op_stop,
1790 .add_interface = wl1271_op_add_interface,
1791 .remove_interface = wl1271_op_remove_interface,
1792 .config = wl1271_op_config,
1793/* .config_interface = wl1271_op_config_interface, */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001794 .prepare_multicast = wl1271_op_prepare_multicast,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001795 .configure_filter = wl1271_op_configure_filter,
1796 .tx = wl1271_op_tx,
1797 .set_key = wl1271_op_set_key,
1798 .hw_scan = wl1271_op_hw_scan,
1799 .bss_info_changed = wl1271_op_bss_info_changed,
1800 .set_rts_threshold = wl1271_op_set_rts_threshold,
1801};
1802
1803static int wl1271_register_hw(struct wl1271 *wl)
1804{
1805 int ret;
1806
1807 if (wl->mac80211_registered)
1808 return 0;
1809
1810 SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
1811
1812 ret = ieee80211_register_hw(wl->hw);
1813 if (ret < 0) {
1814 wl1271_error("unable to register mac80211 hw: %d", ret);
1815 return ret;
1816 }
1817
1818 wl->mac80211_registered = true;
1819
1820 wl1271_notice("loaded");
1821
1822 return 0;
1823}
1824
1825static int wl1271_init_ieee80211(struct wl1271 *wl)
1826{
Juuso Oikarinen1e2b7972009-10-08 21:56:20 +03001827 /* The tx descriptor buffer and the TKIP space. */
1828 wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
1829 sizeof(struct wl1271_tx_hw_descr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001830
1831 /* unit us */
1832 /* FIXME: find a proper value */
1833 wl->hw->channel_change_time = 10000;
1834
1835 wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
Juuso Oikarinen19221672009-10-08 21:56:35 +03001836 IEEE80211_HW_NOISE_DBM |
Juuso Oikarinen03442a32009-11-23 23:22:14 +02001837 IEEE80211_HW_BEACON_FILTER |
1838 IEEE80211_HW_SUPPORTS_PS;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001839
1840 wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
1841 wl->hw->wiphy->max_scan_ssids = 1;
1842 wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz;
1843
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03001844 if (wl1271_11a_enabled())
1845 wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz;
1846
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001847 SET_IEEE80211_DEV(wl->hw, &wl->spi->dev);
1848
1849 return 0;
1850}
1851
1852static void wl1271_device_release(struct device *dev)
1853{
1854
1855}
1856
1857static struct platform_device wl1271_device = {
1858 .name = "wl1271",
1859 .id = -1,
1860
1861 /* device model insists to have a release function */
1862 .dev = {
1863 .release = wl1271_device_release,
1864 },
1865};
1866
1867#define WL1271_DEFAULT_CHANNEL 0
1868static int __devinit wl1271_probe(struct spi_device *spi)
1869{
1870 struct wl12xx_platform_data *pdata;
1871 struct ieee80211_hw *hw;
1872 struct wl1271 *wl;
1873 int ret, i;
1874 static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
1875
1876 pdata = spi->dev.platform_data;
1877 if (!pdata) {
1878 wl1271_error("no platform data");
1879 return -ENODEV;
1880 }
1881
1882 hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
1883 if (!hw) {
1884 wl1271_error("could not alloc ieee80211_hw");
1885 return -ENOMEM;
1886 }
1887
1888 wl = hw->priv;
1889 memset(wl, 0, sizeof(*wl));
1890
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001891 INIT_LIST_HEAD(&wl->list);
1892
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001893 wl->hw = hw;
1894 dev_set_drvdata(&spi->dev, wl);
1895 wl->spi = spi;
1896
1897 skb_queue_head_init(&wl->tx_queue);
1898
Juuso Oikarinen37b70a82009-10-08 21:56:21 +03001899 INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001900 wl->channel = WL1271_DEFAULT_CHANNEL;
1901 wl->scanning = false;
1902 wl->default_key = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001903 wl->rx_counter = 0;
1904 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
1905 wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
1906 wl->elp = false;
1907 wl->psm = 0;
1908 wl->psm_requested = false;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02001909 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001910 wl->tx_queue_stopped = false;
1911 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001912 wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001913 wl->band = IEEE80211_BAND_2GHZ;
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001914 wl->vif = NULL;
Luciano Coelhod6e19d132009-10-12 15:08:43 +03001915 wl->joined = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001916
Juuso Oikarinenbe7078c2009-10-08 21:56:26 +03001917 for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001918 wl->tx_frames[i] = NULL;
1919
1920 spin_lock_init(&wl->wl_lock);
1921
1922 /*
1923 * In case our MAC address is not correctly set,
1924 * we use a random but Nokia MAC.
1925 */
1926 memcpy(wl->mac_addr, nokia_oui, 3);
1927 get_random_bytes(wl->mac_addr + 3, 3);
1928
1929 wl->state = WL1271_STATE_OFF;
1930 mutex_init(&wl->mutex);
1931
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001932 /* This is the only SPI value that we need to set here, the rest
1933 * comes from the board-peripherals file */
1934 spi->bits_per_word = 32;
1935
1936 ret = spi_setup(spi);
1937 if (ret < 0) {
1938 wl1271_error("spi_setup failed");
1939 goto out_free;
1940 }
1941
1942 wl->set_power = pdata->set_power;
1943 if (!wl->set_power) {
1944 wl1271_error("set power function missing in platform data");
1945 ret = -ENODEV;
1946 goto out_free;
1947 }
1948
1949 wl->irq = spi->irq;
1950 if (wl->irq < 0) {
1951 wl1271_error("irq missing in platform data");
1952 ret = -ENODEV;
1953 goto out_free;
1954 }
1955
1956 ret = request_irq(wl->irq, wl1271_irq, 0, DRIVER_NAME, wl);
1957 if (ret < 0) {
1958 wl1271_error("request_irq() failed: %d", ret);
1959 goto out_free;
1960 }
1961
1962 set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING);
1963
1964 disable_irq(wl->irq);
1965
1966 ret = platform_device_register(&wl1271_device);
1967 if (ret) {
1968 wl1271_error("couldn't register platform device");
1969 goto out_irq;
1970 }
1971 dev_set_drvdata(&wl1271_device.dev, wl);
1972
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001973 /* Apply default driver configuration. */
1974 wl1271_conf_init(wl);
1975
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001976 ret = wl1271_init_ieee80211(wl);
1977 if (ret)
1978 goto out_platform;
1979
1980 ret = wl1271_register_hw(wl);
1981 if (ret)
1982 goto out_platform;
1983
1984 wl1271_debugfs_init(wl);
1985
1986 wl1271_notice("initialized");
1987
1988 return 0;
1989
1990 out_platform:
1991 platform_device_unregister(&wl1271_device);
1992
1993 out_irq:
1994 free_irq(wl->irq, wl);
1995
1996 out_free:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001997 ieee80211_free_hw(hw);
1998
1999 return ret;
2000}
2001
2002static int __devexit wl1271_remove(struct spi_device *spi)
2003{
2004 struct wl1271 *wl = dev_get_drvdata(&spi->dev);
2005
2006 ieee80211_unregister_hw(wl->hw);
2007
2008 wl1271_debugfs_exit(wl);
2009 platform_device_unregister(&wl1271_device);
2010 free_irq(wl->irq, wl);
2011 kfree(wl->target_mem_map);
Juuso Oikarinen1fba4972009-10-08 21:56:32 +03002012 vfree(wl->fw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002013 wl->fw = NULL;
2014 kfree(wl->nvs);
2015 wl->nvs = NULL;
2016
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002017 kfree(wl->fw_status);
2018 kfree(wl->tx_res_if);
2019
2020 ieee80211_free_hw(wl->hw);
2021
2022 return 0;
2023}
2024
2025
2026static struct spi_driver wl1271_spi_driver = {
2027 .driver = {
2028 .name = "wl1271",
2029 .bus = &spi_bus_type,
2030 .owner = THIS_MODULE,
2031 },
2032
2033 .probe = wl1271_probe,
2034 .remove = __devexit_p(wl1271_remove),
2035};
2036
2037static int __init wl1271_init(void)
2038{
2039 int ret;
2040
2041 ret = spi_register_driver(&wl1271_spi_driver);
2042 if (ret < 0) {
2043 wl1271_error("failed to register spi driver: %d", ret);
2044 goto out;
2045 }
2046
2047out:
2048 return ret;
2049}
2050
2051static void __exit wl1271_exit(void)
2052{
2053 spi_unregister_driver(&wl1271_spi_driver);
2054
2055 wl1271_notice("unloaded");
2056}
2057
2058module_init(wl1271_init);
2059module_exit(wl1271_exit);
2060
2061MODULE_LICENSE("GPL");
2062MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
Luciano Coelho2f018722009-10-08 21:56:27 +03002063MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
Ben Hutchings49f146d2009-11-07 22:02:15 +00002064MODULE_FIRMWARE(WL1271_FW_NAME);