blob: 147aab222354e71247ebcdf18aeb73012b57da6c [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,
Luciano Coelho1b38ea82009-12-11 15:40:51 +0200281 0xfc, 0x00, 0x80, 0x10, 0xf0, 0xf8,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300282 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 }
Luciano Coelho6e92b412009-12-11 15:40:50 +0200341 },
342 .itrim = {
343 .enable = false,
344 .timeout = 50000,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300345 }
346};
347
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300348static LIST_HEAD(wl_list);
349
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300350static void wl1271_conf_init(struct wl1271 *wl)
351{
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300352
353 /*
354 * This function applies the default configuration to the driver. This
355 * function is invoked upon driver load (spi probe.)
356 *
357 * The configuration is stored in a run-time structure in order to
358 * facilitate for run-time adjustment of any of the parameters. Making
359 * changes to the configuration structure will apply the new values on
360 * the next interface up (wl1271_op_start.)
361 */
362
363 /* apply driver default configuration */
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300364 memcpy(&wl->conf, &default_conf, sizeof(default_conf));
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300365
366 if (wl1271_11a_enabled())
367 wl->conf.init.genparam.single_dual_band = CONF_DUAL_BAND;
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300368}
369
370
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300371static int wl1271_plt_init(struct wl1271 *wl)
372{
373 int ret;
374
Luciano Coelho98b5dd52009-11-23 23:22:17 +0200375 ret = wl1271_cmd_general_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 Coelho98b5dd52009-11-23 23:22:17 +0200379 ret = wl1271_cmd_radio_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200380 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200381 return ret;
382
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300383 ret = wl1271_acx_init_mem_config(wl);
384 if (ret < 0)
385 return ret;
386
387 ret = wl1271_cmd_data_path(wl, wl->channel, 1);
388 if (ret < 0)
389 return ret;
390
391 return 0;
392}
393
394static void wl1271_disable_interrupts(struct wl1271 *wl)
395{
396 disable_irq(wl->irq);
397}
398
399static void wl1271_power_off(struct wl1271 *wl)
400{
401 wl->set_power(false);
402}
403
404static void wl1271_power_on(struct wl1271 *wl)
405{
406 wl->set_power(true);
407}
408
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300409static void wl1271_fw_status(struct wl1271 *wl,
410 struct wl1271_fw_status *status)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300411{
412 u32 total = 0;
413 int i;
414
Juuso Oikarinen74621412009-10-12 15:08:54 +0300415 wl1271_spi_read(wl, FW_STATUS_ADDR, status,
416 sizeof(*status), false);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300417
418 wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
419 "drv_rx_counter = %d, tx_results_counter = %d)",
420 status->intr,
421 status->fw_rx_counter,
422 status->drv_rx_counter,
423 status->tx_results_counter);
424
425 /* update number of available TX blocks */
426 for (i = 0; i < NUM_TX_QUEUES; i++) {
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300427 u32 cnt = le32_to_cpu(status->tx_released_blks[i]) -
428 wl->tx_blocks_freed[i];
429
430 wl->tx_blocks_freed[i] =
431 le32_to_cpu(status->tx_released_blks[i]);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300432 wl->tx_blocks_available += cnt;
433 total += cnt;
434 }
435
436 /* if more blocks are available now, schedule some tx work */
437 if (total && !skb_queue_empty(&wl->tx_queue))
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300438 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300439
440 /* update the host-chipset time offset */
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300441 wl->time_offset = jiffies_to_usecs(jiffies) -
442 le32_to_cpu(status->fw_localtime);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300443}
444
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300445static void wl1271_irq_work(struct work_struct *work)
446{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300447 int ret;
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300448 u32 intr;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300449 struct wl1271 *wl =
450 container_of(work, struct wl1271, irq_work);
451
452 mutex_lock(&wl->mutex);
453
454 wl1271_debug(DEBUG_IRQ, "IRQ work");
455
456 if (wl->state == WL1271_STATE_OFF)
457 goto out;
458
459 ret = wl1271_ps_elp_wakeup(wl, true);
460 if (ret < 0)
461 goto out;
462
Juuso Oikarinen74621412009-10-12 15:08:54 +0300463 wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300464
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300465 wl1271_fw_status(wl, wl->fw_status);
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300466 intr = le32_to_cpu(wl->fw_status->intr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300467 if (!intr) {
468 wl1271_debug(DEBUG_IRQ, "Zero interrupt received.");
469 goto out_sleep;
470 }
471
472 intr &= WL1271_INTR_MASK;
473
Juuso Oikarinen1fd27942009-10-13 12:47:54 +0300474 if (intr & WL1271_ACX_INTR_EVENT_A) {
475 bool do_ack = (intr & WL1271_ACX_INTR_EVENT_B) ? false : true;
476 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
477 wl1271_event_handle(wl, 0, do_ack);
478 }
479
480 if (intr & WL1271_ACX_INTR_EVENT_B) {
481 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
482 wl1271_event_handle(wl, 1, true);
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300483 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300484
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300485 if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
486 wl1271_debug(DEBUG_IRQ,
487 "WL1271_ACX_INTR_INIT_COMPLETE");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300488
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300489 if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
490 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300491
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300492 if (intr & WL1271_ACX_INTR_DATA) {
493 u8 tx_res_cnt = wl->fw_status->tx_results_counter -
494 wl->tx_results_count;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300495
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300496 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300497
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300498 /* check for tx results */
499 if (tx_res_cnt)
500 wl1271_tx_complete(wl, tx_res_cnt);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300501
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300502 wl1271_rx(wl, wl->fw_status);
503 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300504
505out_sleep:
Juuso Oikarinen74621412009-10-12 15:08:54 +0300506 wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK,
Luciano Coelho73d0a132009-08-11 11:58:27 +0300507 WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300508 wl1271_ps_elp_sleep(wl);
509
510out:
511 mutex_unlock(&wl->mutex);
512}
513
514static irqreturn_t wl1271_irq(int irq, void *cookie)
515{
516 struct wl1271 *wl;
517 unsigned long flags;
518
519 wl1271_debug(DEBUG_IRQ, "IRQ");
520
521 wl = cookie;
522
523 /* complete the ELP completion */
524 spin_lock_irqsave(&wl->wl_lock, flags);
525 if (wl->elp_compl) {
526 complete(wl->elp_compl);
527 wl->elp_compl = NULL;
528 }
529
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300530 ieee80211_queue_work(wl->hw, &wl->irq_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300531 spin_unlock_irqrestore(&wl->wl_lock, flags);
532
533 return IRQ_HANDLED;
534}
535
536static int wl1271_fetch_firmware(struct wl1271 *wl)
537{
538 const struct firmware *fw;
539 int ret;
540
541 ret = request_firmware(&fw, WL1271_FW_NAME, &wl->spi->dev);
542
543 if (ret < 0) {
544 wl1271_error("could not get firmware: %d", ret);
545 return ret;
546 }
547
548 if (fw->size % 4) {
549 wl1271_error("firmware size is not multiple of 32 bits: %zu",
550 fw->size);
551 ret = -EILSEQ;
552 goto out;
553 }
554
555 wl->fw_len = fw->size;
Juuso Oikarinen1fba4972009-10-08 21:56:32 +0300556 wl->fw = vmalloc(wl->fw_len);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300557
558 if (!wl->fw) {
559 wl1271_error("could not allocate memory for the firmware");
560 ret = -ENOMEM;
561 goto out;
562 }
563
564 memcpy(wl->fw, fw->data, wl->fw_len);
565
566 ret = 0;
567
568out:
569 release_firmware(fw);
570
571 return ret;
572}
573
574static int wl1271_fetch_nvs(struct wl1271 *wl)
575{
576 const struct firmware *fw;
577 int ret;
578
579 ret = request_firmware(&fw, WL1271_NVS_NAME, &wl->spi->dev);
580
581 if (ret < 0) {
582 wl1271_error("could not get nvs file: %d", ret);
583 return ret;
584 }
585
586 if (fw->size % 4) {
587 wl1271_error("nvs size is not multiple of 32 bits: %zu",
588 fw->size);
589 ret = -EILSEQ;
590 goto out;
591 }
592
593 wl->nvs_len = fw->size;
594 wl->nvs = kmalloc(wl->nvs_len, GFP_KERNEL);
595
596 if (!wl->nvs) {
597 wl1271_error("could not allocate memory for the nvs file");
598 ret = -ENOMEM;
599 goto out;
600 }
601
602 memcpy(wl->nvs, fw->data, wl->nvs_len);
603
604 ret = 0;
605
606out:
607 release_firmware(fw);
608
609 return ret;
610}
611
612static void wl1271_fw_wakeup(struct wl1271 *wl)
613{
614 u32 elp_reg;
615
616 elp_reg = ELPCTRL_WAKE_UP;
Juuso Oikarinen74621412009-10-12 15:08:54 +0300617 wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300618}
619
620static int wl1271_setup(struct wl1271 *wl)
621{
622 wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL);
623 if (!wl->fw_status)
624 return -ENOMEM;
625
626 wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
627 if (!wl->tx_res_if) {
628 kfree(wl->fw_status);
629 return -ENOMEM;
630 }
631
632 INIT_WORK(&wl->irq_work, wl1271_irq_work);
633 INIT_WORK(&wl->tx_work, wl1271_tx_work);
634 return 0;
635}
636
637static int wl1271_chip_wakeup(struct wl1271 *wl)
638{
Juuso Oikarinen451de972009-10-12 15:08:46 +0300639 struct wl1271_partition_set partition;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300640 int ret = 0;
641
642 wl1271_power_on(wl);
643 msleep(WL1271_POWER_ON_SLEEP);
644 wl1271_spi_reset(wl);
645 wl1271_spi_init(wl);
646
647 /* We don't need a real memory partition here, because we only want
648 * to use the registers at this point. */
Juuso Oikarinen451de972009-10-12 15:08:46 +0300649 memset(&partition, 0, sizeof(partition));
650 partition.reg.start = REGISTERS_BASE;
651 partition.reg.size = REGISTERS_DOWN_SIZE;
652 wl1271_set_partition(wl, &partition);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300653
654 /* ELP module wake up */
655 wl1271_fw_wakeup(wl);
656
657 /* whal_FwCtrl_BootSm() */
658
659 /* 0. read chip id from CHIP_ID */
Juuso Oikarinen74621412009-10-12 15:08:54 +0300660 wl->chip.id = wl1271_spi_read32(wl, CHIP_ID_B);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300661
662 /* 1. check if chip id is valid */
663
664 switch (wl->chip.id) {
665 case CHIP_ID_1271_PG10:
666 wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
667 wl->chip.id);
668
669 ret = wl1271_setup(wl);
670 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300671 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300672 break;
673 case CHIP_ID_1271_PG20:
674 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
675 wl->chip.id);
676
677 ret = wl1271_setup(wl);
678 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300679 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300680 break;
681 default:
682 wl1271_error("unsupported chip id: 0x%x", wl->chip.id);
683 ret = -ENODEV;
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300684 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300685 }
686
687 if (wl->fw == NULL) {
688 ret = wl1271_fetch_firmware(wl);
689 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300690 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300691 }
692
693 /* No NVS from netlink, try to get it from the filesystem */
694 if (wl->nvs == NULL) {
695 ret = wl1271_fetch_nvs(wl);
696 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300697 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300698 }
699
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300700 goto out;
701
702out_power_off:
703 wl1271_power_off(wl);
704
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300705out:
706 return ret;
707}
708
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300709int wl1271_plt_start(struct wl1271 *wl)
710{
711 int ret;
712
713 mutex_lock(&wl->mutex);
714
715 wl1271_notice("power up");
716
717 if (wl->state != WL1271_STATE_OFF) {
718 wl1271_error("cannot go into PLT state because not "
719 "in off state: %d", wl->state);
720 ret = -EBUSY;
721 goto out;
722 }
723
724 wl->state = WL1271_STATE_PLT;
725
726 ret = wl1271_chip_wakeup(wl);
727 if (ret < 0)
728 goto out;
729
730 ret = wl1271_boot(wl);
731 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300732 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300733
734 wl1271_notice("firmware booted in PLT mode (%s)", wl->chip.fw_ver);
735
736 ret = wl1271_plt_init(wl);
737 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300738 goto out_irq_disable;
739
Luciano Coelhobd5ea182009-10-13 12:47:58 +0300740 /* Make sure power saving is disabled */
741 ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
742 if (ret < 0)
743 goto out_irq_disable;
744
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300745 goto out;
746
747out_irq_disable:
748 wl1271_disable_interrupts(wl);
749
750out_power_off:
751 wl1271_power_off(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300752
753out:
754 mutex_unlock(&wl->mutex);
755
756 return ret;
757}
758
759int wl1271_plt_stop(struct wl1271 *wl)
760{
761 int ret = 0;
762
763 mutex_lock(&wl->mutex);
764
765 wl1271_notice("power down");
766
767 if (wl->state != WL1271_STATE_PLT) {
768 wl1271_error("cannot power down because not in PLT "
769 "state: %d", wl->state);
770 ret = -EBUSY;
771 goto out;
772 }
773
774 wl1271_disable_interrupts(wl);
775 wl1271_power_off(wl);
776
777 wl->state = WL1271_STATE_OFF;
Luciano Coelhobd5ea182009-10-13 12:47:58 +0300778 wl->rx_counter = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300779
780out:
781 mutex_unlock(&wl->mutex);
782
783 return ret;
784}
785
786
787static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
788{
789 struct wl1271 *wl = hw->priv;
790
791 skb_queue_tail(&wl->tx_queue, skb);
792
793 /*
794 * The chip specific setup must run before the first TX packet -
795 * before that, the tx_work will not be initialized!
796 */
797
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300798 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300799
800 /*
801 * The workqueue is slow to process the tx_queue and we need stop
802 * the queue here, otherwise the queue will get too long.
803 */
804 if (skb_queue_len(&wl->tx_queue) >= WL1271_TX_QUEUE_MAX_LENGTH) {
805 ieee80211_stop_queues(wl->hw);
806
807 /*
808 * FIXME: this is racy, the variable is not properly
809 * protected. Maybe fix this by removing the stupid
810 * variable altogether and checking the real queue state?
811 */
812 wl->tx_queue_stopped = true;
813 }
814
815 return NETDEV_TX_OK;
816}
817
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300818static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
819 void *arg)
820{
821 struct net_device *dev;
822 struct wireless_dev *wdev;
823 struct wiphy *wiphy;
824 struct ieee80211_hw *hw;
825 struct wl1271 *wl;
826 struct wl1271 *wl_temp;
827 struct in_device *idev;
828 struct in_ifaddr *ifa = arg;
829 int ret = 0;
830
831 /* FIXME: this ugly function should probably be implemented in the
832 * mac80211, and here should only be a simple callback handling actual
833 * setting of the filters. Now we need to dig up references to
834 * various structures to gain access to what we need.
835 * Also, because of this, there is no "initial" setting of the filter
836 * in "op_start", because we don't want to dig up struct net_device
837 * there - the filter will be set upon first change of the interface
838 * IP address. */
839
840 dev = ifa->ifa_dev->dev;
841
842 wdev = dev->ieee80211_ptr;
843 if (wdev == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200844 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300845
846 wiphy = wdev->wiphy;
847 if (wiphy == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200848 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300849
850 hw = wiphy_priv(wiphy);
851 if (hw == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200852 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300853
854 /* Check that the interface is one supported by this driver. */
855 wl_temp = hw->priv;
856 list_for_each_entry(wl, &wl_list, list) {
857 if (wl == wl_temp)
858 break;
859 }
860 if (wl == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200861 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300862
863 /* Get the interface IP address for the device. "ifa" will become
864 NULL if:
865 - there is no IPV4 protocol address configured
866 - there are multiple (virtual) IPV4 addresses configured
867 When "ifa" is NULL, filtering will be disabled.
868 */
869 ifa = NULL;
870 idev = dev->ip_ptr;
871 if (idev)
872 ifa = idev->ifa_list;
873
874 if (ifa && ifa->ifa_next)
875 ifa = NULL;
876
877 mutex_lock(&wl->mutex);
878
879 if (wl->state == WL1271_STATE_OFF)
880 goto out;
881
882 ret = wl1271_ps_elp_wakeup(wl, false);
883 if (ret < 0)
884 goto out;
885 if (ifa)
886 ret = wl1271_acx_arp_ip_filter(wl, true,
887 (u8 *)&ifa->ifa_address,
888 ACX_IPV4_VERSION);
889 else
890 ret = wl1271_acx_arp_ip_filter(wl, false, NULL,
891 ACX_IPV4_VERSION);
892 wl1271_ps_elp_sleep(wl);
893
894out:
895 mutex_unlock(&wl->mutex);
896
Luciano Coelho17d72652009-11-23 23:22:15 +0200897 return NOTIFY_OK;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300898}
899
900static struct notifier_block wl1271_dev_notifier = {
901 .notifier_call = wl1271_dev_notify,
902};
903
904
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300905static int wl1271_op_start(struct ieee80211_hw *hw)
906{
907 struct wl1271 *wl = hw->priv;
908 int ret = 0;
909
910 wl1271_debug(DEBUG_MAC80211, "mac80211 start");
911
912 mutex_lock(&wl->mutex);
913
914 if (wl->state != WL1271_STATE_OFF) {
915 wl1271_error("cannot start because not in off state: %d",
916 wl->state);
917 ret = -EBUSY;
918 goto out;
919 }
920
921 ret = wl1271_chip_wakeup(wl);
922 if (ret < 0)
923 goto out;
924
925 ret = wl1271_boot(wl);
926 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300927 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300928
929 ret = wl1271_hw_init(wl);
930 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300931 goto out_irq_disable;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300932
933 wl->state = WL1271_STATE_ON;
934
935 wl1271_info("firmware booted (%s)", wl->chip.fw_ver);
936
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300937 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300938
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300939out_irq_disable:
940 wl1271_disable_interrupts(wl);
941
942out_power_off:
943 wl1271_power_off(wl);
944
945out:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300946 mutex_unlock(&wl->mutex);
947
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300948 if (!ret) {
949 list_add(&wl->list, &wl_list);
950 register_inetaddr_notifier(&wl1271_dev_notifier);
951 }
952
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300953 return ret;
954}
955
956static void wl1271_op_stop(struct ieee80211_hw *hw)
957{
958 struct wl1271 *wl = hw->priv;
959 int i;
960
961 wl1271_info("down");
962
963 wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
964
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300965 unregister_inetaddr_notifier(&wl1271_dev_notifier);
966 list_del(&wl->list);
967
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300968 mutex_lock(&wl->mutex);
969
970 WARN_ON(wl->state != WL1271_STATE_ON);
971
972 if (wl->scanning) {
973 mutex_unlock(&wl->mutex);
974 ieee80211_scan_completed(wl->hw, true);
975 mutex_lock(&wl->mutex);
976 wl->scanning = false;
977 }
978
979 wl->state = WL1271_STATE_OFF;
980
981 wl1271_disable_interrupts(wl);
982
983 mutex_unlock(&wl->mutex);
984
985 cancel_work_sync(&wl->irq_work);
986 cancel_work_sync(&wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300987
988 mutex_lock(&wl->mutex);
989
990 /* let's notify MAC80211 about the remaining pending TX frames */
991 wl1271_tx_flush(wl);
992 wl1271_power_off(wl);
993
994 memset(wl->bssid, 0, ETH_ALEN);
995 memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1);
996 wl->ssid_len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300997 wl->bss_type = MAX_BSS_TYPE;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +0300998 wl->band = IEEE80211_BAND_2GHZ;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300999
1000 wl->rx_counter = 0;
1001 wl->elp = false;
1002 wl->psm = 0;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02001003 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001004 wl->tx_queue_stopped = false;
1005 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
1006 wl->tx_blocks_available = 0;
1007 wl->tx_results_count = 0;
1008 wl->tx_packets_count = 0;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001009 wl->tx_security_last_seq = 0;
1010 wl->tx_security_seq_16 = 0;
1011 wl->tx_security_seq_32 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001012 wl->time_offset = 0;
1013 wl->session_counter = 0;
Luciano Coelhod6e19d132009-10-12 15:08:43 +03001014 wl->joined = false;
1015
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001016 for (i = 0; i < NUM_TX_QUEUES; i++)
1017 wl->tx_blocks_freed[i] = 0;
1018
1019 wl1271_debugfs_reset(wl);
1020 mutex_unlock(&wl->mutex);
1021}
1022
1023static int wl1271_op_add_interface(struct ieee80211_hw *hw,
1024 struct ieee80211_if_init_conf *conf)
1025{
1026 struct wl1271 *wl = hw->priv;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001027 int ret = 0;
1028
John W. Linvillee5539bc2009-08-18 10:50:34 -04001029 wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
1030 conf->type, conf->mac_addr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001031
1032 mutex_lock(&wl->mutex);
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001033 if (wl->vif) {
1034 ret = -EBUSY;
1035 goto out;
1036 }
1037
1038 wl->vif = conf->vif;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001039
1040 switch (conf->type) {
1041 case NL80211_IFTYPE_STATION:
1042 wl->bss_type = BSS_TYPE_STA_BSS;
1043 break;
1044 case NL80211_IFTYPE_ADHOC:
1045 wl->bss_type = BSS_TYPE_IBSS;
1046 break;
1047 default:
1048 ret = -EOPNOTSUPP;
1049 goto out;
1050 }
1051
1052 /* FIXME: what if conf->mac_addr changes? */
1053
1054out:
1055 mutex_unlock(&wl->mutex);
1056 return ret;
1057}
1058
1059static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
1060 struct ieee80211_if_init_conf *conf)
1061{
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001062 struct wl1271 *wl = hw->priv;
1063
1064 mutex_lock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001065 wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001066 wl->vif = NULL;
1067 mutex_unlock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001068}
1069
1070#if 0
1071static int wl1271_op_config_interface(struct ieee80211_hw *hw,
1072 struct ieee80211_vif *vif,
1073 struct ieee80211_if_conf *conf)
1074{
1075 struct wl1271 *wl = hw->priv;
1076 struct sk_buff *beacon;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001077 int ret;
1078
David S. Miller32646902009-09-17 10:18:30 -07001079 wl1271_debug(DEBUG_MAC80211, "mac80211 config_interface bssid %pM",
1080 conf->bssid);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001081 wl1271_dump_ascii(DEBUG_MAC80211, "ssid: ", conf->ssid,
1082 conf->ssid_len);
1083
1084 mutex_lock(&wl->mutex);
1085
1086 ret = wl1271_ps_elp_wakeup(wl, false);
1087 if (ret < 0)
1088 goto out;
1089
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001090 if (memcmp(wl->bssid, conf->bssid, ETH_ALEN)) {
1091 wl1271_debug(DEBUG_MAC80211, "bssid changed");
1092
1093 memcpy(wl->bssid, conf->bssid, ETH_ALEN);
1094
1095 ret = wl1271_cmd_join(wl);
1096 if (ret < 0)
1097 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001098
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02001099 ret = wl1271_cmd_build_null_data(wl);
1100 if (ret < 0)
1101 goto out_sleep;
1102 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001103
1104 wl->ssid_len = conf->ssid_len;
1105 if (wl->ssid_len)
1106 memcpy(wl->ssid, conf->ssid, wl->ssid_len);
1107
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001108 if (conf->changed & IEEE80211_IFCC_BEACON) {
1109 beacon = ieee80211_beacon_get(hw, vif);
1110 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON,
1111 beacon->data, beacon->len);
1112
1113 if (ret < 0) {
1114 dev_kfree_skb(beacon);
1115 goto out_sleep;
1116 }
1117
1118 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE,
1119 beacon->data, beacon->len);
1120
1121 dev_kfree_skb(beacon);
1122
1123 if (ret < 0)
1124 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001125 }
1126
1127out_sleep:
1128 wl1271_ps_elp_sleep(wl);
1129
1130out:
1131 mutex_unlock(&wl->mutex);
1132
1133 return ret;
1134}
1135#endif
1136
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001137static int wl1271_join_channel(struct wl1271 *wl, int channel)
1138{
1139 int ret;
1140 /* we need to use a dummy BSSID for now */
1141 static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
1142 0xad, 0xbe, 0xef };
1143
1144 /* disable mac filter, so we hear everything */
1145 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1146
1147 wl->channel = channel;
1148 memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
1149
1150 ret = wl1271_cmd_join(wl);
1151 if (ret < 0)
1152 goto out;
1153
1154 wl->joined = true;
1155
1156out:
1157 return ret;
1158}
1159
1160static int wl1271_unjoin_channel(struct wl1271 *wl)
1161{
1162 int ret;
1163
1164 /* to stop listening to a channel, we disconnect */
1165 ret = wl1271_cmd_disconnect(wl);
1166 if (ret < 0)
1167 goto out;
1168
1169 wl->joined = false;
1170 wl->channel = 0;
1171 memset(wl->bssid, 0, ETH_ALEN);
1172 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
1173
1174out:
1175 return ret;
1176}
1177
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001178static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
1179{
1180 struct wl1271 *wl = hw->priv;
1181 struct ieee80211_conf *conf = &hw->conf;
1182 int channel, ret = 0;
1183
1184 channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
1185
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001186 wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s",
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001187 channel,
1188 conf->flags & IEEE80211_CONF_PS ? "on" : "off",
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001189 conf->power_level,
1190 conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001191
1192 mutex_lock(&wl->mutex);
1193
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001194 wl->band = conf->channel->band;
1195
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001196 ret = wl1271_ps_elp_wakeup(wl, false);
1197 if (ret < 0)
1198 goto out;
1199
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001200 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1201 if (conf->flags & IEEE80211_CONF_IDLE && wl->joined)
1202 wl1271_unjoin_channel(wl);
1203 else
1204 wl1271_join_channel(wl, channel);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001205 }
1206
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001207 /* if the channel changes while joined, join again */
1208 if (channel != wl->channel && wl->joined)
1209 wl1271_join_channel(wl, channel);
1210
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001211 if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) {
1212 wl1271_info("psm enabled");
1213
1214 wl->psm_requested = true;
1215
1216 /*
1217 * We enter PSM only if we're already associated.
1218 * If we're not, we'll enter it when joining an SSID,
1219 * through the bss_info_changed() hook.
1220 */
1221 ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
1222 } else if (!(conf->flags & IEEE80211_CONF_PS) &&
1223 wl->psm_requested) {
1224 wl1271_info("psm disabled");
1225
1226 wl->psm_requested = false;
1227
1228 if (wl->psm)
1229 ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE);
1230 }
1231
1232 if (conf->power_level != wl->power_level) {
1233 ret = wl1271_acx_tx_power(wl, conf->power_level);
1234 if (ret < 0)
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02001235 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001236
1237 wl->power_level = conf->power_level;
1238 }
1239
1240out_sleep:
1241 wl1271_ps_elp_sleep(wl);
1242
1243out:
1244 mutex_unlock(&wl->mutex);
1245
1246 return ret;
1247}
1248
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001249struct wl1271_filter_params {
1250 bool enabled;
1251 int mc_list_length;
1252 u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
1253};
1254
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001255static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count,
1256 struct dev_addr_list *mc_list)
1257{
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001258 struct wl1271_filter_params *fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001259 int i;
1260
Juuso Oikarinen74441132009-10-13 12:47:53 +03001261 fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001262 if (!fp) {
1263 wl1271_error("Out of memory setting filters.");
1264 return 0;
1265 }
1266
1267 /* update multicast filtering parameters */
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001268 fp->enabled = true;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001269 if (mc_count > ACX_MC_ADDRESS_GROUP_MAX) {
1270 mc_count = 0;
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001271 fp->enabled = false;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001272 }
1273
1274 fp->mc_list_length = 0;
1275 for (i = 0; i < mc_count; i++) {
1276 if (mc_list->da_addrlen == ETH_ALEN) {
1277 memcpy(fp->mc_list[fp->mc_list_length],
1278 mc_list->da_addr, ETH_ALEN);
1279 fp->mc_list_length++;
1280 } else
1281 wl1271_warning("Unknown mc address length.");
Juuso Oikarinen74441132009-10-13 12:47:53 +03001282 mc_list = mc_list->next;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001283 }
1284
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001285 return (u64)(unsigned long)fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001286}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001287
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001288#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
1289 FIF_ALLMULTI | \
1290 FIF_FCSFAIL | \
1291 FIF_BCN_PRBRESP_PROMISC | \
1292 FIF_CONTROL | \
1293 FIF_OTHER_BSS)
1294
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001295static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
1296 unsigned int changed,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001297 unsigned int *total, u64 multicast)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001298{
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001299 struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001300 struct wl1271 *wl = hw->priv;
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001301 int ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001302
1303 wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter");
1304
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001305 mutex_lock(&wl->mutex);
1306
1307 if (wl->state == WL1271_STATE_OFF)
1308 goto out;
1309
1310 ret = wl1271_ps_elp_wakeup(wl, false);
1311 if (ret < 0)
1312 goto out;
1313
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001314 *total &= WL1271_SUPPORTED_FILTERS;
1315 changed &= WL1271_SUPPORTED_FILTERS;
1316
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001317 if (*total & FIF_ALLMULTI)
1318 ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
1319 else if (fp)
1320 ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
1321 fp->mc_list,
1322 fp->mc_list_length);
1323 if (ret < 0)
1324 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001325
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001326 kfree(fp);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001327
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001328 /* FIXME: We still need to set our filters properly */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001329
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001330 /* determine, whether supported filter values have changed */
1331 if (changed == 0)
1332 goto out_sleep;
1333
1334 /* apply configured filters */
1335 ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
1336 if (ret < 0)
1337 goto out_sleep;
1338
1339out_sleep:
1340 wl1271_ps_elp_sleep(wl);
1341
1342out:
1343 mutex_unlock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001344}
1345
1346static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
1347 struct ieee80211_vif *vif,
1348 struct ieee80211_sta *sta,
1349 struct ieee80211_key_conf *key_conf)
1350{
1351 struct wl1271 *wl = hw->priv;
1352 const u8 *addr;
1353 int ret;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001354 u32 tx_seq_32 = 0;
1355 u16 tx_seq_16 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001356 u8 key_type;
1357
1358 static const u8 bcast_addr[ETH_ALEN] =
1359 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1360
1361 wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
1362
1363 addr = sta ? sta->addr : bcast_addr;
1364
1365 wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd);
1366 wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN);
1367 wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
1368 key_conf->alg, key_conf->keyidx,
1369 key_conf->keylen, key_conf->flags);
1370 wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
1371
1372 if (is_zero_ether_addr(addr)) {
1373 /* We dont support TX only encryption */
1374 ret = -EOPNOTSUPP;
1375 goto out;
1376 }
1377
1378 mutex_lock(&wl->mutex);
1379
1380 ret = wl1271_ps_elp_wakeup(wl, false);
1381 if (ret < 0)
1382 goto out_unlock;
1383
1384 switch (key_conf->alg) {
1385 case ALG_WEP:
1386 key_type = KEY_WEP;
1387
1388 key_conf->hw_key_idx = key_conf->keyidx;
1389 break;
1390 case ALG_TKIP:
1391 key_type = KEY_TKIP;
1392
1393 key_conf->hw_key_idx = key_conf->keyidx;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001394 tx_seq_32 = wl->tx_security_seq_32;
1395 tx_seq_16 = wl->tx_security_seq_16;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001396 break;
1397 case ALG_CCMP:
1398 key_type = KEY_AES;
1399
1400 key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001401 tx_seq_32 = wl->tx_security_seq_32;
1402 tx_seq_16 = wl->tx_security_seq_16;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001403 break;
1404 default:
1405 wl1271_error("Unknown key algo 0x%x", key_conf->alg);
1406
1407 ret = -EOPNOTSUPP;
1408 goto out_sleep;
1409 }
1410
1411 switch (cmd) {
1412 case SET_KEY:
1413 ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE,
1414 key_conf->keyidx, key_type,
1415 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001416 addr, tx_seq_32, tx_seq_16);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001417 if (ret < 0) {
1418 wl1271_error("Could not add or replace key");
1419 goto out_sleep;
1420 }
1421 break;
1422
1423 case DISABLE_KEY:
1424 ret = wl1271_cmd_set_key(wl, KEY_REMOVE,
1425 key_conf->keyidx, key_type,
1426 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001427 addr, 0, 0);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001428 if (ret < 0) {
1429 wl1271_error("Could not remove key");
1430 goto out_sleep;
1431 }
1432 break;
1433
1434 default:
1435 wl1271_error("Unsupported key cmd 0x%x", cmd);
1436 ret = -EOPNOTSUPP;
1437 goto out_sleep;
1438
1439 break;
1440 }
1441
1442out_sleep:
1443 wl1271_ps_elp_sleep(wl);
1444
1445out_unlock:
1446 mutex_unlock(&wl->mutex);
1447
1448out:
1449 return ret;
1450}
1451
1452static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
1453 struct cfg80211_scan_request *req)
1454{
1455 struct wl1271 *wl = hw->priv;
1456 int ret;
1457 u8 *ssid = NULL;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001458 size_t len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001459
1460 wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
1461
1462 if (req->n_ssids) {
1463 ssid = req->ssids[0].ssid;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001464 len = req->ssids[0].ssid_len;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001465 }
1466
1467 mutex_lock(&wl->mutex);
1468
1469 ret = wl1271_ps_elp_wakeup(wl, false);
1470 if (ret < 0)
1471 goto out;
1472
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001473 if (wl1271_11a_enabled())
1474 ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
1475 WL1271_SCAN_BAND_DUAL, 3);
1476 else
1477 ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
1478 WL1271_SCAN_BAND_2_4_GHZ, 3);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001479
1480 wl1271_ps_elp_sleep(wl);
1481
1482out:
1483 mutex_unlock(&wl->mutex);
1484
1485 return ret;
1486}
1487
1488static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1489{
1490 struct wl1271 *wl = hw->priv;
1491 int ret;
1492
1493 mutex_lock(&wl->mutex);
1494
1495 ret = wl1271_ps_elp_wakeup(wl, false);
1496 if (ret < 0)
1497 goto out;
1498
1499 ret = wl1271_acx_rts_threshold(wl, (u16) value);
1500 if (ret < 0)
1501 wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret);
1502
1503 wl1271_ps_elp_sleep(wl);
1504
1505out:
1506 mutex_unlock(&wl->mutex);
1507
1508 return ret;
1509}
1510
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001511static u32 wl1271_enabled_rates_get(struct wl1271 *wl, u64 basic_rate_set)
1512{
1513 struct ieee80211_supported_band *band;
1514 u32 enabled_rates = 0;
1515 int bit;
1516
1517 band = wl->hw->wiphy->bands[wl->band];
1518 for (bit = 0; bit < band->n_bitrates; bit++) {
1519 if (basic_rate_set & 0x1)
1520 enabled_rates |= band->bitrates[bit].hw_value;
1521 basic_rate_set >>= 1;
1522 }
1523
1524 return enabled_rates;
1525}
1526
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001527static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
1528 struct ieee80211_vif *vif,
1529 struct ieee80211_bss_conf *bss_conf,
1530 u32 changed)
1531{
1532 enum wl1271_cmd_ps_mode mode;
1533 struct wl1271 *wl = hw->priv;
1534 int ret;
1535
1536 wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed");
1537
1538 mutex_lock(&wl->mutex);
1539
1540 ret = wl1271_ps_elp_wakeup(wl, false);
1541 if (ret < 0)
1542 goto out;
1543
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001544 if ((changed & BSS_CHANGED_BSSID) &&
1545 /*
1546 * Now we know the correct bssid, so we send a new join command
1547 * and enable the BSSID filter
1548 */
1549 memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
1550 wl->rx_config |= CFG_BSSID_FILTER_EN;
1551 memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
Luciano Coelhobdcbbb92009-12-11 15:40:48 +02001552 ret = wl1271_cmd_build_null_data(wl);
1553 if (ret < 0) {
1554 wl1271_warning("cmd buld null data failed %d",
1555 ret);
1556 goto out_sleep;
1557 }
Luciano Coelhocd264762009-12-11 15:40:47 +02001558 ret = wl1271_cmd_join(wl);
1559 if (ret < 0) {
1560 wl1271_warning("cmd join failed %d", ret);
1561 goto out_sleep;
1562 }
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001563 wl->joined = true;
1564 }
1565
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001566 if (changed & BSS_CHANGED_ASSOC) {
1567 if (bss_conf->assoc) {
1568 wl->aid = bss_conf->aid;
1569
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001570 /*
1571 * with wl1271, we don't need to update the
1572 * beacon_int and dtim_period, because the firmware
1573 * updates it by itself when the first beacon is
1574 * received after a join.
1575 */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001576 ret = wl1271_cmd_build_ps_poll(wl, wl->aid);
1577 if (ret < 0)
1578 goto out_sleep;
1579
1580 ret = wl1271_acx_aid(wl, wl->aid);
1581 if (ret < 0)
1582 goto out_sleep;
1583
1584 /* If we want to go in PSM but we're not there yet */
1585 if (wl->psm_requested && !wl->psm) {
1586 mode = STATION_POWER_SAVE_MODE;
1587 ret = wl1271_ps_set_mode(wl, mode);
1588 if (ret < 0)
1589 goto out_sleep;
1590 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001591 } else {
1592 /* use defaults when not associated */
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001593 wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
1594 wl->aid = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001595 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001596
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001597 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001598
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001599 if (changed & BSS_CHANGED_ERP_SLOT) {
1600 if (bss_conf->use_short_slot)
1601 ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
1602 else
1603 ret = wl1271_acx_slot(wl, SLOT_TIME_LONG);
1604 if (ret < 0) {
1605 wl1271_warning("Set slot time failed %d", ret);
1606 goto out_sleep;
1607 }
1608 }
1609
1610 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
1611 if (bss_conf->use_short_preamble)
1612 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT);
1613 else
1614 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG);
1615 }
1616
1617 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
1618 if (bss_conf->use_cts_prot)
1619 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE);
1620 else
1621 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE);
1622 if (ret < 0) {
1623 wl1271_warning("Set ctsprotect failed %d", ret);
1624 goto out_sleep;
1625 }
1626 }
1627
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001628 if (changed & BSS_CHANGED_BASIC_RATES) {
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001629 wl->basic_rate_set = wl1271_enabled_rates_get(
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001630 wl, bss_conf->basic_rates);
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001631
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001632 ret = wl1271_acx_rate_policies(wl, wl->basic_rate_set);
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001633 if (ret < 0) {
1634 wl1271_warning("Set rate policies failed %d", ret);
1635 goto out_sleep;
1636 }
1637 }
1638
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001639out_sleep:
1640 wl1271_ps_elp_sleep(wl);
1641
1642out:
1643 mutex_unlock(&wl->mutex);
1644}
1645
1646
1647/* can't be const, mac80211 writes to this */
1648static struct ieee80211_rate wl1271_rates[] = {
1649 { .bitrate = 10,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001650 .hw_value = CONF_HW_BIT_RATE_1MBPS,
1651 .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001652 { .bitrate = 20,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001653 .hw_value = CONF_HW_BIT_RATE_2MBPS,
1654 .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001655 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
1656 { .bitrate = 55,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001657 .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
1658 .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001659 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
1660 { .bitrate = 110,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001661 .hw_value = CONF_HW_BIT_RATE_11MBPS,
1662 .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001663 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
1664 { .bitrate = 60,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001665 .hw_value = CONF_HW_BIT_RATE_6MBPS,
1666 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001667 { .bitrate = 90,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001668 .hw_value = CONF_HW_BIT_RATE_9MBPS,
1669 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001670 { .bitrate = 120,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001671 .hw_value = CONF_HW_BIT_RATE_12MBPS,
1672 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001673 { .bitrate = 180,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001674 .hw_value = CONF_HW_BIT_RATE_18MBPS,
1675 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001676 { .bitrate = 240,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001677 .hw_value = CONF_HW_BIT_RATE_24MBPS,
1678 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001679 { .bitrate = 360,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001680 .hw_value = CONF_HW_BIT_RATE_36MBPS,
1681 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001682 { .bitrate = 480,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001683 .hw_value = CONF_HW_BIT_RATE_48MBPS,
1684 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001685 { .bitrate = 540,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001686 .hw_value = CONF_HW_BIT_RATE_54MBPS,
1687 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001688};
1689
1690/* can't be const, mac80211 writes to this */
1691static struct ieee80211_channel wl1271_channels[] = {
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02001692 { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
1693 { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
1694 { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
1695 { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
1696 { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
1697 { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
1698 { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
1699 { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
1700 { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
1701 { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
1702 { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
1703 { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
1704 { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001705};
1706
1707/* can't be const, mac80211 writes to this */
1708static struct ieee80211_supported_band wl1271_band_2ghz = {
1709 .channels = wl1271_channels,
1710 .n_channels = ARRAY_SIZE(wl1271_channels),
1711 .bitrates = wl1271_rates,
1712 .n_bitrates = ARRAY_SIZE(wl1271_rates),
1713};
1714
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03001715/* 5 GHz data rates for WL1273 */
1716static struct ieee80211_rate wl1271_rates_5ghz[] = {
1717 { .bitrate = 60,
1718 .hw_value = CONF_HW_BIT_RATE_6MBPS,
1719 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
1720 { .bitrate = 90,
1721 .hw_value = CONF_HW_BIT_RATE_9MBPS,
1722 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
1723 { .bitrate = 120,
1724 .hw_value = CONF_HW_BIT_RATE_12MBPS,
1725 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
1726 { .bitrate = 180,
1727 .hw_value = CONF_HW_BIT_RATE_18MBPS,
1728 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
1729 { .bitrate = 240,
1730 .hw_value = CONF_HW_BIT_RATE_24MBPS,
1731 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
1732 { .bitrate = 360,
1733 .hw_value = CONF_HW_BIT_RATE_36MBPS,
1734 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
1735 { .bitrate = 480,
1736 .hw_value = CONF_HW_BIT_RATE_48MBPS,
1737 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
1738 { .bitrate = 540,
1739 .hw_value = CONF_HW_BIT_RATE_54MBPS,
1740 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
1741};
1742
1743/* 5 GHz band channels for WL1273 */
1744static struct ieee80211_channel wl1271_channels_5ghz[] = {
1745 { .hw_value = 183, .center_freq = 4915},
1746 { .hw_value = 184, .center_freq = 4920},
1747 { .hw_value = 185, .center_freq = 4925},
1748 { .hw_value = 187, .center_freq = 4935},
1749 { .hw_value = 188, .center_freq = 4940},
1750 { .hw_value = 189, .center_freq = 4945},
1751 { .hw_value = 192, .center_freq = 4960},
1752 { .hw_value = 196, .center_freq = 4980},
1753 { .hw_value = 7, .center_freq = 5035},
1754 { .hw_value = 8, .center_freq = 5040},
1755 { .hw_value = 9, .center_freq = 5045},
1756 { .hw_value = 11, .center_freq = 5055},
1757 { .hw_value = 12, .center_freq = 5060},
1758 { .hw_value = 16, .center_freq = 5080},
1759 { .hw_value = 34, .center_freq = 5170},
1760 { .hw_value = 36, .center_freq = 5180},
1761 { .hw_value = 38, .center_freq = 5190},
1762 { .hw_value = 40, .center_freq = 5200},
1763 { .hw_value = 42, .center_freq = 5210},
1764 { .hw_value = 44, .center_freq = 5220},
1765 { .hw_value = 46, .center_freq = 5230},
1766 { .hw_value = 48, .center_freq = 5240},
1767 { .hw_value = 52, .center_freq = 5260},
1768 { .hw_value = 56, .center_freq = 5280},
1769 { .hw_value = 60, .center_freq = 5300},
1770 { .hw_value = 64, .center_freq = 5320},
1771 { .hw_value = 100, .center_freq = 5500},
1772 { .hw_value = 104, .center_freq = 5520},
1773 { .hw_value = 108, .center_freq = 5540},
1774 { .hw_value = 112, .center_freq = 5560},
1775 { .hw_value = 116, .center_freq = 5580},
1776 { .hw_value = 120, .center_freq = 5600},
1777 { .hw_value = 124, .center_freq = 5620},
1778 { .hw_value = 128, .center_freq = 5640},
1779 { .hw_value = 132, .center_freq = 5660},
1780 { .hw_value = 136, .center_freq = 5680},
1781 { .hw_value = 140, .center_freq = 5700},
1782 { .hw_value = 149, .center_freq = 5745},
1783 { .hw_value = 153, .center_freq = 5765},
1784 { .hw_value = 157, .center_freq = 5785},
1785 { .hw_value = 161, .center_freq = 5805},
1786 { .hw_value = 165, .center_freq = 5825},
1787};
1788
1789
1790static struct ieee80211_supported_band wl1271_band_5ghz = {
1791 .channels = wl1271_channels_5ghz,
1792 .n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
1793 .bitrates = wl1271_rates_5ghz,
1794 .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
1795};
1796
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001797static const struct ieee80211_ops wl1271_ops = {
1798 .start = wl1271_op_start,
1799 .stop = wl1271_op_stop,
1800 .add_interface = wl1271_op_add_interface,
1801 .remove_interface = wl1271_op_remove_interface,
1802 .config = wl1271_op_config,
1803/* .config_interface = wl1271_op_config_interface, */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001804 .prepare_multicast = wl1271_op_prepare_multicast,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001805 .configure_filter = wl1271_op_configure_filter,
1806 .tx = wl1271_op_tx,
1807 .set_key = wl1271_op_set_key,
1808 .hw_scan = wl1271_op_hw_scan,
1809 .bss_info_changed = wl1271_op_bss_info_changed,
1810 .set_rts_threshold = wl1271_op_set_rts_threshold,
1811};
1812
1813static int wl1271_register_hw(struct wl1271 *wl)
1814{
1815 int ret;
1816
1817 if (wl->mac80211_registered)
1818 return 0;
1819
1820 SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
1821
1822 ret = ieee80211_register_hw(wl->hw);
1823 if (ret < 0) {
1824 wl1271_error("unable to register mac80211 hw: %d", ret);
1825 return ret;
1826 }
1827
1828 wl->mac80211_registered = true;
1829
1830 wl1271_notice("loaded");
1831
1832 return 0;
1833}
1834
1835static int wl1271_init_ieee80211(struct wl1271 *wl)
1836{
Juuso Oikarinen1e2b7972009-10-08 21:56:20 +03001837 /* The tx descriptor buffer and the TKIP space. */
1838 wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
1839 sizeof(struct wl1271_tx_hw_descr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001840
1841 /* unit us */
1842 /* FIXME: find a proper value */
1843 wl->hw->channel_change_time = 10000;
1844
1845 wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
Juuso Oikarinen19221672009-10-08 21:56:35 +03001846 IEEE80211_HW_NOISE_DBM |
Juuso Oikarinen03442a32009-11-23 23:22:14 +02001847 IEEE80211_HW_BEACON_FILTER |
1848 IEEE80211_HW_SUPPORTS_PS;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001849
1850 wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
1851 wl->hw->wiphy->max_scan_ssids = 1;
1852 wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz;
1853
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03001854 if (wl1271_11a_enabled())
1855 wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz;
1856
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001857 SET_IEEE80211_DEV(wl->hw, &wl->spi->dev);
1858
1859 return 0;
1860}
1861
1862static void wl1271_device_release(struct device *dev)
1863{
1864
1865}
1866
1867static struct platform_device wl1271_device = {
1868 .name = "wl1271",
1869 .id = -1,
1870
1871 /* device model insists to have a release function */
1872 .dev = {
1873 .release = wl1271_device_release,
1874 },
1875};
1876
1877#define WL1271_DEFAULT_CHANNEL 0
1878static int __devinit wl1271_probe(struct spi_device *spi)
1879{
1880 struct wl12xx_platform_data *pdata;
1881 struct ieee80211_hw *hw;
1882 struct wl1271 *wl;
1883 int ret, i;
1884 static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
1885
1886 pdata = spi->dev.platform_data;
1887 if (!pdata) {
1888 wl1271_error("no platform data");
1889 return -ENODEV;
1890 }
1891
1892 hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
1893 if (!hw) {
1894 wl1271_error("could not alloc ieee80211_hw");
1895 return -ENOMEM;
1896 }
1897
1898 wl = hw->priv;
1899 memset(wl, 0, sizeof(*wl));
1900
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001901 INIT_LIST_HEAD(&wl->list);
1902
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001903 wl->hw = hw;
1904 dev_set_drvdata(&spi->dev, wl);
1905 wl->spi = spi;
1906
1907 skb_queue_head_init(&wl->tx_queue);
1908
Juuso Oikarinen37b70a82009-10-08 21:56:21 +03001909 INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001910 wl->channel = WL1271_DEFAULT_CHANNEL;
1911 wl->scanning = false;
1912 wl->default_key = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001913 wl->rx_counter = 0;
1914 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
1915 wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
1916 wl->elp = false;
1917 wl->psm = 0;
1918 wl->psm_requested = false;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02001919 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001920 wl->tx_queue_stopped = false;
1921 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001922 wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001923 wl->band = IEEE80211_BAND_2GHZ;
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001924 wl->vif = NULL;
Luciano Coelhod6e19d132009-10-12 15:08:43 +03001925 wl->joined = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001926
Juuso Oikarinenbe7078c2009-10-08 21:56:26 +03001927 for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001928 wl->tx_frames[i] = NULL;
1929
1930 spin_lock_init(&wl->wl_lock);
1931
1932 /*
1933 * In case our MAC address is not correctly set,
1934 * we use a random but Nokia MAC.
1935 */
1936 memcpy(wl->mac_addr, nokia_oui, 3);
1937 get_random_bytes(wl->mac_addr + 3, 3);
1938
1939 wl->state = WL1271_STATE_OFF;
1940 mutex_init(&wl->mutex);
1941
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001942 /* This is the only SPI value that we need to set here, the rest
1943 * comes from the board-peripherals file */
1944 spi->bits_per_word = 32;
1945
1946 ret = spi_setup(spi);
1947 if (ret < 0) {
1948 wl1271_error("spi_setup failed");
1949 goto out_free;
1950 }
1951
1952 wl->set_power = pdata->set_power;
1953 if (!wl->set_power) {
1954 wl1271_error("set power function missing in platform data");
1955 ret = -ENODEV;
1956 goto out_free;
1957 }
1958
1959 wl->irq = spi->irq;
1960 if (wl->irq < 0) {
1961 wl1271_error("irq missing in platform data");
1962 ret = -ENODEV;
1963 goto out_free;
1964 }
1965
1966 ret = request_irq(wl->irq, wl1271_irq, 0, DRIVER_NAME, wl);
1967 if (ret < 0) {
1968 wl1271_error("request_irq() failed: %d", ret);
1969 goto out_free;
1970 }
1971
1972 set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING);
1973
1974 disable_irq(wl->irq);
1975
1976 ret = platform_device_register(&wl1271_device);
1977 if (ret) {
1978 wl1271_error("couldn't register platform device");
1979 goto out_irq;
1980 }
1981 dev_set_drvdata(&wl1271_device.dev, wl);
1982
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001983 /* Apply default driver configuration. */
1984 wl1271_conf_init(wl);
1985
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001986 ret = wl1271_init_ieee80211(wl);
1987 if (ret)
1988 goto out_platform;
1989
1990 ret = wl1271_register_hw(wl);
1991 if (ret)
1992 goto out_platform;
1993
1994 wl1271_debugfs_init(wl);
1995
1996 wl1271_notice("initialized");
1997
1998 return 0;
1999
2000 out_platform:
2001 platform_device_unregister(&wl1271_device);
2002
2003 out_irq:
2004 free_irq(wl->irq, wl);
2005
2006 out_free:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002007 ieee80211_free_hw(hw);
2008
2009 return ret;
2010}
2011
2012static int __devexit wl1271_remove(struct spi_device *spi)
2013{
2014 struct wl1271 *wl = dev_get_drvdata(&spi->dev);
2015
2016 ieee80211_unregister_hw(wl->hw);
2017
2018 wl1271_debugfs_exit(wl);
2019 platform_device_unregister(&wl1271_device);
2020 free_irq(wl->irq, wl);
2021 kfree(wl->target_mem_map);
Juuso Oikarinen1fba4972009-10-08 21:56:32 +03002022 vfree(wl->fw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002023 wl->fw = NULL;
2024 kfree(wl->nvs);
2025 wl->nvs = NULL;
2026
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002027 kfree(wl->fw_status);
2028 kfree(wl->tx_res_if);
2029
2030 ieee80211_free_hw(wl->hw);
2031
2032 return 0;
2033}
2034
2035
2036static struct spi_driver wl1271_spi_driver = {
2037 .driver = {
2038 .name = "wl1271",
2039 .bus = &spi_bus_type,
2040 .owner = THIS_MODULE,
2041 },
2042
2043 .probe = wl1271_probe,
2044 .remove = __devexit_p(wl1271_remove),
2045};
2046
2047static int __init wl1271_init(void)
2048{
2049 int ret;
2050
2051 ret = spi_register_driver(&wl1271_spi_driver);
2052 if (ret < 0) {
2053 wl1271_error("failed to register spi driver: %d", ret);
2054 goto out;
2055 }
2056
2057out:
2058 return ret;
2059}
2060
2061static void __exit wl1271_exit(void)
2062{
2063 spi_unregister_driver(&wl1271_spi_driver);
2064
2065 wl1271_notice("unloaded");
2066}
2067
2068module_init(wl1271_init);
2069module_exit(wl1271_exit);
2070
2071MODULE_LICENSE("GPL");
2072MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
Luciano Coelho2f018722009-10-08 21:56:27 +03002073MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
Ben Hutchings49f146d2009-11-07 22:02:15 +00002074MODULE_FIRMWARE(WL1271_FW_NAME);