blob: 5a6e4d44696250d8b5eeb922d911bd7abc1e8a7b [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,
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +020070 .rx_cca_threshold = 0,
71 .irq_blk_threshold = 0xFFFF,
72 .irq_pkt_threshold = 0,
73 .irq_timeout = 600,
Juuso Oikarinen8a080482009-10-13 12:47:44 +030074 .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,
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200175 .tx_compl_timeout = 700,
176 .tx_compl_threshold = 4
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300177 },
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 },
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200189 .synch_fail_thold = 10,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300190 .bss_lose_timeout = 100,
191 .beacon_rx_timeout = 10000,
192 .broadcast_timeout = 20000,
193 .rx_broadcast_in_ps = 1,
Luciano Coelho3ed8f2c2009-12-11 15:40:54 +0200194 .ps_poll_threshold = 20,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300195 .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 = {
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300229 .genparam = {
Luciano Coelhoc7c8adb2009-11-23 23:22:19 +0200230 .ref_clk = CONF_REF_CLK_38_4_E,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300231 .settling_time = 5,
232 .clk_valid_on_wakeup = 0,
233 .dc2dcmode = 0,
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300234 .single_dual_band = CONF_SINGLE_BAND,
Luciano Coelhoa3e84842009-12-11 15:40:42 +0200235 .tx_bip_fem_autodetect = 1,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300236 .tx_bip_fem_manufacturer = 1,
237 .settings = 1,
Luciano Coelho76c0f8d2009-12-11 15:40:41 +0200238 .sr_state = 1,
Juuso Oikarinen149e3872009-12-11 15:40:56 +0200239 .srf1 = { 0x07, 0x03, 0x18, 0x10, 0x05, 0xfb, 0xf0,
240 0xe8, 0, 0, 0, 0, 0, 0, 0, 0 },
241 .srf2 = { 0x07, 0x03, 0x18, 0x10, 0x05, 0xfb, 0xf0,
242 0xe8, 0, 0, 0, 0, 0, 0, 0, 0 },
243 .srf3 = { 0x07, 0x03, 0x18, 0x10, 0x05, 0xfb, 0xf0,
244 0xe8, 0, 0, 0, 0, 0, 0, 0, 0 },
Luciano Coelho76c0f8d2009-12-11 15:40:41 +0200245 .sr_debug_table = { 0, 0, 0, 0, 0, 0, 0, 0,
246 0, 0, 0, 0, 0, 0, 0, 0 },
247 .sr_sen_n_p = 0,
248 .sr_sen_n_p_gain = 0,
249 .sr_sen_nrn = 0,
250 .sr_sen_prn = 0,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300251 },
252 .radioparam = {
Luciano Coelhoa3e84842009-12-11 15:40:42 +0200253 .rx_trace_loss = 0x24,
254 .tx_trace_loss = 0x0,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300255 .rx_rssi_and_proc_compens = {
256 0xec, 0xf6, 0x00, 0x0c, 0x18, 0xf8,
Luciano Coelho1b38ea82009-12-11 15:40:51 +0200257 0xfc, 0x00, 0x80, 0x10, 0xf0, 0xf8,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300258 0x00, 0x0a, 0x14 },
259 .rx_trace_loss_5 = { 0, 0, 0, 0, 0, 0, 0 },
260 .tx_trace_loss_5 = { 0, 0, 0, 0, 0, 0, 0 },
261 .rx_rssi_and_proc_compens_5 = {
262 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
263 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
264 0x00, 0x00, 0x00 },
Luciano Coelhoa3e84842009-12-11 15:40:42 +0200265 .tx_ref_pd_voltage = 0x1a9,
266 .tx_ref_power = 0x80,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300267 .tx_offset_db = 0x0,
268 .tx_rate_limits_normal = {
Luciano Coelhoa3e84842009-12-11 15:40:42 +0200269 0x1d, 0x1f, 0x24, 0x28, 0x28, 0x29 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300270 .tx_rate_limits_degraded = {
Luciano Coelhoa3e84842009-12-11 15:40:42 +0200271 0x19, 0x1f, 0x22, 0x23, 0x27, 0x28 },
Luciano Coelhocf18be42009-12-11 15:40:43 +0200272 .tx_rate_limits_extreme = {
273 0x19, 0x1c, 0x1e, 0x20, 0x24, 0x25 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300274 .tx_channel_limits_11b = {
275 0x22, 0x50, 0x50, 0x50, 0x50, 0x50,
276 0x50, 0x50, 0x50, 0x50, 0x22, 0x50,
277 0x22, 0x50 },
278 .tx_channel_limits_ofdm = {
279 0x20, 0x50, 0x50, 0x50, 0x50, 0x50,
280 0x50, 0x50, 0x50, 0x50, 0x20, 0x50,
281 0x20, 0x50 },
282 .tx_pdv_rate_offsets = {
Luciano Coelhoa3e84842009-12-11 15:40:42 +0200283 0x07, 0x08, 0x04, 0x02, 0x02, 0x00 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300284 .tx_ibias = {
Luciano Coelhoa3e84842009-12-11 15:40:42 +0200285 0x11, 0x11, 0x15, 0x11, 0x15, 0x0f },
286 .rx_fem_insertion_loss = 0x0e,
Luciano Coelhocf18be42009-12-11 15:40:43 +0200287 .degraded_low_to_normal_threshold = 0x1e,
288 .degraded_normal_to_high_threshold = 0x2d,
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300289 .tx_ref_pd_voltage_5 = {
290 0x0190, 0x01a4, 0x01c3, 0x01d8,
291 0x020a, 0x021c },
292 .tx_ref_power_5 = {
293 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 },
294 .tx_offset_db_5 = {
295 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300296 .tx_rate_limits_normal_5 = {
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300297 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300298 .tx_rate_limits_degraded_5 = {
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300299 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 },
Luciano Coelhocf18be42009-12-11 15:40:43 +0200300 .tx_rate_limits_extreme_5 = {
301 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300302 .tx_channel_limits_ofdm_5 = {
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300303 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
304 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
305 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
306 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
307 0x50, 0x50, 0x50 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300308 .tx_pdv_rate_offsets_5 = {
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300309 0x01, 0x02, 0x02, 0x02, 0x02, 0x00 },
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300310 .tx_ibias_5 = {
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300311 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 },
312 .rx_fem_insertion_loss_5 = {
Luciano Coelhocf18be42009-12-11 15:40:43 +0200313 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 },
314 .degraded_low_to_normal_threshold_5 = 0x00,
315 .degraded_normal_to_high_threshold_5 = 0x00
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300316 }
Luciano Coelho6e92b412009-12-11 15:40:50 +0200317 },
318 .itrim = {
319 .enable = false,
320 .timeout = 50000,
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300321 }
322};
323
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300324static LIST_HEAD(wl_list);
325
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300326static void wl1271_conf_init(struct wl1271 *wl)
327{
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300328
329 /*
330 * This function applies the default configuration to the driver. This
331 * function is invoked upon driver load (spi probe.)
332 *
333 * The configuration is stored in a run-time structure in order to
334 * facilitate for run-time adjustment of any of the parameters. Making
335 * changes to the configuration structure will apply the new values on
336 * the next interface up (wl1271_op_start.)
337 */
338
339 /* apply driver default configuration */
Juuso Oikarinen8a080482009-10-13 12:47:44 +0300340 memcpy(&wl->conf, &default_conf, sizeof(default_conf));
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +0300341
342 if (wl1271_11a_enabled())
343 wl->conf.init.genparam.single_dual_band = CONF_DUAL_BAND;
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +0300344}
345
346
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300347static int wl1271_plt_init(struct wl1271 *wl)
348{
349 int ret;
350
Luciano Coelho98b5dd52009-11-23 23:22:17 +0200351 ret = wl1271_cmd_general_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200352 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200353 return ret;
354
Luciano Coelho98b5dd52009-11-23 23:22:17 +0200355 ret = wl1271_cmd_radio_parms(wl);
Luciano Coelho4a904062009-11-23 23:22:18 +0200356 if (ret < 0)
Luciano Coelhocc7defa2009-11-23 23:22:16 +0200357 return ret;
358
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300359 ret = wl1271_acx_init_mem_config(wl);
360 if (ret < 0)
361 return ret;
362
Luciano Coelho94210892009-12-11 15:40:55 +0200363 ret = wl1271_cmd_data_path(wl, 1);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300364 if (ret < 0)
365 return ret;
366
367 return 0;
368}
369
370static void wl1271_disable_interrupts(struct wl1271 *wl)
371{
372 disable_irq(wl->irq);
373}
374
375static void wl1271_power_off(struct wl1271 *wl)
376{
377 wl->set_power(false);
Luciano Coelho98b2a682009-12-11 15:40:52 +0200378 wl->gpio_power = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300379}
380
381static void wl1271_power_on(struct wl1271 *wl)
382{
383 wl->set_power(true);
Luciano Coelho98b2a682009-12-11 15:40:52 +0200384 wl->gpio_power = true;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300385}
386
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300387static void wl1271_fw_status(struct wl1271 *wl,
388 struct wl1271_fw_status *status)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300389{
390 u32 total = 0;
391 int i;
392
Juuso Oikarinen74621412009-10-12 15:08:54 +0300393 wl1271_spi_read(wl, FW_STATUS_ADDR, status,
394 sizeof(*status), false);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300395
396 wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
397 "drv_rx_counter = %d, tx_results_counter = %d)",
398 status->intr,
399 status->fw_rx_counter,
400 status->drv_rx_counter,
401 status->tx_results_counter);
402
403 /* update number of available TX blocks */
404 for (i = 0; i < NUM_TX_QUEUES; i++) {
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300405 u32 cnt = le32_to_cpu(status->tx_released_blks[i]) -
406 wl->tx_blocks_freed[i];
407
408 wl->tx_blocks_freed[i] =
409 le32_to_cpu(status->tx_released_blks[i]);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300410 wl->tx_blocks_available += cnt;
411 total += cnt;
412 }
413
414 /* if more blocks are available now, schedule some tx work */
415 if (total && !skb_queue_empty(&wl->tx_queue))
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300416 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300417
418 /* update the host-chipset time offset */
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300419 wl->time_offset = jiffies_to_usecs(jiffies) -
420 le32_to_cpu(status->fw_localtime);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300421}
422
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300423static void wl1271_irq_work(struct work_struct *work)
424{
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300425 int ret;
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300426 u32 intr;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300427 struct wl1271 *wl =
428 container_of(work, struct wl1271, irq_work);
429
430 mutex_lock(&wl->mutex);
431
432 wl1271_debug(DEBUG_IRQ, "IRQ work");
433
434 if (wl->state == WL1271_STATE_OFF)
435 goto out;
436
437 ret = wl1271_ps_elp_wakeup(wl, true);
438 if (ret < 0)
439 goto out;
440
Juuso Oikarinen74621412009-10-12 15:08:54 +0300441 wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300442
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300443 wl1271_fw_status(wl, wl->fw_status);
Luciano Coelhod0f63b22009-10-15 10:33:29 +0300444 intr = le32_to_cpu(wl->fw_status->intr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300445 if (!intr) {
446 wl1271_debug(DEBUG_IRQ, "Zero interrupt received.");
447 goto out_sleep;
448 }
449
450 intr &= WL1271_INTR_MASK;
451
Juuso Oikarinen1fd27942009-10-13 12:47:54 +0300452 if (intr & WL1271_ACX_INTR_EVENT_A) {
453 bool do_ack = (intr & WL1271_ACX_INTR_EVENT_B) ? false : true;
454 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
455 wl1271_event_handle(wl, 0, do_ack);
456 }
457
458 if (intr & WL1271_ACX_INTR_EVENT_B) {
459 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
460 wl1271_event_handle(wl, 1, true);
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300461 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300462
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300463 if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
464 wl1271_debug(DEBUG_IRQ,
465 "WL1271_ACX_INTR_INIT_COMPLETE");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300466
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300467 if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
468 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300469
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300470 if (intr & WL1271_ACX_INTR_DATA) {
471 u8 tx_res_cnt = wl->fw_status->tx_results_counter -
472 wl->tx_results_count;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300473
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300474 wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300475
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300476 /* check for tx results */
477 if (tx_res_cnt)
478 wl1271_tx_complete(wl, tx_res_cnt);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300479
Juuso Oikarinenc15f63b2009-10-12 15:08:50 +0300480 wl1271_rx(wl, wl->fw_status);
481 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300482
483out_sleep:
Juuso Oikarinen74621412009-10-12 15:08:54 +0300484 wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK,
Luciano Coelho73d0a132009-08-11 11:58:27 +0300485 WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK));
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300486 wl1271_ps_elp_sleep(wl);
487
488out:
489 mutex_unlock(&wl->mutex);
490}
491
492static irqreturn_t wl1271_irq(int irq, void *cookie)
493{
494 struct wl1271 *wl;
495 unsigned long flags;
496
497 wl1271_debug(DEBUG_IRQ, "IRQ");
498
499 wl = cookie;
500
501 /* complete the ELP completion */
502 spin_lock_irqsave(&wl->wl_lock, flags);
503 if (wl->elp_compl) {
504 complete(wl->elp_compl);
505 wl->elp_compl = NULL;
506 }
507
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300508 ieee80211_queue_work(wl->hw, &wl->irq_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300509 spin_unlock_irqrestore(&wl->wl_lock, flags);
510
511 return IRQ_HANDLED;
512}
513
514static int wl1271_fetch_firmware(struct wl1271 *wl)
515{
516 const struct firmware *fw;
517 int ret;
518
519 ret = request_firmware(&fw, WL1271_FW_NAME, &wl->spi->dev);
520
521 if (ret < 0) {
522 wl1271_error("could not get firmware: %d", ret);
523 return ret;
524 }
525
526 if (fw->size % 4) {
527 wl1271_error("firmware size is not multiple of 32 bits: %zu",
528 fw->size);
529 ret = -EILSEQ;
530 goto out;
531 }
532
533 wl->fw_len = fw->size;
Juuso Oikarinen1fba4972009-10-08 21:56:32 +0300534 wl->fw = vmalloc(wl->fw_len);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300535
536 if (!wl->fw) {
537 wl1271_error("could not allocate memory for the firmware");
538 ret = -ENOMEM;
539 goto out;
540 }
541
542 memcpy(wl->fw, fw->data, wl->fw_len);
543
544 ret = 0;
545
546out:
547 release_firmware(fw);
548
549 return ret;
550}
551
552static int wl1271_fetch_nvs(struct wl1271 *wl)
553{
554 const struct firmware *fw;
555 int ret;
556
557 ret = request_firmware(&fw, WL1271_NVS_NAME, &wl->spi->dev);
558
559 if (ret < 0) {
560 wl1271_error("could not get nvs file: %d", ret);
561 return ret;
562 }
563
564 if (fw->size % 4) {
565 wl1271_error("nvs size is not multiple of 32 bits: %zu",
566 fw->size);
567 ret = -EILSEQ;
568 goto out;
569 }
570
571 wl->nvs_len = fw->size;
572 wl->nvs = kmalloc(wl->nvs_len, GFP_KERNEL);
573
574 if (!wl->nvs) {
575 wl1271_error("could not allocate memory for the nvs file");
576 ret = -ENOMEM;
577 goto out;
578 }
579
580 memcpy(wl->nvs, fw->data, wl->nvs_len);
581
582 ret = 0;
583
584out:
585 release_firmware(fw);
586
587 return ret;
588}
589
590static void wl1271_fw_wakeup(struct wl1271 *wl)
591{
592 u32 elp_reg;
593
594 elp_reg = ELPCTRL_WAKE_UP;
Juuso Oikarinen74621412009-10-12 15:08:54 +0300595 wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300596}
597
598static int wl1271_setup(struct wl1271 *wl)
599{
600 wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL);
601 if (!wl->fw_status)
602 return -ENOMEM;
603
604 wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
605 if (!wl->tx_res_if) {
606 kfree(wl->fw_status);
607 return -ENOMEM;
608 }
609
610 INIT_WORK(&wl->irq_work, wl1271_irq_work);
611 INIT_WORK(&wl->tx_work, wl1271_tx_work);
612 return 0;
613}
614
615static int wl1271_chip_wakeup(struct wl1271 *wl)
616{
Juuso Oikarinen451de972009-10-12 15:08:46 +0300617 struct wl1271_partition_set partition;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300618 int ret = 0;
619
620 wl1271_power_on(wl);
621 msleep(WL1271_POWER_ON_SLEEP);
622 wl1271_spi_reset(wl);
623 wl1271_spi_init(wl);
624
625 /* We don't need a real memory partition here, because we only want
626 * to use the registers at this point. */
Juuso Oikarinen451de972009-10-12 15:08:46 +0300627 memset(&partition, 0, sizeof(partition));
628 partition.reg.start = REGISTERS_BASE;
629 partition.reg.size = REGISTERS_DOWN_SIZE;
630 wl1271_set_partition(wl, &partition);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300631
632 /* ELP module wake up */
633 wl1271_fw_wakeup(wl);
634
635 /* whal_FwCtrl_BootSm() */
636
637 /* 0. read chip id from CHIP_ID */
Juuso Oikarinen74621412009-10-12 15:08:54 +0300638 wl->chip.id = wl1271_spi_read32(wl, CHIP_ID_B);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300639
640 /* 1. check if chip id is valid */
641
642 switch (wl->chip.id) {
643 case CHIP_ID_1271_PG10:
644 wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
645 wl->chip.id);
646
647 ret = wl1271_setup(wl);
648 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300649 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300650 break;
651 case CHIP_ID_1271_PG20:
652 wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
653 wl->chip.id);
654
655 ret = wl1271_setup(wl);
656 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300657 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300658 break;
659 default:
660 wl1271_error("unsupported chip id: 0x%x", wl->chip.id);
661 ret = -ENODEV;
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300662 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300663 }
664
665 if (wl->fw == NULL) {
666 ret = wl1271_fetch_firmware(wl);
667 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300668 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300669 }
670
671 /* No NVS from netlink, try to get it from the filesystem */
672 if (wl->nvs == NULL) {
673 ret = wl1271_fetch_nvs(wl);
674 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300675 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300676 }
677
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300678 goto out;
679
680out_power_off:
681 wl1271_power_off(wl);
682
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300683out:
684 return ret;
685}
686
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300687int wl1271_plt_start(struct wl1271 *wl)
688{
689 int ret;
690
691 mutex_lock(&wl->mutex);
692
693 wl1271_notice("power up");
694
695 if (wl->state != WL1271_STATE_OFF) {
696 wl1271_error("cannot go into PLT state because not "
697 "in off state: %d", wl->state);
698 ret = -EBUSY;
699 goto out;
700 }
701
702 wl->state = WL1271_STATE_PLT;
703
704 ret = wl1271_chip_wakeup(wl);
705 if (ret < 0)
706 goto out;
707
708 ret = wl1271_boot(wl);
709 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300710 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300711
712 wl1271_notice("firmware booted in PLT mode (%s)", wl->chip.fw_ver);
713
714 ret = wl1271_plt_init(wl);
715 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300716 goto out_irq_disable;
717
Luciano Coelhobd5ea182009-10-13 12:47:58 +0300718 /* Make sure power saving is disabled */
719 ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
720 if (ret < 0)
721 goto out_irq_disable;
722
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300723 goto out;
724
725out_irq_disable:
726 wl1271_disable_interrupts(wl);
727
728out_power_off:
729 wl1271_power_off(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300730
731out:
732 mutex_unlock(&wl->mutex);
733
734 return ret;
735}
736
737int wl1271_plt_stop(struct wl1271 *wl)
738{
739 int ret = 0;
740
741 mutex_lock(&wl->mutex);
742
743 wl1271_notice("power down");
744
745 if (wl->state != WL1271_STATE_PLT) {
746 wl1271_error("cannot power down because not in PLT "
747 "state: %d", wl->state);
748 ret = -EBUSY;
749 goto out;
750 }
751
752 wl1271_disable_interrupts(wl);
753 wl1271_power_off(wl);
754
755 wl->state = WL1271_STATE_OFF;
Luciano Coelhobd5ea182009-10-13 12:47:58 +0300756 wl->rx_counter = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300757
758out:
759 mutex_unlock(&wl->mutex);
760
761 return ret;
762}
763
764
765static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
766{
767 struct wl1271 *wl = hw->priv;
768
769 skb_queue_tail(&wl->tx_queue, skb);
770
771 /*
772 * The chip specific setup must run before the first TX packet -
773 * before that, the tx_work will not be initialized!
774 */
775
Juuso Oikarinena64b07e2009-10-08 21:56:29 +0300776 ieee80211_queue_work(wl->hw, &wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300777
778 /*
779 * The workqueue is slow to process the tx_queue and we need stop
780 * the queue here, otherwise the queue will get too long.
781 */
782 if (skb_queue_len(&wl->tx_queue) >= WL1271_TX_QUEUE_MAX_LENGTH) {
783 ieee80211_stop_queues(wl->hw);
784
785 /*
786 * FIXME: this is racy, the variable is not properly
787 * protected. Maybe fix this by removing the stupid
788 * variable altogether and checking the real queue state?
789 */
790 wl->tx_queue_stopped = true;
791 }
792
793 return NETDEV_TX_OK;
794}
795
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300796static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
797 void *arg)
798{
799 struct net_device *dev;
800 struct wireless_dev *wdev;
801 struct wiphy *wiphy;
802 struct ieee80211_hw *hw;
803 struct wl1271 *wl;
804 struct wl1271 *wl_temp;
805 struct in_device *idev;
806 struct in_ifaddr *ifa = arg;
807 int ret = 0;
808
809 /* FIXME: this ugly function should probably be implemented in the
810 * mac80211, and here should only be a simple callback handling actual
811 * setting of the filters. Now we need to dig up references to
812 * various structures to gain access to what we need.
813 * Also, because of this, there is no "initial" setting of the filter
814 * in "op_start", because we don't want to dig up struct net_device
815 * there - the filter will be set upon first change of the interface
816 * IP address. */
817
818 dev = ifa->ifa_dev->dev;
819
820 wdev = dev->ieee80211_ptr;
821 if (wdev == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200822 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300823
824 wiphy = wdev->wiphy;
825 if (wiphy == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200826 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300827
828 hw = wiphy_priv(wiphy);
829 if (hw == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200830 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300831
832 /* Check that the interface is one supported by this driver. */
833 wl_temp = hw->priv;
834 list_for_each_entry(wl, &wl_list, list) {
835 if (wl == wl_temp)
836 break;
837 }
838 if (wl == NULL)
Luciano Coelho17d72652009-11-23 23:22:15 +0200839 return NOTIFY_DONE;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300840
841 /* Get the interface IP address for the device. "ifa" will become
842 NULL if:
843 - there is no IPV4 protocol address configured
844 - there are multiple (virtual) IPV4 addresses configured
845 When "ifa" is NULL, filtering will be disabled.
846 */
847 ifa = NULL;
848 idev = dev->ip_ptr;
849 if (idev)
850 ifa = idev->ifa_list;
851
852 if (ifa && ifa->ifa_next)
853 ifa = NULL;
854
855 mutex_lock(&wl->mutex);
856
857 if (wl->state == WL1271_STATE_OFF)
858 goto out;
859
860 ret = wl1271_ps_elp_wakeup(wl, false);
861 if (ret < 0)
862 goto out;
863 if (ifa)
864 ret = wl1271_acx_arp_ip_filter(wl, true,
865 (u8 *)&ifa->ifa_address,
866 ACX_IPV4_VERSION);
867 else
868 ret = wl1271_acx_arp_ip_filter(wl, false, NULL,
869 ACX_IPV4_VERSION);
870 wl1271_ps_elp_sleep(wl);
871
872out:
873 mutex_unlock(&wl->mutex);
874
Luciano Coelho17d72652009-11-23 23:22:15 +0200875 return NOTIFY_OK;
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300876}
877
878static struct notifier_block wl1271_dev_notifier = {
879 .notifier_call = wl1271_dev_notify,
880};
881
882
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300883static int wl1271_op_start(struct ieee80211_hw *hw)
884{
885 struct wl1271 *wl = hw->priv;
886 int ret = 0;
887
888 wl1271_debug(DEBUG_MAC80211, "mac80211 start");
889
890 mutex_lock(&wl->mutex);
891
892 if (wl->state != WL1271_STATE_OFF) {
893 wl1271_error("cannot start because not in off state: %d",
894 wl->state);
895 ret = -EBUSY;
896 goto out;
897 }
898
899 ret = wl1271_chip_wakeup(wl);
900 if (ret < 0)
901 goto out;
902
903 ret = wl1271_boot(wl);
904 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300905 goto out_power_off;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300906
907 ret = wl1271_hw_init(wl);
908 if (ret < 0)
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300909 goto out_irq_disable;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300910
911 wl->state = WL1271_STATE_ON;
912
913 wl1271_info("firmware booted (%s)", wl->chip.fw_ver);
914
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300915 goto out;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300916
Juuso Oikarineneb5b28d2009-10-13 12:47:45 +0300917out_irq_disable:
918 wl1271_disable_interrupts(wl);
919
920out_power_off:
921 wl1271_power_off(wl);
922
923out:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300924 mutex_unlock(&wl->mutex);
925
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300926 if (!ret) {
927 list_add(&wl->list, &wl_list);
928 register_inetaddr_notifier(&wl1271_dev_notifier);
929 }
930
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300931 return ret;
932}
933
934static void wl1271_op_stop(struct ieee80211_hw *hw)
935{
936 struct wl1271 *wl = hw->priv;
937 int i;
938
939 wl1271_info("down");
940
941 wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
942
Juuso Oikarinen01c09162009-10-13 12:47:55 +0300943 unregister_inetaddr_notifier(&wl1271_dev_notifier);
944 list_del(&wl->list);
945
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300946 mutex_lock(&wl->mutex);
947
948 WARN_ON(wl->state != WL1271_STATE_ON);
949
950 if (wl->scanning) {
951 mutex_unlock(&wl->mutex);
952 ieee80211_scan_completed(wl->hw, true);
953 mutex_lock(&wl->mutex);
954 wl->scanning = false;
955 }
956
957 wl->state = WL1271_STATE_OFF;
958
959 wl1271_disable_interrupts(wl);
960
961 mutex_unlock(&wl->mutex);
962
963 cancel_work_sync(&wl->irq_work);
964 cancel_work_sync(&wl->tx_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300965
966 mutex_lock(&wl->mutex);
967
968 /* let's notify MAC80211 about the remaining pending TX frames */
969 wl1271_tx_flush(wl);
970 wl1271_power_off(wl);
971
972 memset(wl->bssid, 0, ETH_ALEN);
973 memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1);
974 wl->ssid_len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300975 wl->bss_type = MAX_BSS_TYPE;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +0300976 wl->band = IEEE80211_BAND_2GHZ;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300977
978 wl->rx_counter = 0;
979 wl->elp = false;
980 wl->psm = 0;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +0200981 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300982 wl->tx_queue_stopped = false;
983 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
984 wl->tx_blocks_available = 0;
985 wl->tx_results_count = 0;
986 wl->tx_packets_count = 0;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +0300987 wl->tx_security_last_seq = 0;
988 wl->tx_security_seq_16 = 0;
989 wl->tx_security_seq_32 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300990 wl->time_offset = 0;
991 wl->session_counter = 0;
Luciano Coelhod6e19d12009-10-12 15:08:43 +0300992 wl->joined = false;
993
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300994 for (i = 0; i < NUM_TX_QUEUES; i++)
995 wl->tx_blocks_freed[i] = 0;
996
997 wl1271_debugfs_reset(wl);
998 mutex_unlock(&wl->mutex);
999}
1000
1001static int wl1271_op_add_interface(struct ieee80211_hw *hw,
1002 struct ieee80211_if_init_conf *conf)
1003{
1004 struct wl1271 *wl = hw->priv;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001005 int ret = 0;
1006
John W. Linvillee5539bc2009-08-18 10:50:34 -04001007 wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
1008 conf->type, conf->mac_addr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001009
1010 mutex_lock(&wl->mutex);
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001011 if (wl->vif) {
1012 ret = -EBUSY;
1013 goto out;
1014 }
1015
1016 wl->vif = conf->vif;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001017
1018 switch (conf->type) {
1019 case NL80211_IFTYPE_STATION:
1020 wl->bss_type = BSS_TYPE_STA_BSS;
1021 break;
1022 case NL80211_IFTYPE_ADHOC:
1023 wl->bss_type = BSS_TYPE_IBSS;
1024 break;
1025 default:
1026 ret = -EOPNOTSUPP;
1027 goto out;
1028 }
1029
1030 /* FIXME: what if conf->mac_addr changes? */
1031
1032out:
1033 mutex_unlock(&wl->mutex);
1034 return ret;
1035}
1036
1037static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
1038 struct ieee80211_if_init_conf *conf)
1039{
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001040 struct wl1271 *wl = hw->priv;
1041
1042 mutex_lock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001043 wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001044 wl->vif = NULL;
1045 mutex_unlock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001046}
1047
1048#if 0
1049static int wl1271_op_config_interface(struct ieee80211_hw *hw,
1050 struct ieee80211_vif *vif,
1051 struct ieee80211_if_conf *conf)
1052{
1053 struct wl1271 *wl = hw->priv;
1054 struct sk_buff *beacon;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001055 int ret;
1056
David S. Miller32646902009-09-17 10:18:30 -07001057 wl1271_debug(DEBUG_MAC80211, "mac80211 config_interface bssid %pM",
1058 conf->bssid);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001059 wl1271_dump_ascii(DEBUG_MAC80211, "ssid: ", conf->ssid,
1060 conf->ssid_len);
1061
1062 mutex_lock(&wl->mutex);
1063
1064 ret = wl1271_ps_elp_wakeup(wl, false);
1065 if (ret < 0)
1066 goto out;
1067
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001068 if (memcmp(wl->bssid, conf->bssid, ETH_ALEN)) {
1069 wl1271_debug(DEBUG_MAC80211, "bssid changed");
1070
1071 memcpy(wl->bssid, conf->bssid, ETH_ALEN);
1072
1073 ret = wl1271_cmd_join(wl);
1074 if (ret < 0)
1075 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001076
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02001077 ret = wl1271_cmd_build_null_data(wl);
1078 if (ret < 0)
1079 goto out_sleep;
1080 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001081
1082 wl->ssid_len = conf->ssid_len;
1083 if (wl->ssid_len)
1084 memcpy(wl->ssid, conf->ssid, wl->ssid_len);
1085
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001086 if (conf->changed & IEEE80211_IFCC_BEACON) {
1087 beacon = ieee80211_beacon_get(hw, vif);
1088 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON,
1089 beacon->data, beacon->len);
1090
1091 if (ret < 0) {
1092 dev_kfree_skb(beacon);
1093 goto out_sleep;
1094 }
1095
1096 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE,
1097 beacon->data, beacon->len);
1098
1099 dev_kfree_skb(beacon);
1100
1101 if (ret < 0)
1102 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001103 }
1104
1105out_sleep:
1106 wl1271_ps_elp_sleep(wl);
1107
1108out:
1109 mutex_unlock(&wl->mutex);
1110
1111 return ret;
1112}
1113#endif
1114
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001115static int wl1271_join_channel(struct wl1271 *wl, int channel)
1116{
1117 int ret;
1118 /* we need to use a dummy BSSID for now */
1119 static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
1120 0xad, 0xbe, 0xef };
1121
1122 /* disable mac filter, so we hear everything */
1123 wl->rx_config &= ~CFG_BSSID_FILTER_EN;
1124
1125 wl->channel = channel;
1126 memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
1127
1128 ret = wl1271_cmd_join(wl);
1129 if (ret < 0)
1130 goto out;
1131
1132 wl->joined = true;
1133
1134out:
1135 return ret;
1136}
1137
1138static int wl1271_unjoin_channel(struct wl1271 *wl)
1139{
1140 int ret;
1141
1142 /* to stop listening to a channel, we disconnect */
1143 ret = wl1271_cmd_disconnect(wl);
1144 if (ret < 0)
1145 goto out;
1146
1147 wl->joined = false;
1148 wl->channel = 0;
1149 memset(wl->bssid, 0, ETH_ALEN);
1150 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
1151
1152out:
1153 return ret;
1154}
1155
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001156static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
1157{
1158 struct wl1271 *wl = hw->priv;
1159 struct ieee80211_conf *conf = &hw->conf;
1160 int channel, ret = 0;
1161
1162 channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
1163
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001164 wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s",
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001165 channel,
1166 conf->flags & IEEE80211_CONF_PS ? "on" : "off",
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001167 conf->power_level,
1168 conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use");
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001169
1170 mutex_lock(&wl->mutex);
1171
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001172 wl->band = conf->channel->band;
1173
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001174 ret = wl1271_ps_elp_wakeup(wl, false);
1175 if (ret < 0)
1176 goto out;
1177
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001178 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1179 if (conf->flags & IEEE80211_CONF_IDLE && wl->joined)
1180 wl1271_unjoin_channel(wl);
1181 else
1182 wl1271_join_channel(wl, channel);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001183 }
1184
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001185 /* if the channel changes while joined, join again */
1186 if (channel != wl->channel && wl->joined)
1187 wl1271_join_channel(wl, channel);
1188
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001189 if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) {
1190 wl1271_info("psm enabled");
1191
1192 wl->psm_requested = true;
1193
1194 /*
1195 * We enter PSM only if we're already associated.
1196 * If we're not, we'll enter it when joining an SSID,
1197 * through the bss_info_changed() hook.
1198 */
1199 ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
1200 } else if (!(conf->flags & IEEE80211_CONF_PS) &&
1201 wl->psm_requested) {
1202 wl1271_info("psm disabled");
1203
1204 wl->psm_requested = false;
1205
1206 if (wl->psm)
1207 ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE);
1208 }
1209
1210 if (conf->power_level != wl->power_level) {
1211 ret = wl1271_acx_tx_power(wl, conf->power_level);
1212 if (ret < 0)
Juuso Oikarinenc6317a52009-11-02 20:22:08 +02001213 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001214
1215 wl->power_level = conf->power_level;
1216 }
1217
1218out_sleep:
1219 wl1271_ps_elp_sleep(wl);
1220
1221out:
1222 mutex_unlock(&wl->mutex);
1223
1224 return ret;
1225}
1226
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001227struct wl1271_filter_params {
1228 bool enabled;
1229 int mc_list_length;
1230 u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
1231};
1232
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001233static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count,
1234 struct dev_addr_list *mc_list)
1235{
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001236 struct wl1271_filter_params *fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001237 int i;
1238
Juuso Oikarinen74441132009-10-13 12:47:53 +03001239 fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001240 if (!fp) {
1241 wl1271_error("Out of memory setting filters.");
1242 return 0;
1243 }
1244
1245 /* update multicast filtering parameters */
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001246 fp->enabled = true;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001247 if (mc_count > ACX_MC_ADDRESS_GROUP_MAX) {
1248 mc_count = 0;
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001249 fp->enabled = false;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001250 }
1251
1252 fp->mc_list_length = 0;
1253 for (i = 0; i < mc_count; i++) {
1254 if (mc_list->da_addrlen == ETH_ALEN) {
1255 memcpy(fp->mc_list[fp->mc_list_length],
1256 mc_list->da_addr, ETH_ALEN);
1257 fp->mc_list_length++;
1258 } else
1259 wl1271_warning("Unknown mc address length.");
Juuso Oikarinen74441132009-10-13 12:47:53 +03001260 mc_list = mc_list->next;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001261 }
1262
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001263 return (u64)(unsigned long)fp;
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001264}
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001265
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001266#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
1267 FIF_ALLMULTI | \
1268 FIF_FCSFAIL | \
1269 FIF_BCN_PRBRESP_PROMISC | \
1270 FIF_CONTROL | \
1271 FIF_OTHER_BSS)
1272
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001273static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
1274 unsigned int changed,
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001275 unsigned int *total, u64 multicast)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001276{
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001277 struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001278 struct wl1271 *wl = hw->priv;
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001279 int ret;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001280
1281 wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter");
1282
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001283 mutex_lock(&wl->mutex);
1284
1285 if (wl->state == WL1271_STATE_OFF)
1286 goto out;
1287
1288 ret = wl1271_ps_elp_wakeup(wl, false);
1289 if (ret < 0)
1290 goto out;
1291
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001292 *total &= WL1271_SUPPORTED_FILTERS;
1293 changed &= WL1271_SUPPORTED_FILTERS;
1294
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001295 if (*total & FIF_ALLMULTI)
1296 ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
1297 else if (fp)
1298 ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
1299 fp->mc_list,
1300 fp->mc_list_length);
1301 if (ret < 0)
1302 goto out_sleep;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001303
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001304 kfree(fp);
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001305
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001306 /* FIXME: We still need to set our filters properly */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001307
Juuso Oikarinenb54853f2009-10-13 12:47:59 +03001308 /* determine, whether supported filter values have changed */
1309 if (changed == 0)
1310 goto out_sleep;
1311
1312 /* apply configured filters */
1313 ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
1314 if (ret < 0)
1315 goto out_sleep;
1316
1317out_sleep:
1318 wl1271_ps_elp_sleep(wl);
1319
1320out:
1321 mutex_unlock(&wl->mutex);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001322}
1323
1324static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
1325 struct ieee80211_vif *vif,
1326 struct ieee80211_sta *sta,
1327 struct ieee80211_key_conf *key_conf)
1328{
1329 struct wl1271 *wl = hw->priv;
1330 const u8 *addr;
1331 int ret;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001332 u32 tx_seq_32 = 0;
1333 u16 tx_seq_16 = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001334 u8 key_type;
1335
1336 static const u8 bcast_addr[ETH_ALEN] =
1337 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1338
1339 wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
1340
1341 addr = sta ? sta->addr : bcast_addr;
1342
1343 wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd);
1344 wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN);
1345 wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
1346 key_conf->alg, key_conf->keyidx,
1347 key_conf->keylen, key_conf->flags);
1348 wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
1349
1350 if (is_zero_ether_addr(addr)) {
1351 /* We dont support TX only encryption */
1352 ret = -EOPNOTSUPP;
1353 goto out;
1354 }
1355
1356 mutex_lock(&wl->mutex);
1357
1358 ret = wl1271_ps_elp_wakeup(wl, false);
1359 if (ret < 0)
1360 goto out_unlock;
1361
1362 switch (key_conf->alg) {
1363 case ALG_WEP:
1364 key_type = KEY_WEP;
1365
1366 key_conf->hw_key_idx = key_conf->keyidx;
1367 break;
1368 case ALG_TKIP:
1369 key_type = KEY_TKIP;
1370
1371 key_conf->hw_key_idx = key_conf->keyidx;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001372 tx_seq_32 = wl->tx_security_seq_32;
1373 tx_seq_16 = wl->tx_security_seq_16;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001374 break;
1375 case ALG_CCMP:
1376 key_type = KEY_AES;
1377
1378 key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001379 tx_seq_32 = wl->tx_security_seq_32;
1380 tx_seq_16 = wl->tx_security_seq_16;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001381 break;
1382 default:
1383 wl1271_error("Unknown key algo 0x%x", key_conf->alg);
1384
1385 ret = -EOPNOTSUPP;
1386 goto out_sleep;
1387 }
1388
1389 switch (cmd) {
1390 case SET_KEY:
1391 ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE,
1392 key_conf->keyidx, key_type,
1393 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001394 addr, tx_seq_32, tx_seq_16);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001395 if (ret < 0) {
1396 wl1271_error("Could not add or replace key");
1397 goto out_sleep;
1398 }
1399 break;
1400
1401 case DISABLE_KEY:
1402 ret = wl1271_cmd_set_key(wl, KEY_REMOVE,
1403 key_conf->keyidx, key_type,
1404 key_conf->keylen, key_conf->key,
Juuso Oikarinenac4e4ce2009-10-08 21:56:19 +03001405 addr, 0, 0);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001406 if (ret < 0) {
1407 wl1271_error("Could not remove key");
1408 goto out_sleep;
1409 }
1410 break;
1411
1412 default:
1413 wl1271_error("Unsupported key cmd 0x%x", cmd);
1414 ret = -EOPNOTSUPP;
1415 goto out_sleep;
1416
1417 break;
1418 }
1419
1420out_sleep:
1421 wl1271_ps_elp_sleep(wl);
1422
1423out_unlock:
1424 mutex_unlock(&wl->mutex);
1425
1426out:
1427 return ret;
1428}
1429
1430static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
1431 struct cfg80211_scan_request *req)
1432{
1433 struct wl1271 *wl = hw->priv;
1434 int ret;
1435 u8 *ssid = NULL;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001436 size_t len = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001437
1438 wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
1439
1440 if (req->n_ssids) {
1441 ssid = req->ssids[0].ssid;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001442 len = req->ssids[0].ssid_len;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001443 }
1444
1445 mutex_lock(&wl->mutex);
1446
1447 ret = wl1271_ps_elp_wakeup(wl, false);
1448 if (ret < 0)
1449 goto out;
1450
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +03001451 if (wl1271_11a_enabled())
1452 ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
1453 WL1271_SCAN_BAND_DUAL, 3);
1454 else
1455 ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
1456 WL1271_SCAN_BAND_2_4_GHZ, 3);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001457
1458 wl1271_ps_elp_sleep(wl);
1459
1460out:
1461 mutex_unlock(&wl->mutex);
1462
1463 return ret;
1464}
1465
1466static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1467{
1468 struct wl1271 *wl = hw->priv;
1469 int ret;
1470
1471 mutex_lock(&wl->mutex);
1472
1473 ret = wl1271_ps_elp_wakeup(wl, false);
1474 if (ret < 0)
1475 goto out;
1476
1477 ret = wl1271_acx_rts_threshold(wl, (u16) value);
1478 if (ret < 0)
1479 wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret);
1480
1481 wl1271_ps_elp_sleep(wl);
1482
1483out:
1484 mutex_unlock(&wl->mutex);
1485
1486 return ret;
1487}
1488
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001489static u32 wl1271_enabled_rates_get(struct wl1271 *wl, u64 basic_rate_set)
1490{
1491 struct ieee80211_supported_band *band;
1492 u32 enabled_rates = 0;
1493 int bit;
1494
1495 band = wl->hw->wiphy->bands[wl->band];
1496 for (bit = 0; bit < band->n_bitrates; bit++) {
1497 if (basic_rate_set & 0x1)
1498 enabled_rates |= band->bitrates[bit].hw_value;
1499 basic_rate_set >>= 1;
1500 }
1501
1502 return enabled_rates;
1503}
1504
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001505static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
1506 struct ieee80211_vif *vif,
1507 struct ieee80211_bss_conf *bss_conf,
1508 u32 changed)
1509{
1510 enum wl1271_cmd_ps_mode mode;
1511 struct wl1271 *wl = hw->priv;
1512 int ret;
1513
1514 wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed");
1515
1516 mutex_lock(&wl->mutex);
1517
1518 ret = wl1271_ps_elp_wakeup(wl, false);
1519 if (ret < 0)
1520 goto out;
1521
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001522 if ((changed & BSS_CHANGED_BSSID) &&
1523 /*
1524 * Now we know the correct bssid, so we send a new join command
1525 * and enable the BSSID filter
1526 */
1527 memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
1528 wl->rx_config |= CFG_BSSID_FILTER_EN;
1529 memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
Luciano Coelhobdcbbb92009-12-11 15:40:48 +02001530 ret = wl1271_cmd_build_null_data(wl);
1531 if (ret < 0) {
1532 wl1271_warning("cmd buld null data failed %d",
1533 ret);
1534 goto out_sleep;
1535 }
Luciano Coelhocd264762009-12-11 15:40:47 +02001536 ret = wl1271_cmd_join(wl);
1537 if (ret < 0) {
1538 wl1271_warning("cmd join failed %d", ret);
1539 goto out_sleep;
1540 }
Luciano Coelhoc7f43e42009-12-11 15:40:44 +02001541 wl->joined = true;
1542 }
1543
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001544 if (changed & BSS_CHANGED_ASSOC) {
1545 if (bss_conf->assoc) {
1546 wl->aid = bss_conf->aid;
1547
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001548 /*
1549 * with wl1271, we don't need to update the
1550 * beacon_int and dtim_period, because the firmware
1551 * updates it by itself when the first beacon is
1552 * received after a join.
1553 */
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001554 ret = wl1271_cmd_build_ps_poll(wl, wl->aid);
1555 if (ret < 0)
1556 goto out_sleep;
1557
1558 ret = wl1271_acx_aid(wl, wl->aid);
1559 if (ret < 0)
1560 goto out_sleep;
1561
1562 /* If we want to go in PSM but we're not there yet */
1563 if (wl->psm_requested && !wl->psm) {
1564 mode = STATION_POWER_SAVE_MODE;
1565 ret = wl1271_ps_set_mode(wl, mode);
1566 if (ret < 0)
1567 goto out_sleep;
1568 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001569 } else {
1570 /* use defaults when not associated */
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001571 wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
1572 wl->aid = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001573 }
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001574
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001575 }
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001576
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001577 if (changed & BSS_CHANGED_ERP_SLOT) {
1578 if (bss_conf->use_short_slot)
1579 ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
1580 else
1581 ret = wl1271_acx_slot(wl, SLOT_TIME_LONG);
1582 if (ret < 0) {
1583 wl1271_warning("Set slot time failed %d", ret);
1584 goto out_sleep;
1585 }
1586 }
1587
1588 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
1589 if (bss_conf->use_short_preamble)
1590 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT);
1591 else
1592 wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG);
1593 }
1594
1595 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
1596 if (bss_conf->use_cts_prot)
1597 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE);
1598 else
1599 ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE);
1600 if (ret < 0) {
1601 wl1271_warning("Set ctsprotect failed %d", ret);
1602 goto out_sleep;
1603 }
1604 }
1605
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001606 if (changed & BSS_CHANGED_BASIC_RATES) {
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001607 wl->basic_rate_set = wl1271_enabled_rates_get(
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001608 wl, bss_conf->basic_rates);
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001609
Luciano Coelhoae751ba2009-10-12 15:08:57 +03001610 ret = wl1271_acx_rate_policies(wl, wl->basic_rate_set);
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001611 if (ret < 0) {
1612 wl1271_warning("Set rate policies failed %d", ret);
1613 goto out_sleep;
1614 }
1615 }
1616
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001617out_sleep:
1618 wl1271_ps_elp_sleep(wl);
1619
1620out:
1621 mutex_unlock(&wl->mutex);
1622}
1623
1624
1625/* can't be const, mac80211 writes to this */
1626static struct ieee80211_rate wl1271_rates[] = {
1627 { .bitrate = 10,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001628 .hw_value = CONF_HW_BIT_RATE_1MBPS,
1629 .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001630 { .bitrate = 20,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001631 .hw_value = CONF_HW_BIT_RATE_2MBPS,
1632 .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001633 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
1634 { .bitrate = 55,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001635 .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
1636 .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001637 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
1638 { .bitrate = 110,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001639 .hw_value = CONF_HW_BIT_RATE_11MBPS,
1640 .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001641 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
1642 { .bitrate = 60,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001643 .hw_value = CONF_HW_BIT_RATE_6MBPS,
1644 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001645 { .bitrate = 90,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001646 .hw_value = CONF_HW_BIT_RATE_9MBPS,
1647 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001648 { .bitrate = 120,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001649 .hw_value = CONF_HW_BIT_RATE_12MBPS,
1650 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001651 { .bitrate = 180,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001652 .hw_value = CONF_HW_BIT_RATE_18MBPS,
1653 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001654 { .bitrate = 240,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001655 .hw_value = CONF_HW_BIT_RATE_24MBPS,
1656 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001657 { .bitrate = 360,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001658 .hw_value = CONF_HW_BIT_RATE_36MBPS,
1659 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001660 { .bitrate = 480,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001661 .hw_value = CONF_HW_BIT_RATE_48MBPS,
1662 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001663 { .bitrate = 540,
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001664 .hw_value = CONF_HW_BIT_RATE_54MBPS,
1665 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001666};
1667
1668/* can't be const, mac80211 writes to this */
1669static struct ieee80211_channel wl1271_channels[] = {
Luciano Coelhoa2d0e3f2009-12-11 15:40:46 +02001670 { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
1671 { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
1672 { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
1673 { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
1674 { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
1675 { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
1676 { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
1677 { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
1678 { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
1679 { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
1680 { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
1681 { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
1682 { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001683};
1684
1685/* can't be const, mac80211 writes to this */
1686static struct ieee80211_supported_band wl1271_band_2ghz = {
1687 .channels = wl1271_channels,
1688 .n_channels = ARRAY_SIZE(wl1271_channels),
1689 .bitrates = wl1271_rates,
1690 .n_bitrates = ARRAY_SIZE(wl1271_rates),
1691};
1692
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03001693/* 5 GHz data rates for WL1273 */
1694static struct ieee80211_rate wl1271_rates_5ghz[] = {
1695 { .bitrate = 60,
1696 .hw_value = CONF_HW_BIT_RATE_6MBPS,
1697 .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
1698 { .bitrate = 90,
1699 .hw_value = CONF_HW_BIT_RATE_9MBPS,
1700 .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
1701 { .bitrate = 120,
1702 .hw_value = CONF_HW_BIT_RATE_12MBPS,
1703 .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
1704 { .bitrate = 180,
1705 .hw_value = CONF_HW_BIT_RATE_18MBPS,
1706 .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
1707 { .bitrate = 240,
1708 .hw_value = CONF_HW_BIT_RATE_24MBPS,
1709 .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
1710 { .bitrate = 360,
1711 .hw_value = CONF_HW_BIT_RATE_36MBPS,
1712 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
1713 { .bitrate = 480,
1714 .hw_value = CONF_HW_BIT_RATE_48MBPS,
1715 .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
1716 { .bitrate = 540,
1717 .hw_value = CONF_HW_BIT_RATE_54MBPS,
1718 .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
1719};
1720
1721/* 5 GHz band channels for WL1273 */
1722static struct ieee80211_channel wl1271_channels_5ghz[] = {
1723 { .hw_value = 183, .center_freq = 4915},
1724 { .hw_value = 184, .center_freq = 4920},
1725 { .hw_value = 185, .center_freq = 4925},
1726 { .hw_value = 187, .center_freq = 4935},
1727 { .hw_value = 188, .center_freq = 4940},
1728 { .hw_value = 189, .center_freq = 4945},
1729 { .hw_value = 192, .center_freq = 4960},
1730 { .hw_value = 196, .center_freq = 4980},
1731 { .hw_value = 7, .center_freq = 5035},
1732 { .hw_value = 8, .center_freq = 5040},
1733 { .hw_value = 9, .center_freq = 5045},
1734 { .hw_value = 11, .center_freq = 5055},
1735 { .hw_value = 12, .center_freq = 5060},
1736 { .hw_value = 16, .center_freq = 5080},
1737 { .hw_value = 34, .center_freq = 5170},
1738 { .hw_value = 36, .center_freq = 5180},
1739 { .hw_value = 38, .center_freq = 5190},
1740 { .hw_value = 40, .center_freq = 5200},
1741 { .hw_value = 42, .center_freq = 5210},
1742 { .hw_value = 44, .center_freq = 5220},
1743 { .hw_value = 46, .center_freq = 5230},
1744 { .hw_value = 48, .center_freq = 5240},
1745 { .hw_value = 52, .center_freq = 5260},
1746 { .hw_value = 56, .center_freq = 5280},
1747 { .hw_value = 60, .center_freq = 5300},
1748 { .hw_value = 64, .center_freq = 5320},
1749 { .hw_value = 100, .center_freq = 5500},
1750 { .hw_value = 104, .center_freq = 5520},
1751 { .hw_value = 108, .center_freq = 5540},
1752 { .hw_value = 112, .center_freq = 5560},
1753 { .hw_value = 116, .center_freq = 5580},
1754 { .hw_value = 120, .center_freq = 5600},
1755 { .hw_value = 124, .center_freq = 5620},
1756 { .hw_value = 128, .center_freq = 5640},
1757 { .hw_value = 132, .center_freq = 5660},
1758 { .hw_value = 136, .center_freq = 5680},
1759 { .hw_value = 140, .center_freq = 5700},
1760 { .hw_value = 149, .center_freq = 5745},
1761 { .hw_value = 153, .center_freq = 5765},
1762 { .hw_value = 157, .center_freq = 5785},
1763 { .hw_value = 161, .center_freq = 5805},
1764 { .hw_value = 165, .center_freq = 5825},
1765};
1766
1767
1768static struct ieee80211_supported_band wl1271_band_5ghz = {
1769 .channels = wl1271_channels_5ghz,
1770 .n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
1771 .bitrates = wl1271_rates_5ghz,
1772 .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
1773};
1774
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001775static const struct ieee80211_ops wl1271_ops = {
1776 .start = wl1271_op_start,
1777 .stop = wl1271_op_stop,
1778 .add_interface = wl1271_op_add_interface,
1779 .remove_interface = wl1271_op_remove_interface,
1780 .config = wl1271_op_config,
1781/* .config_interface = wl1271_op_config_interface, */
Juuso Oikarinenc87dec92009-10-08 21:56:31 +03001782 .prepare_multicast = wl1271_op_prepare_multicast,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001783 .configure_filter = wl1271_op_configure_filter,
1784 .tx = wl1271_op_tx,
1785 .set_key = wl1271_op_set_key,
1786 .hw_scan = wl1271_op_hw_scan,
1787 .bss_info_changed = wl1271_op_bss_info_changed,
1788 .set_rts_threshold = wl1271_op_set_rts_threshold,
1789};
1790
1791static int wl1271_register_hw(struct wl1271 *wl)
1792{
1793 int ret;
1794
1795 if (wl->mac80211_registered)
1796 return 0;
1797
1798 SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
1799
1800 ret = ieee80211_register_hw(wl->hw);
1801 if (ret < 0) {
1802 wl1271_error("unable to register mac80211 hw: %d", ret);
1803 return ret;
1804 }
1805
1806 wl->mac80211_registered = true;
1807
1808 wl1271_notice("loaded");
1809
1810 return 0;
1811}
1812
1813static int wl1271_init_ieee80211(struct wl1271 *wl)
1814{
Juuso Oikarinen1e2b7972009-10-08 21:56:20 +03001815 /* The tx descriptor buffer and the TKIP space. */
1816 wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
1817 sizeof(struct wl1271_tx_hw_descr);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001818
1819 /* unit us */
1820 /* FIXME: find a proper value */
1821 wl->hw->channel_change_time = 10000;
1822
1823 wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
Juuso Oikarinen19221672009-10-08 21:56:35 +03001824 IEEE80211_HW_NOISE_DBM |
Juuso Oikarinen03442a32009-11-23 23:22:14 +02001825 IEEE80211_HW_BEACON_FILTER |
1826 IEEE80211_HW_SUPPORTS_PS;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001827
1828 wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
1829 wl->hw->wiphy->max_scan_ssids = 1;
1830 wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz;
1831
Teemu Paasikivi1ebec3d2009-10-13 12:47:48 +03001832 if (wl1271_11a_enabled())
1833 wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz;
1834
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001835 SET_IEEE80211_DEV(wl->hw, &wl->spi->dev);
1836
1837 return 0;
1838}
1839
1840static void wl1271_device_release(struct device *dev)
1841{
1842
1843}
1844
1845static struct platform_device wl1271_device = {
1846 .name = "wl1271",
1847 .id = -1,
1848
1849 /* device model insists to have a release function */
1850 .dev = {
1851 .release = wl1271_device_release,
1852 },
1853};
1854
1855#define WL1271_DEFAULT_CHANNEL 0
1856static int __devinit wl1271_probe(struct spi_device *spi)
1857{
1858 struct wl12xx_platform_data *pdata;
1859 struct ieee80211_hw *hw;
1860 struct wl1271 *wl;
1861 int ret, i;
1862 static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
1863
1864 pdata = spi->dev.platform_data;
1865 if (!pdata) {
1866 wl1271_error("no platform data");
1867 return -ENODEV;
1868 }
1869
1870 hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
1871 if (!hw) {
1872 wl1271_error("could not alloc ieee80211_hw");
1873 return -ENOMEM;
1874 }
1875
1876 wl = hw->priv;
1877 memset(wl, 0, sizeof(*wl));
1878
Juuso Oikarinen01c09162009-10-13 12:47:55 +03001879 INIT_LIST_HEAD(&wl->list);
1880
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001881 wl->hw = hw;
1882 dev_set_drvdata(&spi->dev, wl);
1883 wl->spi = spi;
1884
1885 skb_queue_head_init(&wl->tx_queue);
1886
Juuso Oikarinen37b70a82009-10-08 21:56:21 +03001887 INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001888 wl->channel = WL1271_DEFAULT_CHANNEL;
1889 wl->scanning = false;
1890 wl->default_key = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001891 wl->rx_counter = 0;
1892 wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
1893 wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
1894 wl->elp = false;
1895 wl->psm = 0;
1896 wl->psm_requested = false;
Juuso Oikarinen19ad0712009-11-02 20:22:11 +02001897 wl->psm_entry_retry = 0;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001898 wl->tx_queue_stopped = false;
1899 wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
Juuso Oikarinend94cd292009-10-08 21:56:25 +03001900 wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
Juuso Oikarinen8a5a37a2009-10-08 21:56:24 +03001901 wl->band = IEEE80211_BAND_2GHZ;
Juuso Oikarinenb771eee2009-10-08 21:56:34 +03001902 wl->vif = NULL;
Luciano Coelhod6e19d12009-10-12 15:08:43 +03001903 wl->joined = false;
Luciano Coelho98b2a682009-12-11 15:40:52 +02001904 wl->gpio_power = false;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001905
Juuso Oikarinenbe7078c2009-10-08 21:56:26 +03001906 for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001907 wl->tx_frames[i] = NULL;
1908
1909 spin_lock_init(&wl->wl_lock);
1910
1911 /*
1912 * In case our MAC address is not correctly set,
1913 * we use a random but Nokia MAC.
1914 */
1915 memcpy(wl->mac_addr, nokia_oui, 3);
1916 get_random_bytes(wl->mac_addr + 3, 3);
1917
1918 wl->state = WL1271_STATE_OFF;
1919 mutex_init(&wl->mutex);
1920
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001921 /* This is the only SPI value that we need to set here, the rest
1922 * comes from the board-peripherals file */
1923 spi->bits_per_word = 32;
1924
1925 ret = spi_setup(spi);
1926 if (ret < 0) {
1927 wl1271_error("spi_setup failed");
1928 goto out_free;
1929 }
1930
1931 wl->set_power = pdata->set_power;
1932 if (!wl->set_power) {
1933 wl1271_error("set power function missing in platform data");
1934 ret = -ENODEV;
1935 goto out_free;
1936 }
1937
1938 wl->irq = spi->irq;
1939 if (wl->irq < 0) {
1940 wl1271_error("irq missing in platform data");
1941 ret = -ENODEV;
1942 goto out_free;
1943 }
1944
1945 ret = request_irq(wl->irq, wl1271_irq, 0, DRIVER_NAME, wl);
1946 if (ret < 0) {
1947 wl1271_error("request_irq() failed: %d", ret);
1948 goto out_free;
1949 }
1950
1951 set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING);
1952
1953 disable_irq(wl->irq);
1954
1955 ret = platform_device_register(&wl1271_device);
1956 if (ret) {
1957 wl1271_error("couldn't register platform device");
1958 goto out_irq;
1959 }
1960 dev_set_drvdata(&wl1271_device.dev, wl);
1961
Juuso Oikarinen2b60100b2009-10-13 12:47:39 +03001962 /* Apply default driver configuration. */
1963 wl1271_conf_init(wl);
1964
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001965 ret = wl1271_init_ieee80211(wl);
1966 if (ret)
1967 goto out_platform;
1968
1969 ret = wl1271_register_hw(wl);
1970 if (ret)
1971 goto out_platform;
1972
1973 wl1271_debugfs_init(wl);
1974
1975 wl1271_notice("initialized");
1976
1977 return 0;
1978
1979 out_platform:
1980 platform_device_unregister(&wl1271_device);
1981
1982 out_irq:
1983 free_irq(wl->irq, wl);
1984
1985 out_free:
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001986 ieee80211_free_hw(hw);
1987
1988 return ret;
1989}
1990
1991static int __devexit wl1271_remove(struct spi_device *spi)
1992{
1993 struct wl1271 *wl = dev_get_drvdata(&spi->dev);
1994
1995 ieee80211_unregister_hw(wl->hw);
1996
1997 wl1271_debugfs_exit(wl);
1998 platform_device_unregister(&wl1271_device);
1999 free_irq(wl->irq, wl);
2000 kfree(wl->target_mem_map);
Juuso Oikarinen1fba4972009-10-08 21:56:32 +03002001 vfree(wl->fw);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002002 wl->fw = NULL;
2003 kfree(wl->nvs);
2004 wl->nvs = NULL;
2005
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03002006 kfree(wl->fw_status);
2007 kfree(wl->tx_res_if);
2008
2009 ieee80211_free_hw(wl->hw);
2010
2011 return 0;
2012}
2013
2014
2015static struct spi_driver wl1271_spi_driver = {
2016 .driver = {
2017 .name = "wl1271",
2018 .bus = &spi_bus_type,
2019 .owner = THIS_MODULE,
2020 },
2021
2022 .probe = wl1271_probe,
2023 .remove = __devexit_p(wl1271_remove),
2024};
2025
2026static int __init wl1271_init(void)
2027{
2028 int ret;
2029
2030 ret = spi_register_driver(&wl1271_spi_driver);
2031 if (ret < 0) {
2032 wl1271_error("failed to register spi driver: %d", ret);
2033 goto out;
2034 }
2035
2036out:
2037 return ret;
2038}
2039
2040static void __exit wl1271_exit(void)
2041{
2042 spi_unregister_driver(&wl1271_spi_driver);
2043
2044 wl1271_notice("unloaded");
2045}
2046
2047module_init(wl1271_init);
2048module_exit(wl1271_exit);
2049
2050MODULE_LICENSE("GPL");
2051MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
Luciano Coelho2f018722009-10-08 21:56:27 +03002052MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
Ben Hutchings49f146d2009-11-07 22:02:15 +00002053MODULE_FIRMWARE(WL1271_FW_NAME);