blob: b33bdcc1eb4e820100d9126aef5e9acf996c39c4 [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);
Luciano Coelho98b2a682009-12-11 15:40:52 +0200402 wl->gpio_power = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300403}
404
405static void wl1271_power_on(struct wl1271 *wl)
406{
407 wl->set_power(true);
Luciano Coelho98b2a682009-12-11 15:40:52 +0200408 wl->gpio_power = true;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300409}
410
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300411static void wl1271_fw_status(struct wl1271 *wl,
412 struct wl1271_fw_status *status)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300413{
414 u32 total = 0;
415 int i;
416
Juuso Oikarinen74621412009-10-12 15:08:54 +0300417 wl1271_spi_read(wl, FW_STATUS_ADDR, status,
418 sizeof(*status), false);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300419
420 wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
421 "drv_rx_counter = %d, tx_results_counter = %d)",
422 status->intr,
423 status->fw_rx_counter,
424 status->drv_rx_counter,
425 status->tx_results_counter);
426
427 /* update number of available TX blocks */
428 for (i = 0; i < NUM_TX_QUEUES; i++) {
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300429 u32 cnt = le32_to_cpu(status->tx_released_blks[i]) -
430 wl->tx_blocks_freed[i];
431
432 wl->tx_blocks_freed[i] =
433 le32_to_cpu(status->tx_released_blks[i]);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300434 wl->tx_blocks_available += cnt;
435 total += cnt;
436 }
437
438 /* if more blocks are available now, schedule some tx work */
439 if (total && !skb_queue_empty(&wl->tx_queue))
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300440 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300441
442 /* update the host-chipset time offset */
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300443 wl->time_offset = jiffies_to_usecs(jiffies) -
444 le32_to_cpu(status->fw_localtime);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300445}
446
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300447static void wl1271_irq_work(struct work_struct *work)
448{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300449 int ret;
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300450 u32 intr;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300451 struct wl1271 *wl =
452 container_of(work, struct wl1271, irq_work);
453
454 mutex_lock(&wl->mutex);
455
456 wl1271_debug(DEBUG_IRQ, "IRQ work");
457
458 if (wl->state == WL1271_STATE_OFF)
459 goto out;
460
461 ret = wl1271_ps_elp_wakeup(wl, true);
462 if (ret < 0)
463 goto out;
464
Juuso Oikarinen74621412009-10-12 15:08:54 +0300465 wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300466
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300467 wl1271_fw_status(wl, wl->fw_status);
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300468 intr = le32_to_cpu(wl->fw_status->intr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300469 if (!intr) {
470 wl1271_debug(DEBUG_IRQ, "Zero interrupt received.");
471 goto out_sleep;
472 }
473
474 intr &= WL1271_INTR_MASK;
475
Juuso Oikarinen1fd27942009-10-13 12:47:54 +0300476 if (intr & WL1271_ACX_INTR_EVENT_A) {
477 bool do_ack = (intr & WL1271_ACX_INTR_EVENT_B) ? false : true;
478 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
479 wl1271_event_handle(wl, 0, do_ack);
480 }
481
482 if (intr & WL1271_ACX_INTR_EVENT_B) {
483 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
484 wl1271_event_handle(wl, 1, true);
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300485 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300486
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300487 if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
488 wl1271_debug(DEBUG_IRQ,
489 "WL1271_ACX_INTR_INIT_COMPLETE");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300490
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300491 if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
492 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300493
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300494 if (intr & WL1271_ACX_INTR_DATA) {
495 u8 tx_res_cnt = wl->fw_status->tx_results_counter -
496 wl->tx_results_count;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300497
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300498 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300499
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300500 /* check for tx results */
501 if (tx_res_cnt)
502 wl1271_tx_complete(wl, tx_res_cnt);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300503
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300504 wl1271_rx(wl, wl->fw_status);
505 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300506
507out_sleep:
Juuso Oikarinen74621412009-10-12 15:08:54 +0300508 wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK,
Luciano Coelho73d0a132009-08-11 11:58:27 +0300509 WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300510 wl1271_ps_elp_sleep(wl);
511
512out:
513 mutex_unlock(&wl->mutex);
514}
515
516static irqreturn_t wl1271_irq(int irq, void *cookie)
517{
518 struct wl1271 *wl;
519 unsigned long flags;
520
521 wl1271_debug(DEBUG_IRQ, "IRQ");
522
523 wl = cookie;
524
525 /* complete the ELP completion */
526 spin_lock_irqsave(&wl->wl_lock, flags);
527 if (wl->elp_compl) {
528 complete(wl->elp_compl);
529 wl->elp_compl = NULL;
530 }
531
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300532 ieee80211_queue_work(wl->hw, &wl->irq_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300533 spin_unlock_irqrestore(&wl->wl_lock, flags);
534
535 return IRQ_HANDLED;
536}
537
538static int wl1271_fetch_firmware(struct wl1271 *wl)
539{
540 const struct firmware *fw;
541 int ret;
542
543 ret = request_firmware(&fw, WL1271_FW_NAME, &wl->spi->dev);
544
545 if (ret < 0) {
546 wl1271_error("could not get firmware: %d", ret);
547 return ret;
548 }
549
550 if (fw->size % 4) {
551 wl1271_error("firmware size is not multiple of 32 bits: %zu",
552 fw->size);
553 ret = -EILSEQ;
554 goto out;
555 }
556
557 wl->fw_len = fw->size;
Juuso Oikarinen1fba4972009-10-08 21:56:32 +0300558 wl->fw = vmalloc(wl->fw_len);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300559
560 if (!wl->fw) {
561 wl1271_error("could not allocate memory for the firmware");
562 ret = -ENOMEM;
563 goto out;
564 }
565
566 memcpy(wl->fw, fw->data, wl->fw_len);
567
568 ret = 0;
569
570out:
571 release_firmware(fw);
572
573 return ret;
574}
575
576static int wl1271_fetch_nvs(struct wl1271 *wl)
577{
578 const struct firmware *fw;
579 int ret;
580
581 ret = request_firmware(&fw, WL1271_NVS_NAME, &wl->spi->dev);
582
583 if (ret < 0) {
584 wl1271_error("could not get nvs file: %d", ret);
585 return ret;
586 }
587
588 if (fw->size % 4) {
589 wl1271_error("nvs size is not multiple of 32 bits: %zu",
590 fw->size);
591 ret = -EILSEQ;
592 goto out;
593 }
594
595 wl->nvs_len = fw->size;
596 wl->nvs = kmalloc(wl->nvs_len, GFP_KERNEL);
597
598 if (!wl->nvs) {
599 wl1271_error("could not allocate memory for the nvs file");
600 ret = -ENOMEM;
601 goto out;
602 }
603
604 memcpy(wl->nvs, fw->data, wl->nvs_len);
605
606 ret = 0;
607
608out:
609 release_firmware(fw);
610
611 return ret;
612}
613
614static void wl1271_fw_wakeup(struct wl1271 *wl)
615{
616 u32 elp_reg;
617
618 elp_reg = ELPCTRL_WAKE_UP;
Juuso Oikarinen74621412009-10-12 15:08:54 +0300619 wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300620}
621
622static int wl1271_setup(struct wl1271 *wl)
623{
624 wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL);
625 if (!wl->fw_status)
626 return -ENOMEM;
627
628 wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
629 if (!wl->tx_res_if) {
630 kfree(wl->fw_status);
631 return -ENOMEM;
632 }
633
634 INIT_WORK(&wl->irq_work, wl1271_irq_work);
635 INIT_WORK(&wl->tx_work, wl1271_tx_work);
636 return 0;
637}
638
639static int wl1271_chip_wakeup(struct wl1271 *wl)
640{
Juuso Oikarinen451de972009-10-12 15:08:46 +0300641 struct wl1271_partition_set partition;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300642 int ret = 0;
643
644 wl1271_power_on(wl);
645 msleep(WL1271_POWER_ON_SLEEP);
646 wl1271_spi_reset(wl);
647 wl1271_spi_init(wl);
648
649 /* We don't need a real memory partition here, because we only want
650 * to use the registers at this point. */
Juuso Oikarinen451de972009-10-12 15:08:46 +0300651 memset(&partition, 0, sizeof(partition));
652 partition.reg.start = REGISTERS_BASE;
653 partition.reg.size = REGISTERS_DOWN_SIZE;
654 wl1271_set_partition(wl, &partition);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300655
656 /* ELP module wake up */
657 wl1271_fw_wakeup(wl);
658
659 /* whal_FwCtrl_BootSm() */
660
661 /* 0. read chip id from CHIP_ID */
Juuso Oikarinen74621412009-10-12 15:08:54 +0300662 wl->chip.id = wl1271_spi_read32(wl, CHIP_ID_B);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300663
664 /* 1. check if chip id is valid */
665
666 switch (wl->chip.id) {
667 case CHIP_ID_1271_PG10:
668 wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
669 wl->chip.id);
670
671 ret = wl1271_setup(wl);
672 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300673 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300674 break;
675 case CHIP_ID_1271_PG20:
676 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
677 wl->chip.id);
678
679 ret = wl1271_setup(wl);
680 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300681 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300682 break;
683 default:
684 wl1271_error("unsupported chip id: 0x%x", wl->chip.id);
685 ret = -ENODEV;
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300686 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300687 }
688
689 if (wl->fw == NULL) {
690 ret = wl1271_fetch_firmware(wl);
691 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300692 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300693 }
694
695 /* No NVS from netlink, try to get it from the filesystem */
696 if (wl->nvs == NULL) {
697 ret = wl1271_fetch_nvs(wl);
698 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300699 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300700 }
701
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300702 goto out;
703
704out_power_off:
705 wl1271_power_off(wl);
706
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300707out:
708 return ret;
709}
710
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300711int wl1271_plt_start(struct wl1271 *wl)
712{
713 int ret;
714
715 mutex_lock(&wl->mutex);
716
717 wl1271_notice("power up");
718
719 if (wl->state != WL1271_STATE_OFF) {
720 wl1271_error("cannot go into PLT state because not "
721 "in off state: %d", wl->state);
722 ret = -EBUSY;
723 goto out;
724 }
725
726 wl->state = WL1271_STATE_PLT;
727
728 ret = wl1271_chip_wakeup(wl);
729 if (ret < 0)
730 goto out;
731
732 ret = wl1271_boot(wl);
733 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300734 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300735
736 wl1271_notice("firmware booted in PLT mode (%s)", wl->chip.fw_ver);
737
738 ret = wl1271_plt_init(wl);
739 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300740 goto out_irq_disable;
741
Luciano Coelhobd5ea182009-10-13 12:47:58 +0300742 /* Make sure power saving is disabled */
743 ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
744 if (ret < 0)
745 goto out_irq_disable;
746
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300747 goto out;
748
749out_irq_disable:
750 wl1271_disable_interrupts(wl);
751
752out_power_off:
753 wl1271_power_off(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300754
755out:
756 mutex_unlock(&wl->mutex);
757
758 return ret;
759}
760
761int wl1271_plt_stop(struct wl1271 *wl)
762{
763 int ret = 0;
764
765 mutex_lock(&wl->mutex);
766
767 wl1271_notice("power down");
768
769 if (wl->state != WL1271_STATE_PLT) {
770 wl1271_error("cannot power down because not in PLT "
771 "state: %d", wl->state);
772 ret = -EBUSY;
773 goto out;
774 }
775
776 wl1271_disable_interrupts(wl);
777 wl1271_power_off(wl);
778
779 wl->state = WL1271_STATE_OFF;
Luciano Coelhobd5ea182009-10-13 12:47:58 +0300780 wl->rx_counter = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300781
782out:
783 mutex_unlock(&wl->mutex);
784
785 return ret;
786}
787
788
789static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
790{
791 struct wl1271 *wl = hw->priv;
792
793 skb_queue_tail(&wl->tx_queue, skb);
794
795 /*
796 * The chip specific setup must run before the first TX packet -
797 * before that, the tx_work will not be initialized!
798 */
799
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300800 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300801
802 /*
803 * The workqueue is slow to process the tx_queue and we need stop
804 * the queue here, otherwise the queue will get too long.
805 */
806 if (skb_queue_len(&wl->tx_queue) >= WL1271_TX_QUEUE_MAX_LENGTH) {
807 ieee80211_stop_queues(wl->hw);
808
809 /*
810 * FIXME: this is racy, the variable is not properly
811 * protected. Maybe fix this by removing the stupid
812 * variable altogether and checking the real queue state?
813 */
814 wl->tx_queue_stopped = true;
815 }
816
817 return NETDEV_TX_OK;
818}
819
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300820static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
821 void *arg)
822{
823 struct net_device *dev;
824 struct wireless_dev *wdev;
825 struct wiphy *wiphy;
826 struct ieee80211_hw *hw;
827 struct wl1271 *wl;
828 struct wl1271 *wl_temp;
829 struct in_device *idev;
830 struct in_ifaddr *ifa = arg;
831 int ret = 0;
832
833 /* FIXME: this ugly function should probably be implemented in the
834 * mac80211, and here should only be a simple callback handling actual
835 * setting of the filters. Now we need to dig up references to
836 * various structures to gain access to what we need.
837 * Also, because of this, there is no "initial" setting of the filter
838 * in "op_start", because we don't want to dig up struct net_device
839 * there - the filter will be set upon first change of the interface
840 * IP address. */
841
842 dev = ifa->ifa_dev->dev;
843
844 wdev = dev->ieee80211_ptr;
845 if (wdev == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200846 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300847
848 wiphy = wdev->wiphy;
849 if (wiphy == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200850 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300851
852 hw = wiphy_priv(wiphy);
853 if (hw == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200854 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300855
856 /* Check that the interface is one supported by this driver. */
857 wl_temp = hw->priv;
858 list_for_each_entry(wl, &wl_list, list) {
859 if (wl == wl_temp)
860 break;
861 }
862 if (wl == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200863 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300864
865 /* Get the interface IP address for the device. "ifa" will become
866 NULL if:
867 - there is no IPV4 protocol address configured
868 - there are multiple (virtual) IPV4 addresses configured
869 When "ifa" is NULL, filtering will be disabled.
870 */
871 ifa = NULL;
872 idev = dev->ip_ptr;
873 if (idev)
874 ifa = idev->ifa_list;
875
876 if (ifa && ifa->ifa_next)
877 ifa = NULL;
878
879 mutex_lock(&wl->mutex);
880
881 if (wl->state == WL1271_STATE_OFF)
882 goto out;
883
884 ret = wl1271_ps_elp_wakeup(wl, false);
885 if (ret < 0)
886 goto out;
887 if (ifa)
888 ret = wl1271_acx_arp_ip_filter(wl, true,
889 (u8 *)&ifa->ifa_address,
890 ACX_IPV4_VERSION);
891 else
892 ret = wl1271_acx_arp_ip_filter(wl, false, NULL,
893 ACX_IPV4_VERSION);
894 wl1271_ps_elp_sleep(wl);
895
896out:
897 mutex_unlock(&wl->mutex);
898
Luciano Coelho17d72652009-11-23 23:22:15 +0200899 return NOTIFY_OK;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300900}
901
902static struct notifier_block wl1271_dev_notifier = {
903 .notifier_call = wl1271_dev_notify,
904};
905
906
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300907static int wl1271_op_start(struct ieee80211_hw *hw)
908{
909 struct wl1271 *wl = hw->priv;
910 int ret = 0;
911
912 wl1271_debug(DEBUG_MAC80211, "mac80211 start");
913
914 mutex_lock(&wl->mutex);
915
916 if (wl->state != WL1271_STATE_OFF) {
917 wl1271_error("cannot start because not in off state: %d",
918 wl->state);
919 ret = -EBUSY;
920 goto out;
921 }
922
923 ret = wl1271_chip_wakeup(wl);
924 if (ret < 0)
925 goto out;
926
927 ret = wl1271_boot(wl);
928 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300929 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300930
931 ret = wl1271_hw_init(wl);
932 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300933 goto out_irq_disable;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300934
935 wl->state = WL1271_STATE_ON;
936
937 wl1271_info("firmware booted (%s)", wl->chip.fw_ver);
938
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300939 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300940
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300941out_irq_disable:
942 wl1271_disable_interrupts(wl);
943
944out_power_off:
945 wl1271_power_off(wl);
946
947out:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300948 mutex_unlock(&wl->mutex);
949
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300950 if (!ret) {
951 list_add(&wl->list, &wl_list);
952 register_inetaddr_notifier(&wl1271_dev_notifier);
953 }
954
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300955 return ret;
956}
957
958static void wl1271_op_stop(struct ieee80211_hw *hw)
959{
960 struct wl1271 *wl = hw->priv;
961 int i;
962
963 wl1271_info("down");
964
965 wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
966
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300967 unregister_inetaddr_notifier(&wl1271_dev_notifier);
968 list_del(&wl->list);
969
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300970 mutex_lock(&wl->mutex);
971
972 WARN_ON(wl->state != WL1271_STATE_ON);
973
974 if (wl->scanning) {
975 mutex_unlock(&wl->mutex);
976 ieee80211_scan_completed(wl->hw, true);
977 mutex_lock(&wl->mutex);
978 wl->scanning = false;
979 }
980
981 wl->state = WL1271_STATE_OFF;
982
983 wl1271_disable_interrupts(wl);
984
985 mutex_unlock(&wl->mutex);
986
987 cancel_work_sync(&wl->irq_work);
988 cancel_work_sync(&wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300989
990 mutex_lock(&wl->mutex);
991
992 /* let's notify MAC80211 about the remaining pending TX frames */
993 wl1271_tx_flush(wl);
994 wl1271_power_off(wl);
995
996 memset(wl->bssid, 0, ETH_ALEN);
997 memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1);
998 wl->ssid_len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300999 wl->bss_type = MAX_BSS_TYPE;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001000 wl->band = IEEE80211_BAND_2GHZ;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001001
1002 wl->rx_counter = 0;
1003 wl->elp = false;
1004 wl->psm = 0;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02001005 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001006 wl->tx_queue_stopped = false;
1007 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
1008 wl->tx_blocks_available = 0;
1009 wl->tx_results_count = 0;
1010 wl->tx_packets_count = 0;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001011 wl->tx_security_last_seq = 0;
1012 wl->tx_security_seq_16 = 0;
1013 wl->tx_security_seq_32 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001014 wl->time_offset = 0;
1015 wl->session_counter = 0;
Luciano Coelhod6e19d12009-10-12 15:08:43 +03001016 wl->joined = false;
1017
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001018 for (i = 0; i < NUM_TX_QUEUES; i++)
1019 wl->tx_blocks_freed[i] = 0;
1020
1021 wl1271_debugfs_reset(wl);
1022 mutex_unlock(&wl->mutex);
1023}
1024
1025static int wl1271_op_add_interface(struct ieee80211_hw *hw,
1026 struct ieee80211_if_init_conf *conf)
1027{
1028 struct wl1271 *wl = hw->priv;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001029 int ret = 0;
1030
John W. Linvillee5539bc2009-08-18 10:50:34 -04001031 wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
1032 conf->type, conf->mac_addr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001033
1034 mutex_lock(&wl->mutex);
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001035 if (wl->vif) {
1036 ret = -EBUSY;
1037 goto out;
1038 }
1039
1040 wl->vif = conf->vif;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001041
1042 switch (conf->type) {
1043 case NL80211_IFTYPE_STATION:
1044 wl->bss_type = BSS_TYPE_STA_BSS;
1045 break;
1046 case NL80211_IFTYPE_ADHOC:
1047 wl->bss_type = BSS_TYPE_IBSS;
1048 break;
1049 default:
1050 ret = -EOPNOTSUPP;
1051 goto out;
1052 }
1053
1054 /* FIXME: what if conf->mac_addr changes? */
1055
1056out:
1057 mutex_unlock(&wl->mutex);
1058 return ret;
1059}
1060
1061static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
1062 struct ieee80211_if_init_conf *conf)
1063{
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001064 struct wl1271 *wl = hw->priv;
1065
1066 mutex_lock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001067 wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001068 wl->vif = NULL;
1069 mutex_unlock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001070}
1071
1072#if 0
1073static int wl1271_op_config_interface(struct ieee80211_hw *hw,
1074 struct ieee80211_vif *vif,
1075 struct ieee80211_if_conf *conf)
1076{
1077 struct wl1271 *wl = hw->priv;
1078 struct sk_buff *beacon;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001079 int ret;
1080
David S. Miller32646902009-09-17 10:18:30 -07001081 wl1271_debug(DEBUG_MAC80211, "mac80211 config_interface bssid %pM",
1082 conf->bssid);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001083 wl1271_dump_ascii(DEBUG_MAC80211, "ssid: ", conf->ssid,
1084 conf->ssid_len);
1085
1086 mutex_lock(&wl->mutex);
1087
1088 ret = wl1271_ps_elp_wakeup(wl, false);
1089 if (ret < 0)
1090 goto out;
1091
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001092 if (memcmp(wl->bssid, conf->bssid, ETH_ALEN)) {
1093 wl1271_debug(DEBUG_MAC80211, "bssid changed");
1094
1095 memcpy(wl->bssid, conf->bssid, ETH_ALEN);
1096
1097 ret = wl1271_cmd_join(wl);
1098 if (ret < 0)
1099 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001100
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02001101 ret = wl1271_cmd_build_null_data(wl);
1102 if (ret < 0)
1103 goto out_sleep;
1104 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001105
1106 wl->ssid_len = conf->ssid_len;
1107 if (wl->ssid_len)
1108 memcpy(wl->ssid, conf->ssid, wl->ssid_len);
1109
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001110 if (conf->changed & IEEE80211_IFCC_BEACON) {
1111 beacon = ieee80211_beacon_get(hw, vif);
1112 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON,
1113 beacon->data, beacon->len);
1114
1115 if (ret < 0) {
1116 dev_kfree_skb(beacon);
1117 goto out_sleep;
1118 }
1119
1120 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE,
1121 beacon->data, beacon->len);
1122
1123 dev_kfree_skb(beacon);
1124
1125 if (ret < 0)
1126 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001127 }
1128
1129out_sleep:
1130 wl1271_ps_elp_sleep(wl);
1131
1132out:
1133 mutex_unlock(&wl->mutex);
1134
1135 return ret;
1136}
1137#endif
1138
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001139static int wl1271_join_channel(struct wl1271 *wl, int channel)
1140{
1141 int ret;
1142 /* we need to use a dummy BSSID for now */
1143 static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
1144 0xad, 0xbe, 0xef };
1145
1146 /* disable mac filter, so we hear everything */
1147 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1148
1149 wl->channel = channel;
1150 memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
1151
1152 ret = wl1271_cmd_join(wl);
1153 if (ret < 0)
1154 goto out;
1155
1156 wl->joined = true;
1157
1158out:
1159 return ret;
1160}
1161
1162static int wl1271_unjoin_channel(struct wl1271 *wl)
1163{
1164 int ret;
1165
1166 /* to stop listening to a channel, we disconnect */
1167 ret = wl1271_cmd_disconnect(wl);
1168 if (ret < 0)
1169 goto out;
1170
1171 wl->joined = false;
1172 wl->channel = 0;
1173 memset(wl->bssid, 0, ETH_ALEN);
1174 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
1175
1176out:
1177 return ret;
1178}
1179
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001180static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
1181{
1182 struct wl1271 *wl = hw->priv;
1183 struct ieee80211_conf *conf = &hw->conf;
1184 int channel, ret = 0;
1185
1186 channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
1187
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001188 wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s",
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001189 channel,
1190 conf->flags & IEEE80211_CONF_PS ? "on" : "off",
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001191 conf->power_level,
1192 conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001193
1194 mutex_lock(&wl->mutex);
1195
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001196 wl->band = conf->channel->band;
1197
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001198 ret = wl1271_ps_elp_wakeup(wl, false);
1199 if (ret < 0)
1200 goto out;
1201
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001202 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1203 if (conf->flags & IEEE80211_CONF_IDLE && wl->joined)
1204 wl1271_unjoin_channel(wl);
1205 else
1206 wl1271_join_channel(wl, channel);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001207 }
1208
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001209 /* if the channel changes while joined, join again */
1210 if (channel != wl->channel && wl->joined)
1211 wl1271_join_channel(wl, channel);
1212
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001213 if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) {
1214 wl1271_info("psm enabled");
1215
1216 wl->psm_requested = true;
1217
1218 /*
1219 * We enter PSM only if we're already associated.
1220 * If we're not, we'll enter it when joining an SSID,
1221 * through the bss_info_changed() hook.
1222 */
1223 ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
1224 } else if (!(conf->flags & IEEE80211_CONF_PS) &&
1225 wl->psm_requested) {
1226 wl1271_info("psm disabled");
1227
1228 wl->psm_requested = false;
1229
1230 if (wl->psm)
1231 ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE);
1232 }
1233
1234 if (conf->power_level != wl->power_level) {
1235 ret = wl1271_acx_tx_power(wl, conf->power_level);
1236 if (ret < 0)
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02001237 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001238
1239 wl->power_level = conf->power_level;
1240 }
1241
1242out_sleep:
1243 wl1271_ps_elp_sleep(wl);
1244
1245out:
1246 mutex_unlock(&wl->mutex);
1247
1248 return ret;
1249}
1250
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001251struct wl1271_filter_params {
1252 bool enabled;
1253 int mc_list_length;
1254 u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
1255};
1256
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001257static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count,
1258 struct dev_addr_list *mc_list)
1259{
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001260 struct wl1271_filter_params *fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001261 int i;
1262
Juuso Oikarinen74441132009-10-13 12:47:53 +03001263 fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001264 if (!fp) {
1265 wl1271_error("Out of memory setting filters.");
1266 return 0;
1267 }
1268
1269 /* update multicast filtering parameters */
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001270 fp->enabled = true;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001271 if (mc_count > ACX_MC_ADDRESS_GROUP_MAX) {
1272 mc_count = 0;
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001273 fp->enabled = false;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001274 }
1275
1276 fp->mc_list_length = 0;
1277 for (i = 0; i < mc_count; i++) {
1278 if (mc_list->da_addrlen == ETH_ALEN) {
1279 memcpy(fp->mc_list[fp->mc_list_length],
1280 mc_list->da_addr, ETH_ALEN);
1281 fp->mc_list_length++;
1282 } else
1283 wl1271_warning("Unknown mc address length.");
Juuso Oikarinen74441132009-10-13 12:47:53 +03001284 mc_list = mc_list->next;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001285 }
1286
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001287 return (u64)(unsigned long)fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001288}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001289
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001290#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
1291 FIF_ALLMULTI | \
1292 FIF_FCSFAIL | \
1293 FIF_BCN_PRBRESP_PROMISC | \
1294 FIF_CONTROL | \
1295 FIF_OTHER_BSS)
1296
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001297static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
1298 unsigned int changed,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001299 unsigned int *total, u64 multicast)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001300{
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001301 struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001302 struct wl1271 *wl = hw->priv;
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001303 int ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001304
1305 wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter");
1306
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001307 mutex_lock(&wl->mutex);
1308
1309 if (wl->state == WL1271_STATE_OFF)
1310 goto out;
1311
1312 ret = wl1271_ps_elp_wakeup(wl, false);
1313 if (ret < 0)
1314 goto out;
1315
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001316 *total &= WL1271_SUPPORTED_FILTERS;
1317 changed &= WL1271_SUPPORTED_FILTERS;
1318
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001319 if (*total & FIF_ALLMULTI)
1320 ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
1321 else if (fp)
1322 ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
1323 fp->mc_list,
1324 fp->mc_list_length);
1325 if (ret < 0)
1326 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001327
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001328 kfree(fp);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001329
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001330 /* FIXME: We still need to set our filters properly */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001331
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001332 /* determine, whether supported filter values have changed */
1333 if (changed == 0)
1334 goto out_sleep;
1335
1336 /* apply configured filters */
1337 ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
1338 if (ret < 0)
1339 goto out_sleep;
1340
1341out_sleep:
1342 wl1271_ps_elp_sleep(wl);
1343
1344out:
1345 mutex_unlock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001346}
1347
1348static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
1349 struct ieee80211_vif *vif,
1350 struct ieee80211_sta *sta,
1351 struct ieee80211_key_conf *key_conf)
1352{
1353 struct wl1271 *wl = hw->priv;
1354 const u8 *addr;
1355 int ret;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001356 u32 tx_seq_32 = 0;
1357 u16 tx_seq_16 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001358 u8 key_type;
1359
1360 static const u8 bcast_addr[ETH_ALEN] =
1361 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1362
1363 wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
1364
1365 addr = sta ? sta->addr : bcast_addr;
1366
1367 wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd);
1368 wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN);
1369 wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
1370 key_conf->alg, key_conf->keyidx,
1371 key_conf->keylen, key_conf->flags);
1372 wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
1373
1374 if (is_zero_ether_addr(addr)) {
1375 /* We dont support TX only encryption */
1376 ret = -EOPNOTSUPP;
1377 goto out;
1378 }
1379
1380 mutex_lock(&wl->mutex);
1381
1382 ret = wl1271_ps_elp_wakeup(wl, false);
1383 if (ret < 0)
1384 goto out_unlock;
1385
1386 switch (key_conf->alg) {
1387 case ALG_WEP:
1388 key_type = KEY_WEP;
1389
1390 key_conf->hw_key_idx = key_conf->keyidx;
1391 break;
1392 case ALG_TKIP:
1393 key_type = KEY_TKIP;
1394
1395 key_conf->hw_key_idx = key_conf->keyidx;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001396 tx_seq_32 = wl->tx_security_seq_32;
1397 tx_seq_16 = wl->tx_security_seq_16;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001398 break;
1399 case ALG_CCMP:
1400 key_type = KEY_AES;
1401
1402 key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001403 tx_seq_32 = wl->tx_security_seq_32;
1404 tx_seq_16 = wl->tx_security_seq_16;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001405 break;
1406 default:
1407 wl1271_error("Unknown key algo 0x%x", key_conf->alg);
1408
1409 ret = -EOPNOTSUPP;
1410 goto out_sleep;
1411 }
1412
1413 switch (cmd) {
1414 case SET_KEY:
1415 ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE,
1416 key_conf->keyidx, key_type,
1417 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001418 addr, tx_seq_32, tx_seq_16);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001419 if (ret < 0) {
1420 wl1271_error("Could not add or replace key");
1421 goto out_sleep;
1422 }
1423 break;
1424
1425 case DISABLE_KEY:
1426 ret = wl1271_cmd_set_key(wl, KEY_REMOVE,
1427 key_conf->keyidx, key_type,
1428 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001429 addr, 0, 0);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001430 if (ret < 0) {
1431 wl1271_error("Could not remove key");
1432 goto out_sleep;
1433 }
1434 break;
1435
1436 default:
1437 wl1271_error("Unsupported key cmd 0x%x", cmd);
1438 ret = -EOPNOTSUPP;
1439 goto out_sleep;
1440
1441 break;
1442 }
1443
1444out_sleep:
1445 wl1271_ps_elp_sleep(wl);
1446
1447out_unlock:
1448 mutex_unlock(&wl->mutex);
1449
1450out:
1451 return ret;
1452}
1453
1454static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
1455 struct cfg80211_scan_request *req)
1456{
1457 struct wl1271 *wl = hw->priv;
1458 int ret;
1459 u8 *ssid = NULL;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001460 size_t len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001461
1462 wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
1463
1464 if (req->n_ssids) {
1465 ssid = req->ssids[0].ssid;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001466 len = req->ssids[0].ssid_len;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001467 }
1468
1469 mutex_lock(&wl->mutex);
1470
1471 ret = wl1271_ps_elp_wakeup(wl, false);
1472 if (ret < 0)
1473 goto out;
1474
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001475 if (wl1271_11a_enabled())
1476 ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
1477 WL1271_SCAN_BAND_DUAL, 3);
1478 else
1479 ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
1480 WL1271_SCAN_BAND_2_4_GHZ, 3);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001481
1482 wl1271_ps_elp_sleep(wl);
1483
1484out:
1485 mutex_unlock(&wl->mutex);
1486
1487 return ret;
1488}
1489
1490static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1491{
1492 struct wl1271 *wl = hw->priv;
1493 int ret;
1494
1495 mutex_lock(&wl->mutex);
1496
1497 ret = wl1271_ps_elp_wakeup(wl, false);
1498 if (ret < 0)
1499 goto out;
1500
1501 ret = wl1271_acx_rts_threshold(wl, (u16) value);
1502 if (ret < 0)
1503 wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret);
1504
1505 wl1271_ps_elp_sleep(wl);
1506
1507out:
1508 mutex_unlock(&wl->mutex);
1509
1510 return ret;
1511}
1512
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001513static u32 wl1271_enabled_rates_get(struct wl1271 *wl, u64 basic_rate_set)
1514{
1515 struct ieee80211_supported_band *band;
1516 u32 enabled_rates = 0;
1517 int bit;
1518
1519 band = wl->hw->wiphy->bands[wl->band];
1520 for (bit = 0; bit < band->n_bitrates; bit++) {
1521 if (basic_rate_set & 0x1)
1522 enabled_rates |= band->bitrates[bit].hw_value;
1523 basic_rate_set >>= 1;
1524 }
1525
1526 return enabled_rates;
1527}
1528
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001529static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
1530 struct ieee80211_vif *vif,
1531 struct ieee80211_bss_conf *bss_conf,
1532 u32 changed)
1533{
1534 enum wl1271_cmd_ps_mode mode;
1535 struct wl1271 *wl = hw->priv;
1536 int ret;
1537
1538 wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed");
1539
1540 mutex_lock(&wl->mutex);
1541
1542 ret = wl1271_ps_elp_wakeup(wl, false);
1543 if (ret < 0)
1544 goto out;
1545
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001546 if ((changed & BSS_CHANGED_BSSID) &&
1547 /*
1548 * Now we know the correct bssid, so we send a new join command
1549 * and enable the BSSID filter
1550 */
1551 memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
1552 wl->rx_config |= CFG_BSSID_FILTER_EN;
1553 memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
Luciano Coelhobdcbbb92009-12-11 15:40:48 +02001554 ret = wl1271_cmd_build_null_data(wl);
1555 if (ret < 0) {
1556 wl1271_warning("cmd buld null data failed %d",
1557 ret);
1558 goto out_sleep;
1559 }
Luciano Coelhocd264762009-12-11 15:40:47 +02001560 ret = wl1271_cmd_join(wl);
1561 if (ret < 0) {
1562 wl1271_warning("cmd join failed %d", ret);
1563 goto out_sleep;
1564 }
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001565 wl->joined = true;
1566 }
1567
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001568 if (changed & BSS_CHANGED_ASSOC) {
1569 if (bss_conf->assoc) {
1570 wl->aid = bss_conf->aid;
1571
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001572 /*
1573 * with wl1271, we don't need to update the
1574 * beacon_int and dtim_period, because the firmware
1575 * updates it by itself when the first beacon is
1576 * received after a join.
1577 */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001578 ret = wl1271_cmd_build_ps_poll(wl, wl->aid);
1579 if (ret < 0)
1580 goto out_sleep;
1581
1582 ret = wl1271_acx_aid(wl, wl->aid);
1583 if (ret < 0)
1584 goto out_sleep;
1585
1586 /* If we want to go in PSM but we're not there yet */
1587 if (wl->psm_requested && !wl->psm) {
1588 mode = STATION_POWER_SAVE_MODE;
1589 ret = wl1271_ps_set_mode(wl, mode);
1590 if (ret < 0)
1591 goto out_sleep;
1592 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001593 } else {
1594 /* use defaults when not associated */
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001595 wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
1596 wl->aid = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001597 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001598
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001599 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001600
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001601 if (changed & BSS_CHANGED_ERP_SLOT) {
1602 if (bss_conf->use_short_slot)
1603 ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
1604 else
1605 ret = wl1271_acx_slot(wl, SLOT_TIME_LONG);
1606 if (ret < 0) {
1607 wl1271_warning("Set slot time failed %d", ret);
1608 goto out_sleep;
1609 }
1610 }
1611
1612 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
1613 if (bss_conf->use_short_preamble)
1614 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT);
1615 else
1616 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG);
1617 }
1618
1619 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
1620 if (bss_conf->use_cts_prot)
1621 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE);
1622 else
1623 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE);
1624 if (ret < 0) {
1625 wl1271_warning("Set ctsprotect failed %d", ret);
1626 goto out_sleep;
1627 }
1628 }
1629
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001630 if (changed & BSS_CHANGED_BASIC_RATES) {
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001631 wl->basic_rate_set = wl1271_enabled_rates_get(
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001632 wl, bss_conf->basic_rates);
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001633
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001634 ret = wl1271_acx_rate_policies(wl, wl->basic_rate_set);
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001635 if (ret < 0) {
1636 wl1271_warning("Set rate policies failed %d", ret);
1637 goto out_sleep;
1638 }
1639 }
1640
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001641out_sleep:
1642 wl1271_ps_elp_sleep(wl);
1643
1644out:
1645 mutex_unlock(&wl->mutex);
1646}
1647
1648
1649/* can't be const, mac80211 writes to this */
1650static struct ieee80211_rate wl1271_rates[] = {
1651 { .bitrate = 10,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001652 .hw_value = CONF_HW_BIT_RATE_1MBPS,
1653 .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001654 { .bitrate = 20,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001655 .hw_value = CONF_HW_BIT_RATE_2MBPS,
1656 .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001657 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
1658 { .bitrate = 55,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001659 .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
1660 .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001661 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
1662 { .bitrate = 110,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001663 .hw_value = CONF_HW_BIT_RATE_11MBPS,
1664 .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001665 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
1666 { .bitrate = 60,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001667 .hw_value = CONF_HW_BIT_RATE_6MBPS,
1668 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001669 { .bitrate = 90,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001670 .hw_value = CONF_HW_BIT_RATE_9MBPS,
1671 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001672 { .bitrate = 120,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001673 .hw_value = CONF_HW_BIT_RATE_12MBPS,
1674 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001675 { .bitrate = 180,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001676 .hw_value = CONF_HW_BIT_RATE_18MBPS,
1677 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001678 { .bitrate = 240,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001679 .hw_value = CONF_HW_BIT_RATE_24MBPS,
1680 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001681 { .bitrate = 360,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001682 .hw_value = CONF_HW_BIT_RATE_36MBPS,
1683 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001684 { .bitrate = 480,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001685 .hw_value = CONF_HW_BIT_RATE_48MBPS,
1686 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001687 { .bitrate = 540,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001688 .hw_value = CONF_HW_BIT_RATE_54MBPS,
1689 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001690};
1691
1692/* can't be const, mac80211 writes to this */
1693static struct ieee80211_channel wl1271_channels[] = {
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02001694 { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
1695 { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
1696 { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
1697 { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
1698 { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
1699 { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
1700 { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
1701 { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
1702 { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
1703 { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
1704 { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
1705 { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
1706 { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001707};
1708
1709/* can't be const, mac80211 writes to this */
1710static struct ieee80211_supported_band wl1271_band_2ghz = {
1711 .channels = wl1271_channels,
1712 .n_channels = ARRAY_SIZE(wl1271_channels),
1713 .bitrates = wl1271_rates,
1714 .n_bitrates = ARRAY_SIZE(wl1271_rates),
1715};
1716
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03001717/* 5 GHz data rates for WL1273 */
1718static struct ieee80211_rate wl1271_rates_5ghz[] = {
1719 { .bitrate = 60,
1720 .hw_value = CONF_HW_BIT_RATE_6MBPS,
1721 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
1722 { .bitrate = 90,
1723 .hw_value = CONF_HW_BIT_RATE_9MBPS,
1724 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
1725 { .bitrate = 120,
1726 .hw_value = CONF_HW_BIT_RATE_12MBPS,
1727 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
1728 { .bitrate = 180,
1729 .hw_value = CONF_HW_BIT_RATE_18MBPS,
1730 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
1731 { .bitrate = 240,
1732 .hw_value = CONF_HW_BIT_RATE_24MBPS,
1733 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
1734 { .bitrate = 360,
1735 .hw_value = CONF_HW_BIT_RATE_36MBPS,
1736 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
1737 { .bitrate = 480,
1738 .hw_value = CONF_HW_BIT_RATE_48MBPS,
1739 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
1740 { .bitrate = 540,
1741 .hw_value = CONF_HW_BIT_RATE_54MBPS,
1742 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
1743};
1744
1745/* 5 GHz band channels for WL1273 */
1746static struct ieee80211_channel wl1271_channels_5ghz[] = {
1747 { .hw_value = 183, .center_freq = 4915},
1748 { .hw_value = 184, .center_freq = 4920},
1749 { .hw_value = 185, .center_freq = 4925},
1750 { .hw_value = 187, .center_freq = 4935},
1751 { .hw_value = 188, .center_freq = 4940},
1752 { .hw_value = 189, .center_freq = 4945},
1753 { .hw_value = 192, .center_freq = 4960},
1754 { .hw_value = 196, .center_freq = 4980},
1755 { .hw_value = 7, .center_freq = 5035},
1756 { .hw_value = 8, .center_freq = 5040},
1757 { .hw_value = 9, .center_freq = 5045},
1758 { .hw_value = 11, .center_freq = 5055},
1759 { .hw_value = 12, .center_freq = 5060},
1760 { .hw_value = 16, .center_freq = 5080},
1761 { .hw_value = 34, .center_freq = 5170},
1762 { .hw_value = 36, .center_freq = 5180},
1763 { .hw_value = 38, .center_freq = 5190},
1764 { .hw_value = 40, .center_freq = 5200},
1765 { .hw_value = 42, .center_freq = 5210},
1766 { .hw_value = 44, .center_freq = 5220},
1767 { .hw_value = 46, .center_freq = 5230},
1768 { .hw_value = 48, .center_freq = 5240},
1769 { .hw_value = 52, .center_freq = 5260},
1770 { .hw_value = 56, .center_freq = 5280},
1771 { .hw_value = 60, .center_freq = 5300},
1772 { .hw_value = 64, .center_freq = 5320},
1773 { .hw_value = 100, .center_freq = 5500},
1774 { .hw_value = 104, .center_freq = 5520},
1775 { .hw_value = 108, .center_freq = 5540},
1776 { .hw_value = 112, .center_freq = 5560},
1777 { .hw_value = 116, .center_freq = 5580},
1778 { .hw_value = 120, .center_freq = 5600},
1779 { .hw_value = 124, .center_freq = 5620},
1780 { .hw_value = 128, .center_freq = 5640},
1781 { .hw_value = 132, .center_freq = 5660},
1782 { .hw_value = 136, .center_freq = 5680},
1783 { .hw_value = 140, .center_freq = 5700},
1784 { .hw_value = 149, .center_freq = 5745},
1785 { .hw_value = 153, .center_freq = 5765},
1786 { .hw_value = 157, .center_freq = 5785},
1787 { .hw_value = 161, .center_freq = 5805},
1788 { .hw_value = 165, .center_freq = 5825},
1789};
1790
1791
1792static struct ieee80211_supported_band wl1271_band_5ghz = {
1793 .channels = wl1271_channels_5ghz,
1794 .n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
1795 .bitrates = wl1271_rates_5ghz,
1796 .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
1797};
1798
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001799static const struct ieee80211_ops wl1271_ops = {
1800 .start = wl1271_op_start,
1801 .stop = wl1271_op_stop,
1802 .add_interface = wl1271_op_add_interface,
1803 .remove_interface = wl1271_op_remove_interface,
1804 .config = wl1271_op_config,
1805/* .config_interface = wl1271_op_config_interface, */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001806 .prepare_multicast = wl1271_op_prepare_multicast,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001807 .configure_filter = wl1271_op_configure_filter,
1808 .tx = wl1271_op_tx,
1809 .set_key = wl1271_op_set_key,
1810 .hw_scan = wl1271_op_hw_scan,
1811 .bss_info_changed = wl1271_op_bss_info_changed,
1812 .set_rts_threshold = wl1271_op_set_rts_threshold,
1813};
1814
1815static int wl1271_register_hw(struct wl1271 *wl)
1816{
1817 int ret;
1818
1819 if (wl->mac80211_registered)
1820 return 0;
1821
1822 SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
1823
1824 ret = ieee80211_register_hw(wl->hw);
1825 if (ret < 0) {
1826 wl1271_error("unable to register mac80211 hw: %d", ret);
1827 return ret;
1828 }
1829
1830 wl->mac80211_registered = true;
1831
1832 wl1271_notice("loaded");
1833
1834 return 0;
1835}
1836
1837static int wl1271_init_ieee80211(struct wl1271 *wl)
1838{
Juuso Oikarinen1e2b7972009-10-08 21:56:20 +03001839 /* The tx descriptor buffer and the TKIP space. */
1840 wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
1841 sizeof(struct wl1271_tx_hw_descr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001842
1843 /* unit us */
1844 /* FIXME: find a proper value */
1845 wl->hw->channel_change_time = 10000;
1846
1847 wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
Juuso Oikarinen19221672009-10-08 21:56:35 +03001848 IEEE80211_HW_NOISE_DBM |
Juuso Oikarinen03442a32009-11-23 23:22:14 +02001849 IEEE80211_HW_BEACON_FILTER |
1850 IEEE80211_HW_SUPPORTS_PS;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001851
1852 wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
1853 wl->hw->wiphy->max_scan_ssids = 1;
1854 wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz;
1855
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03001856 if (wl1271_11a_enabled())
1857 wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz;
1858
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001859 SET_IEEE80211_DEV(wl->hw, &wl->spi->dev);
1860
1861 return 0;
1862}
1863
1864static void wl1271_device_release(struct device *dev)
1865{
1866
1867}
1868
1869static struct platform_device wl1271_device = {
1870 .name = "wl1271",
1871 .id = -1,
1872
1873 /* device model insists to have a release function */
1874 .dev = {
1875 .release = wl1271_device_release,
1876 },
1877};
1878
1879#define WL1271_DEFAULT_CHANNEL 0
1880static int __devinit wl1271_probe(struct spi_device *spi)
1881{
1882 struct wl12xx_platform_data *pdata;
1883 struct ieee80211_hw *hw;
1884 struct wl1271 *wl;
1885 int ret, i;
1886 static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
1887
1888 pdata = spi->dev.platform_data;
1889 if (!pdata) {
1890 wl1271_error("no platform data");
1891 return -ENODEV;
1892 }
1893
1894 hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
1895 if (!hw) {
1896 wl1271_error("could not alloc ieee80211_hw");
1897 return -ENOMEM;
1898 }
1899
1900 wl = hw->priv;
1901 memset(wl, 0, sizeof(*wl));
1902
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001903 INIT_LIST_HEAD(&wl->list);
1904
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001905 wl->hw = hw;
1906 dev_set_drvdata(&spi->dev, wl);
1907 wl->spi = spi;
1908
1909 skb_queue_head_init(&wl->tx_queue);
1910
Juuso Oikarinen37b70a82009-10-08 21:56:21 +03001911 INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001912 wl->channel = WL1271_DEFAULT_CHANNEL;
1913 wl->scanning = false;
1914 wl->default_key = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001915 wl->rx_counter = 0;
1916 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
1917 wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
1918 wl->elp = false;
1919 wl->psm = 0;
1920 wl->psm_requested = false;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02001921 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001922 wl->tx_queue_stopped = false;
1923 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001924 wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001925 wl->band = IEEE80211_BAND_2GHZ;
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001926 wl->vif = NULL;
Luciano Coelhod6e19d12009-10-12 15:08:43 +03001927 wl->joined = false;
Luciano Coelho98b2a682009-12-11 15:40:52 +02001928 wl->gpio_power = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001929
Juuso Oikarinenbe7078c2009-10-08 21:56:26 +03001930 for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001931 wl->tx_frames[i] = NULL;
1932
1933 spin_lock_init(&wl->wl_lock);
1934
1935 /*
1936 * In case our MAC address is not correctly set,
1937 * we use a random but Nokia MAC.
1938 */
1939 memcpy(wl->mac_addr, nokia_oui, 3);
1940 get_random_bytes(wl->mac_addr + 3, 3);
1941
1942 wl->state = WL1271_STATE_OFF;
1943 mutex_init(&wl->mutex);
1944
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001945 /* This is the only SPI value that we need to set here, the rest
1946 * comes from the board-peripherals file */
1947 spi->bits_per_word = 32;
1948
1949 ret = spi_setup(spi);
1950 if (ret < 0) {
1951 wl1271_error("spi_setup failed");
1952 goto out_free;
1953 }
1954
1955 wl->set_power = pdata->set_power;
1956 if (!wl->set_power) {
1957 wl1271_error("set power function missing in platform data");
1958 ret = -ENODEV;
1959 goto out_free;
1960 }
1961
1962 wl->irq = spi->irq;
1963 if (wl->irq < 0) {
1964 wl1271_error("irq missing in platform data");
1965 ret = -ENODEV;
1966 goto out_free;
1967 }
1968
1969 ret = request_irq(wl->irq, wl1271_irq, 0, DRIVER_NAME, wl);
1970 if (ret < 0) {
1971 wl1271_error("request_irq() failed: %d", ret);
1972 goto out_free;
1973 }
1974
1975 set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING);
1976
1977 disable_irq(wl->irq);
1978
1979 ret = platform_device_register(&wl1271_device);
1980 if (ret) {
1981 wl1271_error("couldn't register platform device");
1982 goto out_irq;
1983 }
1984 dev_set_drvdata(&wl1271_device.dev, wl);
1985
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001986 /* Apply default driver configuration. */
1987 wl1271_conf_init(wl);
1988
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001989 ret = wl1271_init_ieee80211(wl);
1990 if (ret)
1991 goto out_platform;
1992
1993 ret = wl1271_register_hw(wl);
1994 if (ret)
1995 goto out_platform;
1996
1997 wl1271_debugfs_init(wl);
1998
1999 wl1271_notice("initialized");
2000
2001 return 0;
2002
2003 out_platform:
2004 platform_device_unregister(&wl1271_device);
2005
2006 out_irq:
2007 free_irq(wl->irq, wl);
2008
2009 out_free:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002010 ieee80211_free_hw(hw);
2011
2012 return ret;
2013}
2014
2015static int __devexit wl1271_remove(struct spi_device *spi)
2016{
2017 struct wl1271 *wl = dev_get_drvdata(&spi->dev);
2018
2019 ieee80211_unregister_hw(wl->hw);
2020
2021 wl1271_debugfs_exit(wl);
2022 platform_device_unregister(&wl1271_device);
2023 free_irq(wl->irq, wl);
2024 kfree(wl->target_mem_map);
Juuso Oikarinen1fba4972009-10-08 21:56:32 +03002025 vfree(wl->fw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002026 wl->fw = NULL;
2027 kfree(wl->nvs);
2028 wl->nvs = NULL;
2029
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002030 kfree(wl->fw_status);
2031 kfree(wl->tx_res_if);
2032
2033 ieee80211_free_hw(wl->hw);
2034
2035 return 0;
2036}
2037
2038
2039static struct spi_driver wl1271_spi_driver = {
2040 .driver = {
2041 .name = "wl1271",
2042 .bus = &spi_bus_type,
2043 .owner = THIS_MODULE,
2044 },
2045
2046 .probe = wl1271_probe,
2047 .remove = __devexit_p(wl1271_remove),
2048};
2049
2050static int __init wl1271_init(void)
2051{
2052 int ret;
2053
2054 ret = spi_register_driver(&wl1271_spi_driver);
2055 if (ret < 0) {
2056 wl1271_error("failed to register spi driver: %d", ret);
2057 goto out;
2058 }
2059
2060out:
2061 return ret;
2062}
2063
2064static void __exit wl1271_exit(void)
2065{
2066 spi_unregister_driver(&wl1271_spi_driver);
2067
2068 wl1271_notice("unloaded");
2069}
2070
2071module_init(wl1271_init);
2072module_exit(wl1271_exit);
2073
2074MODULE_LICENSE("GPL");
2075MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
Luciano Coelho2f018722009-10-08 21:56:27 +03002076MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
Ben Hutchings49f146d2009-11-07 22:02:15 +00002077MODULE_FIRMWARE(WL1271_FW_NAME);