1 | /* |
2 | * drivers/net/wireless/mwl8k.c |
3 | * Driver for Marvell TOPDOG 802.11 Wireless cards |
4 | * |
5 | * Copyright (C) 2008, 2009, 2010 Marvell Semiconductor Inc. |
6 | * |
7 | * This file is licensed under the terms of the GNU General Public |
8 | * License version 2. This program is licensed "as is" without any |
9 | * warranty of any kind, whether express or implied. |
10 | */ |
11 | |
12 | #include <linux/interrupt.h> |
13 | #include <linux/module.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/sched.h> |
16 | #include <linux/spinlock.h> |
17 | #include <linux/list.h> |
18 | #include <linux/pci.h> |
19 | #include <linux/delay.h> |
20 | #include <linux/completion.h> |
21 | #include <linux/etherdevice.h> |
22 | #include <linux/slab.h> |
23 | #include <net/mac80211.h> |
24 | #include <linux/moduleparam.h> |
25 | #include <linux/firmware.h> |
26 | #include <linux/workqueue.h> |
27 | |
28 | #define MWL8K_DESC "Marvell TOPDOG(R) 802.11 Wireless Network Driver" |
29 | #define MWL8K_NAME KBUILD_MODNAME |
30 | #define MWL8K_VERSION "0.13" |
31 | |
32 | /* Module parameters */ |
33 | static bool ap_mode_default; |
34 | module_param(ap_mode_default, bool, 0); |
35 | MODULE_PARM_DESC(ap_mode_default, |
36 | "Set to 1 to make ap mode the default instead of sta mode" ); |
37 | |
38 | /* Register definitions */ |
39 | #define MWL8K_HIU_GEN_PTR 0x00000c10 |
40 | #define MWL8K_MODE_STA 0x0000005a |
41 | #define MWL8K_MODE_AP 0x000000a5 |
42 | #define MWL8K_HIU_INT_CODE 0x00000c14 |
43 | #define MWL8K_FWSTA_READY 0xf0f1f2f4 |
44 | #define MWL8K_FWAP_READY 0xf1f2f4a5 |
45 | #define MWL8K_INT_CODE_CMD_FINISHED 0x00000005 |
46 | #define MWL8K_HIU_SCRATCH 0x00000c40 |
47 | |
48 | /* Host->device communications */ |
49 | #define MWL8K_HIU_H2A_INTERRUPT_EVENTS 0x00000c18 |
50 | #define MWL8K_HIU_H2A_INTERRUPT_STATUS 0x00000c1c |
51 | #define MWL8K_HIU_H2A_INTERRUPT_MASK 0x00000c20 |
52 | #define MWL8K_HIU_H2A_INTERRUPT_CLEAR_SEL 0x00000c24 |
53 | #define MWL8K_HIU_H2A_INTERRUPT_STATUS_MASK 0x00000c28 |
54 | #define MWL8K_H2A_INT_DUMMY (1 << 20) |
55 | #define MWL8K_H2A_INT_RESET (1 << 15) |
56 | #define MWL8K_H2A_INT_DOORBELL (1 << 1) |
57 | #define MWL8K_H2A_INT_PPA_READY (1 << 0) |
58 | |
59 | /* Device->host communications */ |
60 | #define MWL8K_HIU_A2H_INTERRUPT_EVENTS 0x00000c2c |
61 | #define MWL8K_HIU_A2H_INTERRUPT_STATUS 0x00000c30 |
62 | #define MWL8K_HIU_A2H_INTERRUPT_MASK 0x00000c34 |
63 | #define MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL 0x00000c38 |
64 | #define MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK 0x00000c3c |
65 | #define MWL8K_A2H_INT_DUMMY (1 << 20) |
66 | #define MWL8K_A2H_INT_BA_WATCHDOG (1 << 14) |
67 | #define MWL8K_A2H_INT_CHNL_SWITCHED (1 << 11) |
68 | #define MWL8K_A2H_INT_QUEUE_EMPTY (1 << 10) |
69 | #define MWL8K_A2H_INT_RADAR_DETECT (1 << 7) |
70 | #define MWL8K_A2H_INT_RADIO_ON (1 << 6) |
71 | #define MWL8K_A2H_INT_RADIO_OFF (1 << 5) |
72 | #define MWL8K_A2H_INT_MAC_EVENT (1 << 3) |
73 | #define MWL8K_A2H_INT_OPC_DONE (1 << 2) |
74 | #define MWL8K_A2H_INT_RX_READY (1 << 1) |
75 | #define MWL8K_A2H_INT_TX_DONE (1 << 0) |
76 | |
77 | /* HW micro second timer register |
78 | * located at offset 0xA600. This |
79 | * will be used to timestamp tx |
80 | * packets. |
81 | */ |
82 | |
83 | #define MWL8K_HW_TIMER_REGISTER 0x0000a600 |
84 | #define BBU_RXRDY_CNT_REG 0x0000a860 |
85 | #define NOK_CCA_CNT_REG 0x0000a6a0 |
86 | #define BBU_AVG_NOISE_VAL 0x67 |
87 | |
88 | #define MWL8K_A2H_EVENTS (MWL8K_A2H_INT_DUMMY | \ |
89 | MWL8K_A2H_INT_CHNL_SWITCHED | \ |
90 | MWL8K_A2H_INT_QUEUE_EMPTY | \ |
91 | MWL8K_A2H_INT_RADAR_DETECT | \ |
92 | MWL8K_A2H_INT_RADIO_ON | \ |
93 | MWL8K_A2H_INT_RADIO_OFF | \ |
94 | MWL8K_A2H_INT_MAC_EVENT | \ |
95 | MWL8K_A2H_INT_OPC_DONE | \ |
96 | MWL8K_A2H_INT_RX_READY | \ |
97 | MWL8K_A2H_INT_TX_DONE | \ |
98 | MWL8K_A2H_INT_BA_WATCHDOG) |
99 | |
100 | #define MWL8K_RX_QUEUES 1 |
101 | #define MWL8K_TX_WMM_QUEUES 4 |
102 | #define MWL8K_MAX_AMPDU_QUEUES 8 |
103 | #define MWL8K_MAX_TX_QUEUES (MWL8K_TX_WMM_QUEUES + MWL8K_MAX_AMPDU_QUEUES) |
104 | #define mwl8k_tx_queues(priv) (MWL8K_TX_WMM_QUEUES + (priv)->num_ampdu_queues) |
105 | |
106 | /* txpriorities are mapped with hw queues. |
107 | * Each hw queue has a txpriority. |
108 | */ |
109 | #define TOTAL_HW_TX_QUEUES 8 |
110 | |
111 | /* Each HW queue can have one AMPDU stream. |
112 | * But, because one of the hw queue is reserved, |
113 | * maximum AMPDU queues that can be created are |
114 | * one short of total tx queues. |
115 | */ |
116 | #define MWL8K_NUM_AMPDU_STREAMS (TOTAL_HW_TX_QUEUES - 1) |
117 | |
118 | #define MWL8K_NUM_CHANS 18 |
119 | |
120 | struct rxd_ops { |
121 | int rxd_size; |
122 | void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr); |
123 | void (*rxd_refill)(void *rxd, dma_addr_t addr, int len); |
124 | int (*rxd_process)(void *rxd, struct ieee80211_rx_status *status, |
125 | __le16 *qos, s8 *noise); |
126 | }; |
127 | |
128 | struct mwl8k_device_info { |
129 | char *part_name; |
130 | char *helper_image; |
131 | char *fw_image_sta; |
132 | char *fw_image_ap; |
133 | struct rxd_ops *ap_rxd_ops; |
134 | u32 fw_api_ap; |
135 | }; |
136 | |
137 | struct mwl8k_rx_queue { |
138 | int rxd_count; |
139 | |
140 | /* hw receives here */ |
141 | int head; |
142 | |
143 | /* refill descs here */ |
144 | int tail; |
145 | |
146 | void *rxd; |
147 | dma_addr_t rxd_dma; |
148 | struct { |
149 | struct sk_buff *skb; |
150 | DEFINE_DMA_UNMAP_ADDR(dma); |
151 | } *buf; |
152 | }; |
153 | |
154 | struct mwl8k_tx_queue { |
155 | /* hw transmits here */ |
156 | int head; |
157 | |
158 | /* sw appends here */ |
159 | int tail; |
160 | |
161 | unsigned int len; |
162 | struct mwl8k_tx_desc *txd; |
163 | dma_addr_t txd_dma; |
164 | struct sk_buff **skb; |
165 | }; |
166 | |
167 | enum { |
168 | AMPDU_NO_STREAM, |
169 | AMPDU_STREAM_NEW, |
170 | AMPDU_STREAM_IN_PROGRESS, |
171 | AMPDU_STREAM_ACTIVE, |
172 | }; |
173 | |
174 | struct mwl8k_ampdu_stream { |
175 | struct ieee80211_sta *sta; |
176 | u8 tid; |
177 | u8 state; |
178 | u8 idx; |
179 | }; |
180 | |
181 | struct mwl8k_priv { |
182 | struct ieee80211_hw *hw; |
183 | struct pci_dev *pdev; |
184 | int irq; |
185 | |
186 | struct mwl8k_device_info *device_info; |
187 | |
188 | void __iomem *sram; |
189 | void __iomem *regs; |
190 | |
191 | /* firmware */ |
192 | const struct firmware *fw_helper; |
193 | const struct firmware *fw_ucode; |
194 | |
195 | /* hardware/firmware parameters */ |
196 | bool ap_fw; |
197 | struct rxd_ops *rxd_ops; |
198 | struct ieee80211_supported_band band_24; |
199 | struct ieee80211_channel channels_24[14]; |
200 | struct ieee80211_rate rates_24[13]; |
201 | struct ieee80211_supported_band band_50; |
202 | struct ieee80211_channel channels_50[9]; |
203 | struct ieee80211_rate rates_50[8]; |
204 | u32 ap_macids_supported; |
205 | u32 sta_macids_supported; |
206 | |
207 | /* Ampdu stream information */ |
208 | u8 num_ampdu_queues; |
209 | spinlock_t stream_lock; |
210 | struct mwl8k_ampdu_stream ampdu[MWL8K_MAX_AMPDU_QUEUES]; |
211 | struct work_struct watchdog_ba_handle; |
212 | |
213 | /* firmware access */ |
214 | struct mutex fw_mutex; |
215 | struct task_struct *fw_mutex_owner; |
216 | struct task_struct *hw_restart_owner; |
217 | int fw_mutex_depth; |
218 | struct completion *hostcmd_wait; |
219 | |
220 | atomic_t watchdog_event_pending; |
221 | |
222 | /* lock held over TX and TX reap */ |
223 | spinlock_t tx_lock; |
224 | |
225 | /* TX quiesce completion, protected by fw_mutex and tx_lock */ |
226 | struct completion *tx_wait; |
227 | |
228 | /* List of interfaces. */ |
229 | u32 macids_used; |
230 | struct list_head vif_list; |
231 | |
232 | /* power management status cookie from firmware */ |
233 | u32 *cookie; |
234 | dma_addr_t cookie_dma; |
235 | |
236 | u16 num_mcaddrs; |
237 | u8 hw_rev; |
238 | u32 fw_rev; |
239 | u32 caps; |
240 | |
241 | /* |
242 | * Running count of TX packets in flight, to avoid |
243 | * iterating over the transmit rings each time. |
244 | */ |
245 | int pending_tx_pkts; |
246 | |
247 | struct mwl8k_rx_queue rxq[MWL8K_RX_QUEUES]; |
248 | struct mwl8k_tx_queue txq[MWL8K_MAX_TX_QUEUES]; |
249 | u32 txq_offset[MWL8K_MAX_TX_QUEUES]; |
250 | |
251 | bool radio_on; |
252 | bool radio_short_preamble; |
253 | bool sniffer_enabled; |
254 | bool wmm_enabled; |
255 | |
256 | /* XXX need to convert this to handle multiple interfaces */ |
257 | bool capture_beacon; |
258 | u8 capture_bssid[ETH_ALEN]; |
259 | struct sk_buff *beacon_skb; |
260 | |
261 | /* |
262 | * This FJ worker has to be global as it is scheduled from the |
263 | * RX handler. At this point we don't know which interface it |
264 | * belongs to until the list of bssids waiting to complete join |
265 | * is checked. |
266 | */ |
267 | struct work_struct finalize_join_worker; |
268 | |
269 | /* Tasklet to perform TX reclaim. */ |
270 | struct tasklet_struct poll_tx_task; |
271 | |
272 | /* Tasklet to perform RX. */ |
273 | struct tasklet_struct poll_rx_task; |
274 | |
275 | /* Most recently reported noise in dBm */ |
276 | s8 noise; |
277 | |
278 | /* |
279 | * preserve the queue configurations so they can be restored if/when |
280 | * the firmware image is swapped. |
281 | */ |
282 | struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_WMM_QUEUES]; |
283 | |
284 | /* To perform the task of reloading the firmware */ |
285 | struct work_struct fw_reload; |
286 | bool hw_restart_in_progress; |
287 | |
288 | /* async firmware loading state */ |
289 | unsigned fw_state; |
290 | char *fw_pref; |
291 | char *fw_alt; |
292 | bool is_8764; |
293 | struct completion firmware_loading_complete; |
294 | |
295 | /* bitmap of running BSSes */ |
296 | u32 running_bsses; |
297 | |
298 | /* ACS related */ |
299 | bool sw_scan_start; |
300 | struct ieee80211_channel *acs_chan; |
301 | unsigned long channel_time; |
302 | struct survey_info survey[MWL8K_NUM_CHANS]; |
303 | }; |
304 | |
305 | #define MAX_WEP_KEY_LEN 13 |
306 | #define NUM_WEP_KEYS 4 |
307 | |
308 | /* Per interface specific private data */ |
309 | struct mwl8k_vif { |
310 | struct list_head list; |
311 | struct ieee80211_vif *vif; |
312 | |
313 | /* Firmware macid for this vif. */ |
314 | int macid; |
315 | |
316 | /* Non AMPDU sequence number assigned by driver. */ |
317 | u16 seqno; |
318 | |
319 | /* Saved WEP keys */ |
320 | struct { |
321 | u8 enabled; |
322 | u8 key[sizeof(struct ieee80211_key_conf) + MAX_WEP_KEY_LEN]; |
323 | } wep_key_conf[NUM_WEP_KEYS]; |
324 | |
325 | /* BSSID */ |
326 | u8 bssid[ETH_ALEN]; |
327 | |
328 | /* A flag to indicate is HW crypto is enabled for this bssid */ |
329 | bool is_hw_crypto_enabled; |
330 | }; |
331 | #define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv)) |
332 | #define IEEE80211_KEY_CONF(_u8) ((struct ieee80211_key_conf *)(_u8)) |
333 | |
334 | struct tx_traffic_info { |
335 | u32 start_time; |
336 | u32 pkts; |
337 | }; |
338 | |
339 | #define MWL8K_MAX_TID 8 |
340 | struct mwl8k_sta { |
341 | /* Index into station database. Returned by UPDATE_STADB. */ |
342 | u8 peer_id; |
343 | u8 is_ampdu_allowed; |
344 | struct tx_traffic_info tx_stats[MWL8K_MAX_TID]; |
345 | }; |
346 | #define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv)) |
347 | |
348 | static const struct ieee80211_channel mwl8k_channels_24[] = { |
349 | { .band = NL80211_BAND_2GHZ, .center_freq = 2412, .hw_value = 1, }, |
350 | { .band = NL80211_BAND_2GHZ, .center_freq = 2417, .hw_value = 2, }, |
351 | { .band = NL80211_BAND_2GHZ, .center_freq = 2422, .hw_value = 3, }, |
352 | { .band = NL80211_BAND_2GHZ, .center_freq = 2427, .hw_value = 4, }, |
353 | { .band = NL80211_BAND_2GHZ, .center_freq = 2432, .hw_value = 5, }, |
354 | { .band = NL80211_BAND_2GHZ, .center_freq = 2437, .hw_value = 6, }, |
355 | { .band = NL80211_BAND_2GHZ, .center_freq = 2442, .hw_value = 7, }, |
356 | { .band = NL80211_BAND_2GHZ, .center_freq = 2447, .hw_value = 8, }, |
357 | { .band = NL80211_BAND_2GHZ, .center_freq = 2452, .hw_value = 9, }, |
358 | { .band = NL80211_BAND_2GHZ, .center_freq = 2457, .hw_value = 10, }, |
359 | { .band = NL80211_BAND_2GHZ, .center_freq = 2462, .hw_value = 11, }, |
360 | { .band = NL80211_BAND_2GHZ, .center_freq = 2467, .hw_value = 12, }, |
361 | { .band = NL80211_BAND_2GHZ, .center_freq = 2472, .hw_value = 13, }, |
362 | { .band = NL80211_BAND_2GHZ, .center_freq = 2484, .hw_value = 14, }, |
363 | }; |
364 | |
365 | static const struct ieee80211_rate mwl8k_rates_24[] = { |
366 | { .bitrate = 10, .hw_value = 2, }, |
367 | { .bitrate = 20, .hw_value = 4, }, |
368 | { .bitrate = 55, .hw_value = 11, }, |
369 | { .bitrate = 110, .hw_value = 22, }, |
370 | { .bitrate = 220, .hw_value = 44, }, |
371 | { .bitrate = 60, .hw_value = 12, }, |
372 | { .bitrate = 90, .hw_value = 18, }, |
373 | { .bitrate = 120, .hw_value = 24, }, |
374 | { .bitrate = 180, .hw_value = 36, }, |
375 | { .bitrate = 240, .hw_value = 48, }, |
376 | { .bitrate = 360, .hw_value = 72, }, |
377 | { .bitrate = 480, .hw_value = 96, }, |
378 | { .bitrate = 540, .hw_value = 108, }, |
379 | }; |
380 | |
381 | static const struct ieee80211_channel mwl8k_channels_50[] = { |
382 | { .band = NL80211_BAND_5GHZ, .center_freq = 5180, .hw_value = 36, }, |
383 | { .band = NL80211_BAND_5GHZ, .center_freq = 5200, .hw_value = 40, }, |
384 | { .band = NL80211_BAND_5GHZ, .center_freq = 5220, .hw_value = 44, }, |
385 | { .band = NL80211_BAND_5GHZ, .center_freq = 5240, .hw_value = 48, }, |
386 | { .band = NL80211_BAND_5GHZ, .center_freq = 5745, .hw_value = 149, }, |
387 | { .band = NL80211_BAND_5GHZ, .center_freq = 5765, .hw_value = 153, }, |
388 | { .band = NL80211_BAND_5GHZ, .center_freq = 5785, .hw_value = 157, }, |
389 | { .band = NL80211_BAND_5GHZ, .center_freq = 5805, .hw_value = 161, }, |
390 | { .band = NL80211_BAND_5GHZ, .center_freq = 5825, .hw_value = 165, }, |
391 | }; |
392 | |
393 | static const struct ieee80211_rate mwl8k_rates_50[] = { |
394 | { .bitrate = 60, .hw_value = 12, }, |
395 | { .bitrate = 90, .hw_value = 18, }, |
396 | { .bitrate = 120, .hw_value = 24, }, |
397 | { .bitrate = 180, .hw_value = 36, }, |
398 | { .bitrate = 240, .hw_value = 48, }, |
399 | { .bitrate = 360, .hw_value = 72, }, |
400 | { .bitrate = 480, .hw_value = 96, }, |
401 | { .bitrate = 540, .hw_value = 108, }, |
402 | }; |
403 | |
404 | /* Set or get info from Firmware */ |
405 | #define MWL8K_CMD_GET 0x0000 |
406 | #define MWL8K_CMD_SET 0x0001 |
407 | #define MWL8K_CMD_SET_LIST 0x0002 |
408 | |
409 | /* Firmware command codes */ |
410 | #define MWL8K_CMD_CODE_DNLD 0x0001 |
411 | #define MWL8K_CMD_GET_HW_SPEC 0x0003 |
412 | #define MWL8K_CMD_SET_HW_SPEC 0x0004 |
413 | #define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010 |
414 | #define MWL8K_CMD_GET_STAT 0x0014 |
415 | #define MWL8K_CMD_BBP_REG_ACCESS 0x001a |
416 | #define MWL8K_CMD_RADIO_CONTROL 0x001c |
417 | #define MWL8K_CMD_RF_TX_POWER 0x001e |
418 | #define MWL8K_CMD_TX_POWER 0x001f |
419 | #define MWL8K_CMD_RF_ANTENNA 0x0020 |
420 | #define MWL8K_CMD_SET_BEACON 0x0100 /* per-vif */ |
421 | #define MWL8K_CMD_SET_PRE_SCAN 0x0107 |
422 | #define MWL8K_CMD_SET_POST_SCAN 0x0108 |
423 | #define MWL8K_CMD_SET_RF_CHANNEL 0x010a |
424 | #define MWL8K_CMD_SET_AID 0x010d |
425 | #define MWL8K_CMD_SET_RATE 0x0110 |
426 | #define MWL8K_CMD_SET_FINALIZE_JOIN 0x0111 |
427 | #define MWL8K_CMD_RTS_THRESHOLD 0x0113 |
428 | #define MWL8K_CMD_SET_SLOT 0x0114 |
429 | #define MWL8K_CMD_SET_EDCA_PARAMS 0x0115 |
430 | #define MWL8K_CMD_SET_WMM_MODE 0x0123 |
431 | #define MWL8K_CMD_MIMO_CONFIG 0x0125 |
432 | #define MWL8K_CMD_USE_FIXED_RATE 0x0126 |
433 | #define MWL8K_CMD_ENABLE_SNIFFER 0x0150 |
434 | #define MWL8K_CMD_SET_MAC_ADDR 0x0202 /* per-vif */ |
435 | #define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 |
436 | #define MWL8K_CMD_GET_WATCHDOG_BITMAP 0x0205 |
437 | #define MWL8K_CMD_DEL_MAC_ADDR 0x0206 /* per-vif */ |
438 | #define MWL8K_CMD_BSS_START 0x1100 /* per-vif */ |
439 | #define MWL8K_CMD_SET_NEW_STN 0x1111 /* per-vif */ |
440 | #define MWL8K_CMD_UPDATE_ENCRYPTION 0x1122 /* per-vif */ |
441 | #define MWL8K_CMD_UPDATE_STADB 0x1123 |
442 | #define MWL8K_CMD_BASTREAM 0x1125 |
443 | |
444 | #define MWL8K_LEGACY_5G_RATE_OFFSET \ |
445 | (ARRAY_SIZE(mwl8k_rates_24) - ARRAY_SIZE(mwl8k_rates_50)) |
446 | |
447 | static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize) |
448 | { |
449 | u16 command = le16_to_cpu(cmd); |
450 | |
451 | #define MWL8K_CMDNAME(x) case MWL8K_CMD_##x: do {\ |
452 | snprintf(buf, bufsize, "%s", #x);\ |
453 | return buf;\ |
454 | } while (0) |
455 | switch (command & ~0x8000) { |
456 | MWL8K_CMDNAME(CODE_DNLD); |
457 | MWL8K_CMDNAME(GET_HW_SPEC); |
458 | MWL8K_CMDNAME(SET_HW_SPEC); |
459 | MWL8K_CMDNAME(MAC_MULTICAST_ADR); |
460 | MWL8K_CMDNAME(GET_STAT); |
461 | MWL8K_CMDNAME(RADIO_CONTROL); |
462 | MWL8K_CMDNAME(RF_TX_POWER); |
463 | MWL8K_CMDNAME(TX_POWER); |
464 | MWL8K_CMDNAME(RF_ANTENNA); |
465 | MWL8K_CMDNAME(SET_BEACON); |
466 | MWL8K_CMDNAME(SET_PRE_SCAN); |
467 | MWL8K_CMDNAME(SET_POST_SCAN); |
468 | MWL8K_CMDNAME(SET_RF_CHANNEL); |
469 | MWL8K_CMDNAME(SET_AID); |
470 | MWL8K_CMDNAME(SET_RATE); |
471 | MWL8K_CMDNAME(SET_FINALIZE_JOIN); |
472 | MWL8K_CMDNAME(RTS_THRESHOLD); |
473 | MWL8K_CMDNAME(SET_SLOT); |
474 | MWL8K_CMDNAME(SET_EDCA_PARAMS); |
475 | MWL8K_CMDNAME(SET_WMM_MODE); |
476 | MWL8K_CMDNAME(MIMO_CONFIG); |
477 | MWL8K_CMDNAME(USE_FIXED_RATE); |
478 | MWL8K_CMDNAME(ENABLE_SNIFFER); |
479 | MWL8K_CMDNAME(SET_MAC_ADDR); |
480 | MWL8K_CMDNAME(SET_RATEADAPT_MODE); |
481 | MWL8K_CMDNAME(BSS_START); |
482 | MWL8K_CMDNAME(SET_NEW_STN); |
483 | MWL8K_CMDNAME(UPDATE_ENCRYPTION); |
484 | MWL8K_CMDNAME(UPDATE_STADB); |
485 | MWL8K_CMDNAME(BASTREAM); |
486 | MWL8K_CMDNAME(GET_WATCHDOG_BITMAP); |
487 | default: |
488 | snprintf(buf, size: bufsize, fmt: "0x%x" , cmd); |
489 | } |
490 | #undef MWL8K_CMDNAME |
491 | |
492 | return buf; |
493 | } |
494 | |
495 | /* Hardware and firmware reset */ |
496 | static void mwl8k_hw_reset(struct mwl8k_priv *priv) |
497 | { |
498 | iowrite32(MWL8K_H2A_INT_RESET, |
499 | priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); |
500 | iowrite32(MWL8K_H2A_INT_RESET, |
501 | priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); |
502 | msleep(msecs: 20); |
503 | } |
504 | |
505 | /* Release fw image */ |
506 | static void mwl8k_release_fw(const struct firmware **fw) |
507 | { |
508 | if (*fw == NULL) |
509 | return; |
510 | release_firmware(fw: *fw); |
511 | *fw = NULL; |
512 | } |
513 | |
514 | static void mwl8k_release_firmware(struct mwl8k_priv *priv) |
515 | { |
516 | mwl8k_release_fw(fw: &priv->fw_ucode); |
517 | mwl8k_release_fw(fw: &priv->fw_helper); |
518 | } |
519 | |
520 | /* states for asynchronous f/w loading */ |
521 | static void mwl8k_fw_state_machine(const struct firmware *fw, void *context); |
522 | enum { |
523 | FW_STATE_INIT = 0, |
524 | FW_STATE_LOADING_PREF, |
525 | FW_STATE_LOADING_ALT, |
526 | FW_STATE_ERROR, |
527 | }; |
528 | |
529 | /* Request fw image */ |
530 | static int mwl8k_request_fw(struct mwl8k_priv *priv, |
531 | const char *fname, const struct firmware **fw, |
532 | bool nowait) |
533 | { |
534 | /* release current image */ |
535 | if (*fw != NULL) |
536 | mwl8k_release_fw(fw); |
537 | |
538 | if (nowait) |
539 | return request_firmware_nowait(THIS_MODULE, uevent: 1, name: fname, |
540 | device: &priv->pdev->dev, GFP_KERNEL, |
541 | context: priv, cont: mwl8k_fw_state_machine); |
542 | else |
543 | return request_firmware(fw, name: fname, device: &priv->pdev->dev); |
544 | } |
545 | |
546 | static int mwl8k_request_firmware(struct mwl8k_priv *priv, char *fw_image, |
547 | bool nowait) |
548 | { |
549 | struct mwl8k_device_info *di = priv->device_info; |
550 | int rc; |
551 | |
552 | if (di->helper_image != NULL) { |
553 | if (nowait) |
554 | rc = mwl8k_request_fw(priv, fname: di->helper_image, |
555 | fw: &priv->fw_helper, nowait: true); |
556 | else |
557 | rc = mwl8k_request_fw(priv, fname: di->helper_image, |
558 | fw: &priv->fw_helper, nowait: false); |
559 | if (rc) |
560 | printk(KERN_ERR "%s: Error requesting helper fw %s\n" , |
561 | pci_name(priv->pdev), di->helper_image); |
562 | |
563 | if (rc || nowait) |
564 | return rc; |
565 | } |
566 | |
567 | if (nowait) { |
568 | /* |
569 | * if we get here, no helper image is needed. Skip the |
570 | * FW_STATE_INIT state. |
571 | */ |
572 | priv->fw_state = FW_STATE_LOADING_PREF; |
573 | rc = mwl8k_request_fw(priv, fname: fw_image, |
574 | fw: &priv->fw_ucode, |
575 | nowait: true); |
576 | } else |
577 | rc = mwl8k_request_fw(priv, fname: fw_image, |
578 | fw: &priv->fw_ucode, nowait: false); |
579 | if (rc) { |
580 | printk(KERN_ERR "%s: Error requesting firmware file %s\n" , |
581 | pci_name(priv->pdev), fw_image); |
582 | mwl8k_release_fw(fw: &priv->fw_helper); |
583 | return rc; |
584 | } |
585 | |
586 | return 0; |
587 | } |
588 | |
589 | struct mwl8k_cmd_pkt { |
590 | __le16 code; |
591 | __le16 length; |
592 | __u8 seq_num; |
593 | __u8 macid; |
594 | __le16 result; |
595 | char payload[]; |
596 | } __packed; |
597 | |
598 | /* |
599 | * Firmware loading. |
600 | */ |
601 | static int |
602 | mwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length) |
603 | { |
604 | void __iomem *regs = priv->regs; |
605 | dma_addr_t dma_addr; |
606 | int loops; |
607 | |
608 | dma_addr = dma_map_single(&priv->pdev->dev, data, length, |
609 | DMA_TO_DEVICE); |
610 | if (dma_mapping_error(dev: &priv->pdev->dev, dma_addr)) |
611 | return -ENOMEM; |
612 | |
613 | iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR); |
614 | iowrite32(0, regs + MWL8K_HIU_INT_CODE); |
615 | iowrite32(MWL8K_H2A_INT_DOORBELL, |
616 | regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); |
617 | iowrite32(MWL8K_H2A_INT_DUMMY, |
618 | regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); |
619 | |
620 | loops = 1000; |
621 | do { |
622 | u32 int_code; |
623 | if (priv->is_8764) { |
624 | int_code = ioread32(regs + |
625 | MWL8K_HIU_H2A_INTERRUPT_STATUS); |
626 | if (int_code == 0) |
627 | break; |
628 | } else { |
629 | int_code = ioread32(regs + MWL8K_HIU_INT_CODE); |
630 | if (int_code == MWL8K_INT_CODE_CMD_FINISHED) { |
631 | iowrite32(0, regs + MWL8K_HIU_INT_CODE); |
632 | break; |
633 | } |
634 | } |
635 | cond_resched(); |
636 | udelay(1); |
637 | } while (--loops); |
638 | |
639 | dma_unmap_single(&priv->pdev->dev, dma_addr, length, DMA_TO_DEVICE); |
640 | |
641 | return loops ? 0 : -ETIMEDOUT; |
642 | } |
643 | |
644 | static int mwl8k_load_fw_image(struct mwl8k_priv *priv, |
645 | const u8 *data, size_t length) |
646 | { |
647 | struct mwl8k_cmd_pkt *cmd; |
648 | int done; |
649 | int rc = 0; |
650 | |
651 | cmd = kmalloc(size: sizeof(*cmd) + 256, GFP_KERNEL); |
652 | if (cmd == NULL) |
653 | return -ENOMEM; |
654 | |
655 | cmd->code = cpu_to_le16(MWL8K_CMD_CODE_DNLD); |
656 | cmd->seq_num = 0; |
657 | cmd->macid = 0; |
658 | cmd->result = 0; |
659 | |
660 | done = 0; |
661 | while (length) { |
662 | int block_size = length > 256 ? 256 : length; |
663 | |
664 | memcpy(cmd->payload, data + done, block_size); |
665 | cmd->length = cpu_to_le16(block_size); |
666 | |
667 | rc = mwl8k_send_fw_load_cmd(priv, data: cmd, |
668 | length: sizeof(*cmd) + block_size); |
669 | if (rc) |
670 | break; |
671 | |
672 | done += block_size; |
673 | length -= block_size; |
674 | } |
675 | |
676 | if (!rc) { |
677 | cmd->length = 0; |
678 | rc = mwl8k_send_fw_load_cmd(priv, data: cmd, length: sizeof(*cmd)); |
679 | } |
680 | |
681 | kfree(objp: cmd); |
682 | |
683 | return rc; |
684 | } |
685 | |
686 | static int mwl8k_feed_fw_image(struct mwl8k_priv *priv, |
687 | const u8 *data, size_t length) |
688 | { |
689 | unsigned char *buffer; |
690 | int may_continue, rc = 0; |
691 | u32 done, prev_block_size; |
692 | |
693 | buffer = kmalloc(size: 1024, GFP_KERNEL); |
694 | if (buffer == NULL) |
695 | return -ENOMEM; |
696 | |
697 | done = 0; |
698 | prev_block_size = 0; |
699 | may_continue = 1000; |
700 | while (may_continue > 0) { |
701 | u32 block_size; |
702 | |
703 | block_size = ioread32(priv->regs + MWL8K_HIU_SCRATCH); |
704 | if (block_size & 1) { |
705 | block_size &= ~1; |
706 | may_continue--; |
707 | } else { |
708 | done += prev_block_size; |
709 | length -= prev_block_size; |
710 | } |
711 | |
712 | if (block_size > 1024 || block_size > length) { |
713 | rc = -EOVERFLOW; |
714 | break; |
715 | } |
716 | |
717 | if (length == 0) { |
718 | rc = 0; |
719 | break; |
720 | } |
721 | |
722 | if (block_size == 0) { |
723 | rc = -EPROTO; |
724 | may_continue--; |
725 | udelay(1); |
726 | continue; |
727 | } |
728 | |
729 | prev_block_size = block_size; |
730 | memcpy(buffer, data + done, block_size); |
731 | |
732 | rc = mwl8k_send_fw_load_cmd(priv, data: buffer, length: block_size); |
733 | if (rc) |
734 | break; |
735 | } |
736 | |
737 | if (!rc && length != 0) |
738 | rc = -EREMOTEIO; |
739 | |
740 | kfree(objp: buffer); |
741 | |
742 | return rc; |
743 | } |
744 | |
745 | static int mwl8k_load_firmware(struct ieee80211_hw *hw) |
746 | { |
747 | struct mwl8k_priv *priv = hw->priv; |
748 | const struct firmware *fw = priv->fw_ucode; |
749 | int rc; |
750 | int loops; |
751 | |
752 | if (!memcmp(p: fw->data, q: "\x01\x00\x00\x00" , size: 4) && !priv->is_8764) { |
753 | const struct firmware *helper = priv->fw_helper; |
754 | |
755 | if (helper == NULL) { |
756 | printk(KERN_ERR "%s: helper image needed but none " |
757 | "given\n" , pci_name(priv->pdev)); |
758 | return -EINVAL; |
759 | } |
760 | |
761 | rc = mwl8k_load_fw_image(priv, data: helper->data, length: helper->size); |
762 | if (rc) { |
763 | printk(KERN_ERR "%s: unable to load firmware " |
764 | "helper image\n" , pci_name(priv->pdev)); |
765 | return rc; |
766 | } |
767 | msleep(msecs: 20); |
768 | |
769 | rc = mwl8k_feed_fw_image(priv, data: fw->data, length: fw->size); |
770 | } else { |
771 | if (priv->is_8764) |
772 | rc = mwl8k_feed_fw_image(priv, data: fw->data, length: fw->size); |
773 | else |
774 | rc = mwl8k_load_fw_image(priv, data: fw->data, length: fw->size); |
775 | } |
776 | |
777 | if (rc) { |
778 | printk(KERN_ERR "%s: unable to load firmware image\n" , |
779 | pci_name(priv->pdev)); |
780 | return rc; |
781 | } |
782 | |
783 | iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR); |
784 | |
785 | loops = 500000; |
786 | do { |
787 | u32 ready_code; |
788 | |
789 | ready_code = ioread32(priv->regs + MWL8K_HIU_INT_CODE); |
790 | if (ready_code == MWL8K_FWAP_READY) { |
791 | priv->ap_fw = true; |
792 | break; |
793 | } else if (ready_code == MWL8K_FWSTA_READY) { |
794 | priv->ap_fw = false; |
795 | break; |
796 | } |
797 | |
798 | cond_resched(); |
799 | udelay(1); |
800 | } while (--loops); |
801 | |
802 | return loops ? 0 : -ETIMEDOUT; |
803 | } |
804 | |
805 | |
806 | /* DMA header used by firmware and hardware. */ |
807 | struct mwl8k_dma_data { |
808 | __le16 fwlen; |
809 | struct ieee80211_hdr wh; |
810 | char data[]; |
811 | } __packed __aligned(2); |
812 | |
813 | /* Routines to add/remove DMA header from skb. */ |
814 | static inline void (struct sk_buff *skb, __le16 qos) |
815 | { |
816 | struct mwl8k_dma_data *tr; |
817 | int hdrlen; |
818 | |
819 | tr = (struct mwl8k_dma_data *)skb->data; |
820 | hdrlen = ieee80211_hdrlen(fc: tr->wh.frame_control); |
821 | |
822 | if (hdrlen != sizeof(tr->wh)) { |
823 | if (ieee80211_is_data_qos(fc: tr->wh.frame_control)) { |
824 | memmove(tr->data - hdrlen, &tr->wh, hdrlen - 2); |
825 | *((__le16 *)(tr->data - 2)) = qos; |
826 | } else { |
827 | memmove(tr->data - hdrlen, &tr->wh, hdrlen); |
828 | } |
829 | } |
830 | |
831 | if (hdrlen != sizeof(*tr)) |
832 | skb_pull(skb, len: sizeof(*tr) - hdrlen); |
833 | } |
834 | |
835 | #define REDUCED_TX_HEADROOM 8 |
836 | |
837 | static void |
838 | (struct mwl8k_priv *priv, struct sk_buff *skb, |
839 | int head_pad, int tail_pad) |
840 | { |
841 | struct ieee80211_hdr *wh; |
842 | int hdrlen; |
843 | int reqd_hdrlen; |
844 | struct mwl8k_dma_data *tr; |
845 | |
846 | /* |
847 | * Add a firmware DMA header; the firmware requires that we |
848 | * present a 2-byte payload length followed by a 4-address |
849 | * header (without QoS field), followed (optionally) by any |
850 | * WEP/ExtIV header (but only filled in for CCMP). |
851 | */ |
852 | wh = (struct ieee80211_hdr *)skb->data; |
853 | |
854 | hdrlen = ieee80211_hdrlen(fc: wh->frame_control); |
855 | |
856 | /* |
857 | * Check if skb_resize is required because of |
858 | * tx_headroom adjustment. |
859 | */ |
860 | if (priv->ap_fw && (hdrlen < (sizeof(struct ieee80211_cts) |
861 | + REDUCED_TX_HEADROOM))) { |
862 | if (pskb_expand_head(skb, REDUCED_TX_HEADROOM, ntail: 0, GFP_ATOMIC)) { |
863 | |
864 | wiphy_err(priv->hw->wiphy, |
865 | "Failed to reallocate TX buffer\n" ); |
866 | return; |
867 | } |
868 | skb->truesize += REDUCED_TX_HEADROOM; |
869 | } |
870 | |
871 | reqd_hdrlen = sizeof(*tr) + head_pad; |
872 | |
873 | if (hdrlen != reqd_hdrlen) |
874 | skb_push(skb, len: reqd_hdrlen - hdrlen); |
875 | |
876 | if (ieee80211_is_data_qos(fc: wh->frame_control)) |
877 | hdrlen -= IEEE80211_QOS_CTL_LEN; |
878 | |
879 | tr = (struct mwl8k_dma_data *)skb->data; |
880 | if (wh != &tr->wh) |
881 | memmove(&tr->wh, wh, hdrlen); |
882 | if (hdrlen != sizeof(tr->wh)) |
883 | memset(((void *)&tr->wh) + hdrlen, 0, sizeof(tr->wh) - hdrlen); |
884 | |
885 | /* |
886 | * Firmware length is the length of the fully formed "802.11 |
887 | * payload". That is, everything except for the 802.11 header. |
888 | * This includes all crypto material including the MIC. |
889 | */ |
890 | tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr) + tail_pad); |
891 | } |
892 | |
893 | static void mwl8k_encapsulate_tx_frame(struct mwl8k_priv *priv, |
894 | struct sk_buff *skb) |
895 | { |
896 | struct ieee80211_hdr *wh; |
897 | struct ieee80211_tx_info *tx_info; |
898 | struct ieee80211_key_conf *key_conf; |
899 | int data_pad; |
900 | int head_pad = 0; |
901 | |
902 | wh = (struct ieee80211_hdr *)skb->data; |
903 | |
904 | tx_info = IEEE80211_SKB_CB(skb); |
905 | |
906 | key_conf = NULL; |
907 | if (ieee80211_is_data(fc: wh->frame_control)) |
908 | key_conf = tx_info->control.hw_key; |
909 | |
910 | /* |
911 | * Make sure the packet header is in the DMA header format (4-address |
912 | * without QoS), and add head & tail padding when HW crypto is enabled. |
913 | * |
914 | * We have the following trailer padding requirements: |
915 | * - WEP: 4 trailer bytes (ICV) |
916 | * - TKIP: 12 trailer bytes (8 MIC + 4 ICV) |
917 | * - CCMP: 8 trailer bytes (MIC) |
918 | */ |
919 | data_pad = 0; |
920 | if (key_conf != NULL) { |
921 | head_pad = key_conf->iv_len; |
922 | switch (key_conf->cipher) { |
923 | case WLAN_CIPHER_SUITE_WEP40: |
924 | case WLAN_CIPHER_SUITE_WEP104: |
925 | data_pad = 4; |
926 | break; |
927 | case WLAN_CIPHER_SUITE_TKIP: |
928 | data_pad = 12; |
929 | break; |
930 | case WLAN_CIPHER_SUITE_CCMP: |
931 | data_pad = 8; |
932 | break; |
933 | } |
934 | } |
935 | mwl8k_add_dma_header(priv, skb, head_pad, tail_pad: data_pad); |
936 | } |
937 | |
938 | /* |
939 | * Packet reception for 88w8366/88w8764 AP firmware. |
940 | */ |
941 | struct mwl8k_rxd_ap { |
942 | __le16 pkt_len; |
943 | __u8 sq2; |
944 | __u8 rate; |
945 | __le32 pkt_phys_addr; |
946 | __le32 next_rxd_phys_addr; |
947 | __le16 qos_control; |
948 | __le16 htsig2; |
949 | __le32 ; |
950 | __le32 hw_noise_floor_info; |
951 | __u8 noise_floor; |
952 | __u8 pad0[3]; |
953 | __u8 ; |
954 | __u8 rx_status; |
955 | __u8 channel; |
956 | __u8 rx_ctrl; |
957 | } __packed; |
958 | |
959 | #define MWL8K_AP_RATE_INFO_MCS_FORMAT 0x80 |
960 | #define MWL8K_AP_RATE_INFO_40MHZ 0x40 |
961 | #define MWL8K_AP_RATE_INFO_RATEID(x) ((x) & 0x3f) |
962 | |
963 | #define MWL8K_AP_RX_CTRL_OWNED_BY_HOST 0x80 |
964 | |
965 | /* 8366/8764 AP rx_status bits */ |
966 | #define MWL8K_AP_RXSTAT_DECRYPT_ERR_MASK 0x80 |
967 | #define MWL8K_AP_RXSTAT_GENERAL_DECRYPT_ERR 0xFF |
968 | #define MWL8K_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR 0x02 |
969 | #define MWL8K_AP_RXSTAT_WEP_DECRYPT_ICV_ERR 0x04 |
970 | #define MWL8K_AP_RXSTAT_TKIP_DECRYPT_ICV_ERR 0x08 |
971 | |
972 | static void mwl8k_rxd_ap_init(void *_rxd, dma_addr_t next_dma_addr) |
973 | { |
974 | struct mwl8k_rxd_ap *rxd = _rxd; |
975 | |
976 | rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr); |
977 | rxd->rx_ctrl = MWL8K_AP_RX_CTRL_OWNED_BY_HOST; |
978 | } |
979 | |
980 | static void mwl8k_rxd_ap_refill(void *_rxd, dma_addr_t addr, int len) |
981 | { |
982 | struct mwl8k_rxd_ap *rxd = _rxd; |
983 | |
984 | rxd->pkt_len = cpu_to_le16(len); |
985 | rxd->pkt_phys_addr = cpu_to_le32(addr); |
986 | wmb(); |
987 | rxd->rx_ctrl = 0; |
988 | } |
989 | |
990 | static int |
991 | mwl8k_rxd_ap_process(void *_rxd, struct ieee80211_rx_status *status, |
992 | __le16 *qos, s8 *noise) |
993 | { |
994 | struct mwl8k_rxd_ap *rxd = _rxd; |
995 | |
996 | if (!(rxd->rx_ctrl & MWL8K_AP_RX_CTRL_OWNED_BY_HOST)) |
997 | return -1; |
998 | rmb(); |
999 | |
1000 | memset(status, 0, sizeof(*status)); |
1001 | |
1002 | status->signal = -rxd->rssi; |
1003 | *noise = -rxd->noise_floor; |
1004 | |
1005 | if (rxd->rate & MWL8K_AP_RATE_INFO_MCS_FORMAT) { |
1006 | status->encoding = RX_ENC_HT; |
1007 | if (rxd->rate & MWL8K_AP_RATE_INFO_40MHZ) |
1008 | status->bw = RATE_INFO_BW_40; |
1009 | status->rate_idx = MWL8K_AP_RATE_INFO_RATEID(rxd->rate); |
1010 | } else { |
1011 | int i; |
1012 | |
1013 | for (i = 0; i < ARRAY_SIZE(mwl8k_rates_24); i++) { |
1014 | if (mwl8k_rates_24[i].hw_value == rxd->rate) { |
1015 | status->rate_idx = i; |
1016 | break; |
1017 | } |
1018 | } |
1019 | } |
1020 | |
1021 | if (rxd->channel > 14) { |
1022 | status->band = NL80211_BAND_5GHZ; |
1023 | if (!(status->encoding == RX_ENC_HT) && |
1024 | status->rate_idx >= MWL8K_LEGACY_5G_RATE_OFFSET) |
1025 | status->rate_idx -= MWL8K_LEGACY_5G_RATE_OFFSET; |
1026 | } else { |
1027 | status->band = NL80211_BAND_2GHZ; |
1028 | } |
1029 | status->freq = ieee80211_channel_to_frequency(chan: rxd->channel, |
1030 | band: status->band); |
1031 | |
1032 | *qos = rxd->qos_control; |
1033 | |
1034 | if ((rxd->rx_status != MWL8K_AP_RXSTAT_GENERAL_DECRYPT_ERR) && |
1035 | (rxd->rx_status & MWL8K_AP_RXSTAT_DECRYPT_ERR_MASK) && |
1036 | (rxd->rx_status & MWL8K_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR)) |
1037 | status->flag |= RX_FLAG_MMIC_ERROR; |
1038 | |
1039 | return le16_to_cpu(rxd->pkt_len); |
1040 | } |
1041 | |
1042 | static struct rxd_ops rxd_ap_ops = { |
1043 | .rxd_size = sizeof(struct mwl8k_rxd_ap), |
1044 | .rxd_init = mwl8k_rxd_ap_init, |
1045 | .rxd_refill = mwl8k_rxd_ap_refill, |
1046 | .rxd_process = mwl8k_rxd_ap_process, |
1047 | }; |
1048 | |
1049 | /* |
1050 | * Packet reception for STA firmware. |
1051 | */ |
1052 | struct mwl8k_rxd_sta { |
1053 | __le16 pkt_len; |
1054 | __u8 link_quality; |
1055 | __u8 noise_level; |
1056 | __le32 pkt_phys_addr; |
1057 | __le32 next_rxd_phys_addr; |
1058 | __le16 qos_control; |
1059 | __le16 rate_info; |
1060 | __le32 pad0[4]; |
1061 | __u8 ; |
1062 | __u8 channel; |
1063 | __le16 pad1; |
1064 | __u8 rx_ctrl; |
1065 | __u8 rx_status; |
1066 | __u8 pad2[2]; |
1067 | } __packed; |
1068 | |
1069 | #define MWL8K_STA_RATE_INFO_SHORTPRE 0x8000 |
1070 | #define MWL8K_STA_RATE_INFO_ANTSELECT(x) (((x) >> 11) & 0x3) |
1071 | #define MWL8K_STA_RATE_INFO_RATEID(x) (((x) >> 3) & 0x3f) |
1072 | #define MWL8K_STA_RATE_INFO_40MHZ 0x0004 |
1073 | #define MWL8K_STA_RATE_INFO_SHORTGI 0x0002 |
1074 | #define MWL8K_STA_RATE_INFO_MCS_FORMAT 0x0001 |
1075 | |
1076 | #define MWL8K_STA_RX_CTRL_OWNED_BY_HOST 0x02 |
1077 | #define MWL8K_STA_RX_CTRL_DECRYPT_ERROR 0x04 |
1078 | /* ICV=0 or MIC=1 */ |
1079 | #define MWL8K_STA_RX_CTRL_DEC_ERR_TYPE 0x08 |
1080 | /* Key is uploaded only in failure case */ |
1081 | #define MWL8K_STA_RX_CTRL_KEY_INDEX 0x30 |
1082 | |
1083 | static void mwl8k_rxd_sta_init(void *_rxd, dma_addr_t next_dma_addr) |
1084 | { |
1085 | struct mwl8k_rxd_sta *rxd = _rxd; |
1086 | |
1087 | rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr); |
1088 | rxd->rx_ctrl = MWL8K_STA_RX_CTRL_OWNED_BY_HOST; |
1089 | } |
1090 | |
1091 | static void mwl8k_rxd_sta_refill(void *_rxd, dma_addr_t addr, int len) |
1092 | { |
1093 | struct mwl8k_rxd_sta *rxd = _rxd; |
1094 | |
1095 | rxd->pkt_len = cpu_to_le16(len); |
1096 | rxd->pkt_phys_addr = cpu_to_le32(addr); |
1097 | wmb(); |
1098 | rxd->rx_ctrl = 0; |
1099 | } |
1100 | |
1101 | static int |
1102 | mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status, |
1103 | __le16 *qos, s8 *noise) |
1104 | { |
1105 | struct mwl8k_rxd_sta *rxd = _rxd; |
1106 | u16 rate_info; |
1107 | |
1108 | if (!(rxd->rx_ctrl & MWL8K_STA_RX_CTRL_OWNED_BY_HOST)) |
1109 | return -1; |
1110 | rmb(); |
1111 | |
1112 | rate_info = le16_to_cpu(rxd->rate_info); |
1113 | |
1114 | memset(status, 0, sizeof(*status)); |
1115 | |
1116 | status->signal = -rxd->rssi; |
1117 | *noise = -rxd->noise_level; |
1118 | status->antenna = MWL8K_STA_RATE_INFO_ANTSELECT(rate_info); |
1119 | status->rate_idx = MWL8K_STA_RATE_INFO_RATEID(rate_info); |
1120 | |
1121 | if (rate_info & MWL8K_STA_RATE_INFO_SHORTPRE) |
1122 | status->enc_flags |= RX_ENC_FLAG_SHORTPRE; |
1123 | if (rate_info & MWL8K_STA_RATE_INFO_40MHZ) |
1124 | status->bw = RATE_INFO_BW_40; |
1125 | if (rate_info & MWL8K_STA_RATE_INFO_SHORTGI) |
1126 | status->enc_flags |= RX_ENC_FLAG_SHORT_GI; |
1127 | if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT) |
1128 | status->encoding = RX_ENC_HT; |
1129 | |
1130 | if (rxd->channel > 14) { |
1131 | status->band = NL80211_BAND_5GHZ; |
1132 | if (!(status->encoding == RX_ENC_HT) && |
1133 | status->rate_idx >= MWL8K_LEGACY_5G_RATE_OFFSET) |
1134 | status->rate_idx -= MWL8K_LEGACY_5G_RATE_OFFSET; |
1135 | } else { |
1136 | status->band = NL80211_BAND_2GHZ; |
1137 | } |
1138 | status->freq = ieee80211_channel_to_frequency(chan: rxd->channel, |
1139 | band: status->band); |
1140 | |
1141 | *qos = rxd->qos_control; |
1142 | if ((rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DECRYPT_ERROR) && |
1143 | (rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DEC_ERR_TYPE)) |
1144 | status->flag |= RX_FLAG_MMIC_ERROR; |
1145 | |
1146 | return le16_to_cpu(rxd->pkt_len); |
1147 | } |
1148 | |
1149 | static struct rxd_ops rxd_sta_ops = { |
1150 | .rxd_size = sizeof(struct mwl8k_rxd_sta), |
1151 | .rxd_init = mwl8k_rxd_sta_init, |
1152 | .rxd_refill = mwl8k_rxd_sta_refill, |
1153 | .rxd_process = mwl8k_rxd_sta_process, |
1154 | }; |
1155 | |
1156 | |
1157 | #define MWL8K_RX_DESCS 256 |
1158 | #define MWL8K_RX_MAXSZ 3800 |
1159 | |
1160 | static int mwl8k_rxq_init(struct ieee80211_hw *hw, int index) |
1161 | { |
1162 | struct mwl8k_priv *priv = hw->priv; |
1163 | struct mwl8k_rx_queue *rxq = priv->rxq + index; |
1164 | int size; |
1165 | int i; |
1166 | |
1167 | rxq->rxd_count = 0; |
1168 | rxq->head = 0; |
1169 | rxq->tail = 0; |
1170 | |
1171 | size = MWL8K_RX_DESCS * priv->rxd_ops->rxd_size; |
1172 | |
1173 | rxq->rxd = dma_alloc_coherent(dev: &priv->pdev->dev, size, dma_handle: &rxq->rxd_dma, |
1174 | GFP_KERNEL); |
1175 | if (rxq->rxd == NULL) { |
1176 | wiphy_err(hw->wiphy, "failed to alloc RX descriptors\n" ); |
1177 | return -ENOMEM; |
1178 | } |
1179 | |
1180 | rxq->buf = kcalloc(MWL8K_RX_DESCS, size: sizeof(*rxq->buf), GFP_KERNEL); |
1181 | if (rxq->buf == NULL) { |
1182 | dma_free_coherent(dev: &priv->pdev->dev, size, cpu_addr: rxq->rxd, |
1183 | dma_handle: rxq->rxd_dma); |
1184 | return -ENOMEM; |
1185 | } |
1186 | |
1187 | for (i = 0; i < MWL8K_RX_DESCS; i++) { |
1188 | int desc_size; |
1189 | void *rxd; |
1190 | int nexti; |
1191 | dma_addr_t next_dma_addr; |
1192 | |
1193 | desc_size = priv->rxd_ops->rxd_size; |
1194 | rxd = rxq->rxd + (i * priv->rxd_ops->rxd_size); |
1195 | |
1196 | nexti = i + 1; |
1197 | if (nexti == MWL8K_RX_DESCS) |
1198 | nexti = 0; |
1199 | next_dma_addr = rxq->rxd_dma + (nexti * desc_size); |
1200 | |
1201 | priv->rxd_ops->rxd_init(rxd, next_dma_addr); |
1202 | } |
1203 | |
1204 | return 0; |
1205 | } |
1206 | |
1207 | static int rxq_refill(struct ieee80211_hw *hw, int index, int limit) |
1208 | { |
1209 | struct mwl8k_priv *priv = hw->priv; |
1210 | struct mwl8k_rx_queue *rxq = priv->rxq + index; |
1211 | int refilled = 0; |
1212 | |
1213 | while (rxq->rxd_count < MWL8K_RX_DESCS && limit--) { |
1214 | struct sk_buff *skb; |
1215 | dma_addr_t addr; |
1216 | int rx; |
1217 | void *rxd; |
1218 | |
1219 | skb = dev_alloc_skb(MWL8K_RX_MAXSZ); |
1220 | if (skb == NULL) |
1221 | break; |
1222 | |
1223 | addr = dma_map_single(&priv->pdev->dev, skb->data, |
1224 | MWL8K_RX_MAXSZ, DMA_FROM_DEVICE); |
1225 | |
1226 | rxq->rxd_count++; |
1227 | rx = rxq->tail++; |
1228 | if (rxq->tail == MWL8K_RX_DESCS) |
1229 | rxq->tail = 0; |
1230 | rxq->buf[rx].skb = skb; |
1231 | dma_unmap_addr_set(&rxq->buf[rx], dma, addr); |
1232 | |
1233 | rxd = rxq->rxd + (rx * priv->rxd_ops->rxd_size); |
1234 | priv->rxd_ops->rxd_refill(rxd, addr, MWL8K_RX_MAXSZ); |
1235 | |
1236 | refilled++; |
1237 | } |
1238 | |
1239 | return refilled; |
1240 | } |
1241 | |
1242 | /* Must be called only when the card's reception is completely halted */ |
1243 | static void mwl8k_rxq_deinit(struct ieee80211_hw *hw, int index) |
1244 | { |
1245 | struct mwl8k_priv *priv = hw->priv; |
1246 | struct mwl8k_rx_queue *rxq = priv->rxq + index; |
1247 | int i; |
1248 | |
1249 | if (rxq->rxd == NULL) |
1250 | return; |
1251 | |
1252 | for (i = 0; i < MWL8K_RX_DESCS; i++) { |
1253 | if (rxq->buf[i].skb != NULL) { |
1254 | dma_unmap_single(&priv->pdev->dev, |
1255 | dma_unmap_addr(&rxq->buf[i], dma), |
1256 | MWL8K_RX_MAXSZ, DMA_FROM_DEVICE); |
1257 | dma_unmap_addr_set(&rxq->buf[i], dma, 0); |
1258 | |
1259 | kfree_skb(skb: rxq->buf[i].skb); |
1260 | rxq->buf[i].skb = NULL; |
1261 | } |
1262 | } |
1263 | |
1264 | kfree(objp: rxq->buf); |
1265 | rxq->buf = NULL; |
1266 | |
1267 | dma_free_coherent(dev: &priv->pdev->dev, |
1268 | MWL8K_RX_DESCS * priv->rxd_ops->rxd_size, cpu_addr: rxq->rxd, |
1269 | dma_handle: rxq->rxd_dma); |
1270 | rxq->rxd = NULL; |
1271 | } |
1272 | |
1273 | |
1274 | /* |
1275 | * Scan a list of BSSIDs to process for finalize join. |
1276 | * Allows for extension to process multiple BSSIDs. |
1277 | */ |
1278 | static inline int |
1279 | mwl8k_capture_bssid(struct mwl8k_priv *priv, struct ieee80211_hdr *wh) |
1280 | { |
1281 | return priv->capture_beacon && |
1282 | ieee80211_is_beacon(fc: wh->frame_control) && |
1283 | ether_addr_equal_64bits(addr1: wh->addr3, addr2: priv->capture_bssid); |
1284 | } |
1285 | |
1286 | static inline void mwl8k_save_beacon(struct ieee80211_hw *hw, |
1287 | struct sk_buff *skb) |
1288 | { |
1289 | struct mwl8k_priv *priv = hw->priv; |
1290 | |
1291 | priv->capture_beacon = false; |
1292 | eth_zero_addr(addr: priv->capture_bssid); |
1293 | |
1294 | /* |
1295 | * Use GFP_ATOMIC as rxq_process is called from |
1296 | * the primary interrupt handler, memory allocation call |
1297 | * must not sleep. |
1298 | */ |
1299 | priv->beacon_skb = skb_copy(skb, GFP_ATOMIC); |
1300 | if (priv->beacon_skb != NULL) |
1301 | ieee80211_queue_work(hw, work: &priv->finalize_join_worker); |
1302 | } |
1303 | |
1304 | static inline struct mwl8k_vif *mwl8k_find_vif_bss(struct list_head *vif_list, |
1305 | u8 *bssid) |
1306 | { |
1307 | struct mwl8k_vif *mwl8k_vif; |
1308 | |
1309 | list_for_each_entry(mwl8k_vif, |
1310 | vif_list, list) { |
1311 | if (memcmp(p: bssid, q: mwl8k_vif->bssid, |
1312 | ETH_ALEN) == 0) |
1313 | return mwl8k_vif; |
1314 | } |
1315 | |
1316 | return NULL; |
1317 | } |
1318 | |
1319 | static int rxq_process(struct ieee80211_hw *hw, int index, int limit) |
1320 | { |
1321 | struct mwl8k_priv *priv = hw->priv; |
1322 | struct mwl8k_vif *mwl8k_vif = NULL; |
1323 | struct mwl8k_rx_queue *rxq = priv->rxq + index; |
1324 | int processed; |
1325 | |
1326 | processed = 0; |
1327 | while (rxq->rxd_count && limit--) { |
1328 | struct sk_buff *skb; |
1329 | void *rxd; |
1330 | int pkt_len; |
1331 | struct ieee80211_rx_status status; |
1332 | struct ieee80211_hdr *wh; |
1333 | __le16 qos; |
1334 | |
1335 | skb = rxq->buf[rxq->head].skb; |
1336 | if (skb == NULL) |
1337 | break; |
1338 | |
1339 | rxd = rxq->rxd + (rxq->head * priv->rxd_ops->rxd_size); |
1340 | |
1341 | pkt_len = priv->rxd_ops->rxd_process(rxd, &status, &qos, |
1342 | &priv->noise); |
1343 | if (pkt_len < 0) |
1344 | break; |
1345 | |
1346 | rxq->buf[rxq->head].skb = NULL; |
1347 | |
1348 | dma_unmap_single(&priv->pdev->dev, |
1349 | dma_unmap_addr(&rxq->buf[rxq->head], dma), |
1350 | MWL8K_RX_MAXSZ, DMA_FROM_DEVICE); |
1351 | dma_unmap_addr_set(&rxq->buf[rxq->head], dma, 0); |
1352 | |
1353 | rxq->head++; |
1354 | if (rxq->head == MWL8K_RX_DESCS) |
1355 | rxq->head = 0; |
1356 | |
1357 | rxq->rxd_count--; |
1358 | |
1359 | wh = &((struct mwl8k_dma_data *)skb->data)->wh; |
1360 | |
1361 | /* |
1362 | * Check for a pending join operation. Save a |
1363 | * copy of the beacon and schedule a tasklet to |
1364 | * send a FINALIZE_JOIN command to the firmware. |
1365 | */ |
1366 | if (mwl8k_capture_bssid(priv, wh: (void *)skb->data)) |
1367 | mwl8k_save_beacon(hw, skb); |
1368 | |
1369 | if (ieee80211_has_protected(fc: wh->frame_control)) { |
1370 | |
1371 | /* Check if hw crypto has been enabled for |
1372 | * this bss. If yes, set the status flags |
1373 | * accordingly |
1374 | */ |
1375 | mwl8k_vif = mwl8k_find_vif_bss(vif_list: &priv->vif_list, |
1376 | bssid: wh->addr1); |
1377 | |
1378 | if (mwl8k_vif != NULL && |
1379 | mwl8k_vif->is_hw_crypto_enabled) { |
1380 | /* |
1381 | * When MMIC ERROR is encountered |
1382 | * by the firmware, payload is |
1383 | * dropped and only 32 bytes of |
1384 | * mwl8k Firmware header is sent |
1385 | * to the host. |
1386 | * |
1387 | * We need to add four bytes of |
1388 | * key information. In it |
1389 | * MAC80211 expects keyidx set to |
1390 | * 0 for triggering Counter |
1391 | * Measure of MMIC failure. |
1392 | */ |
1393 | if (status.flag & RX_FLAG_MMIC_ERROR) { |
1394 | struct mwl8k_dma_data *tr; |
1395 | tr = (struct mwl8k_dma_data *)skb->data; |
1396 | memset((void *)&(tr->data), 0, 4); |
1397 | pkt_len += 4; |
1398 | } |
1399 | |
1400 | if (!ieee80211_is_auth(fc: wh->frame_control)) |
1401 | status.flag |= RX_FLAG_IV_STRIPPED | |
1402 | RX_FLAG_DECRYPTED | |
1403 | RX_FLAG_MMIC_STRIPPED; |
1404 | } |
1405 | } |
1406 | |
1407 | skb_put(skb, len: pkt_len); |
1408 | mwl8k_remove_dma_header(skb, qos); |
1409 | memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); |
1410 | ieee80211_rx_irqsafe(hw, skb); |
1411 | |
1412 | processed++; |
1413 | } |
1414 | |
1415 | return processed; |
1416 | } |
1417 | |
1418 | |
1419 | /* |
1420 | * Packet transmission. |
1421 | */ |
1422 | |
1423 | #define MWL8K_TXD_STATUS_OK 0x00000001 |
1424 | #define MWL8K_TXD_STATUS_OK_RETRY 0x00000002 |
1425 | #define MWL8K_TXD_STATUS_OK_MORE_RETRY 0x00000004 |
1426 | #define MWL8K_TXD_STATUS_MULTICAST_TX 0x00000008 |
1427 | #define MWL8K_TXD_STATUS_FW_OWNED 0x80000000 |
1428 | |
1429 | #define MWL8K_QOS_QLEN_UNSPEC 0xff00 |
1430 | #define MWL8K_QOS_ACK_POLICY_MASK 0x0060 |
1431 | #define MWL8K_QOS_ACK_POLICY_NORMAL 0x0000 |
1432 | #define MWL8K_QOS_ACK_POLICY_BLOCKACK 0x0060 |
1433 | #define MWL8K_QOS_EOSP 0x0010 |
1434 | |
1435 | struct mwl8k_tx_desc { |
1436 | __le32 status; |
1437 | __u8 data_rate; |
1438 | __u8 tx_priority; |
1439 | __le16 qos_control; |
1440 | __le32 pkt_phys_addr; |
1441 | __le16 pkt_len; |
1442 | __u8 dest_MAC_addr[ETH_ALEN]; |
1443 | __le32 next_txd_phys_addr; |
1444 | __le32 timestamp; |
1445 | __le16 rate_info; |
1446 | __u8 peer_id; |
1447 | __u8 tx_frag_cnt; |
1448 | } __packed; |
1449 | |
1450 | #define MWL8K_TX_DESCS 128 |
1451 | |
1452 | static int mwl8k_txq_init(struct ieee80211_hw *hw, int index) |
1453 | { |
1454 | struct mwl8k_priv *priv = hw->priv; |
1455 | struct mwl8k_tx_queue *txq = priv->txq + index; |
1456 | int size; |
1457 | int i; |
1458 | |
1459 | txq->len = 0; |
1460 | txq->head = 0; |
1461 | txq->tail = 0; |
1462 | |
1463 | size = MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc); |
1464 | |
1465 | txq->txd = dma_alloc_coherent(dev: &priv->pdev->dev, size, dma_handle: &txq->txd_dma, |
1466 | GFP_KERNEL); |
1467 | if (txq->txd == NULL) { |
1468 | wiphy_err(hw->wiphy, "failed to alloc TX descriptors\n" ); |
1469 | return -ENOMEM; |
1470 | } |
1471 | |
1472 | txq->skb = kcalloc(MWL8K_TX_DESCS, size: sizeof(*txq->skb), GFP_KERNEL); |
1473 | if (txq->skb == NULL) { |
1474 | dma_free_coherent(dev: &priv->pdev->dev, size, cpu_addr: txq->txd, |
1475 | dma_handle: txq->txd_dma); |
1476 | txq->txd = NULL; |
1477 | return -ENOMEM; |
1478 | } |
1479 | |
1480 | for (i = 0; i < MWL8K_TX_DESCS; i++) { |
1481 | struct mwl8k_tx_desc *tx_desc; |
1482 | int nexti; |
1483 | |
1484 | tx_desc = txq->txd + i; |
1485 | nexti = (i + 1) % MWL8K_TX_DESCS; |
1486 | |
1487 | tx_desc->status = 0; |
1488 | tx_desc->next_txd_phys_addr = |
1489 | cpu_to_le32(txq->txd_dma + nexti * sizeof(*tx_desc)); |
1490 | } |
1491 | |
1492 | return 0; |
1493 | } |
1494 | |
1495 | static inline void mwl8k_tx_start(struct mwl8k_priv *priv) |
1496 | { |
1497 | iowrite32(MWL8K_H2A_INT_PPA_READY, |
1498 | priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); |
1499 | iowrite32(MWL8K_H2A_INT_DUMMY, |
1500 | priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); |
1501 | ioread32(priv->regs + MWL8K_HIU_INT_CODE); |
1502 | } |
1503 | |
1504 | static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw) |
1505 | { |
1506 | struct mwl8k_priv *priv = hw->priv; |
1507 | int i; |
1508 | |
1509 | for (i = 0; i < mwl8k_tx_queues(priv); i++) { |
1510 | struct mwl8k_tx_queue *txq = priv->txq + i; |
1511 | int fw_owned = 0; |
1512 | int drv_owned = 0; |
1513 | int unused = 0; |
1514 | int desc; |
1515 | |
1516 | for (desc = 0; desc < MWL8K_TX_DESCS; desc++) { |
1517 | struct mwl8k_tx_desc *tx_desc = txq->txd + desc; |
1518 | u32 status; |
1519 | |
1520 | status = le32_to_cpu(tx_desc->status); |
1521 | if (status & MWL8K_TXD_STATUS_FW_OWNED) |
1522 | fw_owned++; |
1523 | else |
1524 | drv_owned++; |
1525 | |
1526 | if (tx_desc->pkt_len == 0) |
1527 | unused++; |
1528 | } |
1529 | |
1530 | wiphy_err(hw->wiphy, |
1531 | "txq[%d] len=%d head=%d tail=%d " |
1532 | "fw_owned=%d drv_owned=%d unused=%d\n" , |
1533 | i, |
1534 | txq->len, txq->head, txq->tail, |
1535 | fw_owned, drv_owned, unused); |
1536 | } |
1537 | } |
1538 | |
1539 | /* |
1540 | * Must be called with priv->fw_mutex held and tx queues stopped. |
1541 | */ |
1542 | #define MWL8K_TX_WAIT_TIMEOUT_MS 5000 |
1543 | |
1544 | static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) |
1545 | { |
1546 | struct mwl8k_priv *priv = hw->priv; |
1547 | DECLARE_COMPLETION_ONSTACK(tx_wait); |
1548 | int retry; |
1549 | int rc; |
1550 | |
1551 | might_sleep(); |
1552 | |
1553 | /* Since fw restart is in progress, allow only the firmware |
1554 | * commands from the restart code and block the other |
1555 | * commands since they are going to fail in any case since |
1556 | * the firmware has crashed |
1557 | */ |
1558 | if (priv->hw_restart_in_progress) { |
1559 | if (priv->hw_restart_owner == current) |
1560 | return 0; |
1561 | else |
1562 | return -EBUSY; |
1563 | } |
1564 | |
1565 | if (atomic_read(v: &priv->watchdog_event_pending)) |
1566 | return 0; |
1567 | |
1568 | /* |
1569 | * The TX queues are stopped at this point, so this test |
1570 | * doesn't need to take ->tx_lock. |
1571 | */ |
1572 | if (!priv->pending_tx_pkts) |
1573 | return 0; |
1574 | |
1575 | retry = 1; |
1576 | rc = 0; |
1577 | |
1578 | spin_lock_bh(lock: &priv->tx_lock); |
1579 | priv->tx_wait = &tx_wait; |
1580 | while (!rc) { |
1581 | int oldcount; |
1582 | unsigned long timeout; |
1583 | |
1584 | oldcount = priv->pending_tx_pkts; |
1585 | |
1586 | spin_unlock_bh(lock: &priv->tx_lock); |
1587 | timeout = wait_for_completion_timeout(x: &tx_wait, |
1588 | timeout: msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS)); |
1589 | |
1590 | if (atomic_read(v: &priv->watchdog_event_pending)) { |
1591 | spin_lock_bh(lock: &priv->tx_lock); |
1592 | priv->tx_wait = NULL; |
1593 | spin_unlock_bh(lock: &priv->tx_lock); |
1594 | return 0; |
1595 | } |
1596 | |
1597 | spin_lock_bh(lock: &priv->tx_lock); |
1598 | |
1599 | if (timeout || !priv->pending_tx_pkts) { |
1600 | WARN_ON(priv->pending_tx_pkts); |
1601 | if (retry) |
1602 | wiphy_notice(hw->wiphy, "tx rings drained\n" ); |
1603 | break; |
1604 | } |
1605 | |
1606 | if (retry) { |
1607 | mwl8k_tx_start(priv); |
1608 | retry = 0; |
1609 | continue; |
1610 | } |
1611 | |
1612 | if (priv->pending_tx_pkts < oldcount) { |
1613 | wiphy_notice(hw->wiphy, |
1614 | "waiting for tx rings to drain (%d -> %d pkts)\n" , |
1615 | oldcount, priv->pending_tx_pkts); |
1616 | retry = 1; |
1617 | continue; |
1618 | } |
1619 | |
1620 | priv->tx_wait = NULL; |
1621 | |
1622 | wiphy_err(hw->wiphy, "tx rings stuck for %d ms\n" , |
1623 | MWL8K_TX_WAIT_TIMEOUT_MS); |
1624 | mwl8k_dump_tx_rings(hw); |
1625 | priv->hw_restart_in_progress = true; |
1626 | ieee80211_queue_work(hw, work: &priv->fw_reload); |
1627 | |
1628 | rc = -ETIMEDOUT; |
1629 | } |
1630 | priv->tx_wait = NULL; |
1631 | spin_unlock_bh(lock: &priv->tx_lock); |
1632 | |
1633 | return rc; |
1634 | } |
1635 | |
1636 | #define MWL8K_TXD_SUCCESS(status) \ |
1637 | ((status) & (MWL8K_TXD_STATUS_OK | \ |
1638 | MWL8K_TXD_STATUS_OK_RETRY | \ |
1639 | MWL8K_TXD_STATUS_OK_MORE_RETRY)) |
1640 | |
1641 | static int mwl8k_tid_queue_mapping(u8 tid) |
1642 | { |
1643 | BUG_ON(tid > 7); |
1644 | |
1645 | switch (tid) { |
1646 | case 0: |
1647 | case 3: |
1648 | return IEEE80211_AC_BE; |
1649 | case 1: |
1650 | case 2: |
1651 | return IEEE80211_AC_BK; |
1652 | case 4: |
1653 | case 5: |
1654 | return IEEE80211_AC_VI; |
1655 | case 6: |
1656 | case 7: |
1657 | return IEEE80211_AC_VO; |
1658 | default: |
1659 | return -1; |
1660 | } |
1661 | } |
1662 | |
1663 | /* The firmware will fill in the rate information |
1664 | * for each packet that gets queued in the hardware |
1665 | * and these macros will interpret that info. |
1666 | */ |
1667 | |
1668 | #define RI_FORMAT(a) (a & 0x0001) |
1669 | #define RI_RATE_ID_MCS(a) ((a & 0x01f8) >> 3) |
1670 | |
1671 | static int |
1672 | mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force) |
1673 | { |
1674 | struct mwl8k_priv *priv = hw->priv; |
1675 | struct mwl8k_tx_queue *txq = priv->txq + index; |
1676 | int processed; |
1677 | |
1678 | processed = 0; |
1679 | while (txq->len > 0 && limit--) { |
1680 | int tx; |
1681 | struct mwl8k_tx_desc *tx_desc; |
1682 | unsigned long addr; |
1683 | int size; |
1684 | struct sk_buff *skb; |
1685 | struct ieee80211_tx_info *info; |
1686 | u32 status; |
1687 | struct ieee80211_sta *sta; |
1688 | struct mwl8k_sta *sta_info = NULL; |
1689 | u16 rate_info; |
1690 | struct ieee80211_hdr *wh; |
1691 | |
1692 | tx = txq->head; |
1693 | tx_desc = txq->txd + tx; |
1694 | |
1695 | status = le32_to_cpu(tx_desc->status); |
1696 | |
1697 | if (status & MWL8K_TXD_STATUS_FW_OWNED) { |
1698 | if (!force) |
1699 | break; |
1700 | tx_desc->status &= |
1701 | ~cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED); |
1702 | } |
1703 | |
1704 | txq->head = (tx + 1) % MWL8K_TX_DESCS; |
1705 | BUG_ON(txq->len == 0); |
1706 | txq->len--; |
1707 | priv->pending_tx_pkts--; |
1708 | |
1709 | addr = le32_to_cpu(tx_desc->pkt_phys_addr); |
1710 | size = le16_to_cpu(tx_desc->pkt_len); |
1711 | skb = txq->skb[tx]; |
1712 | txq->skb[tx] = NULL; |
1713 | |
1714 | BUG_ON(skb == NULL); |
1715 | dma_unmap_single(&priv->pdev->dev, addr, size, DMA_TO_DEVICE); |
1716 | |
1717 | mwl8k_remove_dma_header(skb, qos: tx_desc->qos_control); |
1718 | |
1719 | wh = (struct ieee80211_hdr *) skb->data; |
1720 | |
1721 | /* Mark descriptor as unused */ |
1722 | tx_desc->pkt_phys_addr = 0; |
1723 | tx_desc->pkt_len = 0; |
1724 | |
1725 | info = IEEE80211_SKB_CB(skb); |
1726 | if (ieee80211_is_data(fc: wh->frame_control)) { |
1727 | rcu_read_lock(); |
1728 | sta = ieee80211_find_sta_by_ifaddr(hw, addr: wh->addr1, |
1729 | localaddr: wh->addr2); |
1730 | if (sta) { |
1731 | sta_info = MWL8K_STA(sta); |
1732 | BUG_ON(sta_info == NULL); |
1733 | rate_info = le16_to_cpu(tx_desc->rate_info); |
1734 | /* If rate is < 6.5 Mpbs for an ht station |
1735 | * do not form an ampdu. If the station is a |
1736 | * legacy station (format = 0), do not form an |
1737 | * ampdu |
1738 | */ |
1739 | if (RI_RATE_ID_MCS(rate_info) < 1 || |
1740 | RI_FORMAT(rate_info) == 0) { |
1741 | sta_info->is_ampdu_allowed = false; |
1742 | } else { |
1743 | sta_info->is_ampdu_allowed = true; |
1744 | } |
1745 | } |
1746 | rcu_read_unlock(); |
1747 | } |
1748 | |
1749 | ieee80211_tx_info_clear_status(info); |
1750 | |
1751 | /* Rate control is happening in the firmware. |
1752 | * Ensure no tx rate is being reported. |
1753 | */ |
1754 | info->status.rates[0].idx = -1; |
1755 | info->status.rates[0].count = 1; |
1756 | |
1757 | if (MWL8K_TXD_SUCCESS(status)) |
1758 | info->flags |= IEEE80211_TX_STAT_ACK; |
1759 | |
1760 | ieee80211_tx_status_irqsafe(hw, skb); |
1761 | |
1762 | processed++; |
1763 | } |
1764 | |
1765 | return processed; |
1766 | } |
1767 | |
1768 | /* must be called only when the card's transmit is completely halted */ |
1769 | static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index) |
1770 | { |
1771 | struct mwl8k_priv *priv = hw->priv; |
1772 | struct mwl8k_tx_queue *txq = priv->txq + index; |
1773 | |
1774 | if (txq->txd == NULL) |
1775 | return; |
1776 | |
1777 | mwl8k_txq_reclaim(hw, index, INT_MAX, force: 1); |
1778 | |
1779 | kfree(objp: txq->skb); |
1780 | txq->skb = NULL; |
1781 | |
1782 | dma_free_coherent(dev: &priv->pdev->dev, |
1783 | MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc), |
1784 | cpu_addr: txq->txd, dma_handle: txq->txd_dma); |
1785 | txq->txd = NULL; |
1786 | } |
1787 | |
1788 | /* caller must hold priv->stream_lock when calling the stream functions */ |
1789 | static struct mwl8k_ampdu_stream * |
1790 | mwl8k_add_stream(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 tid) |
1791 | { |
1792 | struct mwl8k_ampdu_stream *stream; |
1793 | struct mwl8k_priv *priv = hw->priv; |
1794 | int i; |
1795 | |
1796 | for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) { |
1797 | stream = &priv->ampdu[i]; |
1798 | if (stream->state == AMPDU_NO_STREAM) { |
1799 | stream->sta = sta; |
1800 | stream->state = AMPDU_STREAM_NEW; |
1801 | stream->tid = tid; |
1802 | stream->idx = i; |
1803 | wiphy_debug(hw->wiphy, "Added a new stream for %pM %d" , |
1804 | sta->addr, tid); |
1805 | return stream; |
1806 | } |
1807 | } |
1808 | return NULL; |
1809 | } |
1810 | |
1811 | static int |
1812 | mwl8k_start_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream) |
1813 | { |
1814 | int ret; |
1815 | |
1816 | /* if the stream has already been started, don't start it again */ |
1817 | if (stream->state != AMPDU_STREAM_NEW) |
1818 | return 0; |
1819 | ret = ieee80211_start_tx_ba_session(sta: stream->sta, tid: stream->tid, timeout: 0); |
1820 | if (ret) |
1821 | wiphy_debug(hw->wiphy, "Failed to start stream for %pM %d: " |
1822 | "%d\n" , stream->sta->addr, stream->tid, ret); |
1823 | else |
1824 | wiphy_debug(hw->wiphy, "Started stream for %pM %d\n" , |
1825 | stream->sta->addr, stream->tid); |
1826 | return ret; |
1827 | } |
1828 | |
1829 | static void |
1830 | mwl8k_remove_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream) |
1831 | { |
1832 | wiphy_debug(hw->wiphy, "Remove stream for %pM %d\n" , stream->sta->addr, |
1833 | stream->tid); |
1834 | memset(stream, 0, sizeof(*stream)); |
1835 | } |
1836 | |
1837 | static struct mwl8k_ampdu_stream * |
1838 | mwl8k_lookup_stream(struct ieee80211_hw *hw, u8 *addr, u8 tid) |
1839 | { |
1840 | struct mwl8k_priv *priv = hw->priv; |
1841 | int i; |
1842 | |
1843 | for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) { |
1844 | struct mwl8k_ampdu_stream *stream; |
1845 | stream = &priv->ampdu[i]; |
1846 | if (stream->state == AMPDU_NO_STREAM) |
1847 | continue; |
1848 | if (!memcmp(p: stream->sta->addr, q: addr, ETH_ALEN) && |
1849 | stream->tid == tid) |
1850 | return stream; |
1851 | } |
1852 | return NULL; |
1853 | } |
1854 | |
1855 | #define MWL8K_AMPDU_PACKET_THRESHOLD 64 |
1856 | static inline bool mwl8k_ampdu_allowed(struct ieee80211_sta *sta, u8 tid) |
1857 | { |
1858 | struct mwl8k_sta *sta_info = MWL8K_STA(sta); |
1859 | struct tx_traffic_info *tx_stats; |
1860 | |
1861 | BUG_ON(tid >= MWL8K_MAX_TID); |
1862 | tx_stats = &sta_info->tx_stats[tid]; |
1863 | |
1864 | return sta_info->is_ampdu_allowed && |
1865 | tx_stats->pkts > MWL8K_AMPDU_PACKET_THRESHOLD; |
1866 | } |
1867 | |
1868 | static inline void mwl8k_tx_count_packet(struct ieee80211_sta *sta, u8 tid) |
1869 | { |
1870 | struct mwl8k_sta *sta_info = MWL8K_STA(sta); |
1871 | struct tx_traffic_info *tx_stats; |
1872 | |
1873 | BUG_ON(tid >= MWL8K_MAX_TID); |
1874 | tx_stats = &sta_info->tx_stats[tid]; |
1875 | |
1876 | if (tx_stats->start_time == 0) |
1877 | tx_stats->start_time = jiffies; |
1878 | |
1879 | /* reset the packet count after each second elapses. If the number of |
1880 | * packets ever exceeds the ampdu_min_traffic threshold, we will allow |
1881 | * an ampdu stream to be started. |
1882 | */ |
1883 | if (time_after(jiffies, (unsigned long)tx_stats->start_time + HZ)) { |
1884 | tx_stats->pkts = 0; |
1885 | tx_stats->start_time = 0; |
1886 | } else |
1887 | tx_stats->pkts++; |
1888 | } |
1889 | |
1890 | /* The hardware ampdu queues start from 5. |
1891 | * txpriorities for ampdu queues are |
1892 | * 5 6 7 0 1 2 3 4 ie., queue 5 is highest |
1893 | * and queue 3 is lowest (queue 4 is reserved) |
1894 | */ |
1895 | #define BA_QUEUE 5 |
1896 | |
1897 | static void |
1898 | mwl8k_txq_xmit(struct ieee80211_hw *hw, |
1899 | int index, |
1900 | struct ieee80211_sta *sta, |
1901 | struct sk_buff *skb) |
1902 | { |
1903 | struct mwl8k_priv *priv = hw->priv; |
1904 | struct ieee80211_tx_info *tx_info; |
1905 | struct mwl8k_vif *mwl8k_vif; |
1906 | struct ieee80211_hdr *wh; |
1907 | struct mwl8k_tx_queue *txq; |
1908 | struct mwl8k_tx_desc *tx; |
1909 | dma_addr_t dma; |
1910 | u32 txstatus; |
1911 | u8 txdatarate; |
1912 | u16 qos; |
1913 | int txpriority; |
1914 | u8 tid = 0; |
1915 | struct mwl8k_ampdu_stream *stream = NULL; |
1916 | bool start_ba_session = false; |
1917 | bool mgmtframe = false; |
1918 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; |
1919 | bool eapol_frame = false; |
1920 | |
1921 | wh = (struct ieee80211_hdr *)skb->data; |
1922 | if (ieee80211_is_data_qos(fc: wh->frame_control)) |
1923 | qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh))); |
1924 | else |
1925 | qos = 0; |
1926 | |
1927 | if (skb->protocol == cpu_to_be16(ETH_P_PAE)) |
1928 | eapol_frame = true; |
1929 | |
1930 | if (ieee80211_is_mgmt(fc: wh->frame_control)) |
1931 | mgmtframe = true; |
1932 | |
1933 | if (priv->ap_fw) |
1934 | mwl8k_encapsulate_tx_frame(priv, skb); |
1935 | else |
1936 | mwl8k_add_dma_header(priv, skb, head_pad: 0, tail_pad: 0); |
1937 | |
1938 | wh = &((struct mwl8k_dma_data *)skb->data)->wh; |
1939 | |
1940 | tx_info = IEEE80211_SKB_CB(skb); |
1941 | mwl8k_vif = MWL8K_VIF(tx_info->control.vif); |
1942 | |
1943 | if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { |
1944 | wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); |
1945 | wh->seq_ctrl |= cpu_to_le16(mwl8k_vif->seqno); |
1946 | mwl8k_vif->seqno += 0x10; |
1947 | } |
1948 | |
1949 | /* Setup firmware control bit fields for each frame type. */ |
1950 | txstatus = 0; |
1951 | txdatarate = 0; |
1952 | if (ieee80211_is_mgmt(fc: wh->frame_control) || |
1953 | ieee80211_is_ctl(fc: wh->frame_control)) { |
1954 | txdatarate = 0; |
1955 | qos |= MWL8K_QOS_QLEN_UNSPEC | MWL8K_QOS_EOSP; |
1956 | } else if (ieee80211_is_data(fc: wh->frame_control)) { |
1957 | txdatarate = 1; |
1958 | if (is_multicast_ether_addr(addr: wh->addr1)) |
1959 | txstatus |= MWL8K_TXD_STATUS_MULTICAST_TX; |
1960 | |
1961 | qos &= ~MWL8K_QOS_ACK_POLICY_MASK; |
1962 | if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) |
1963 | qos |= MWL8K_QOS_ACK_POLICY_BLOCKACK; |
1964 | else |
1965 | qos |= MWL8K_QOS_ACK_POLICY_NORMAL; |
1966 | } |
1967 | |
1968 | /* Queue ADDBA request in the respective data queue. While setting up |
1969 | * the ampdu stream, mac80211 queues further packets for that |
1970 | * particular ra/tid pair. However, packets piled up in the hardware |
1971 | * for that ra/tid pair will still go out. ADDBA request and the |
1972 | * related data packets going out from different queues asynchronously |
1973 | * will cause a shift in the receiver window which might result in |
1974 | * ampdu packets getting dropped at the receiver after the stream has |
1975 | * been setup. |
1976 | */ |
1977 | if (unlikely(ieee80211_is_action(wh->frame_control) && |
1978 | mgmt->u.action.category == WLAN_CATEGORY_BACK && |
1979 | mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ && |
1980 | priv->ap_fw)) { |
1981 | u16 capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); |
1982 | tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; |
1983 | index = mwl8k_tid_queue_mapping(tid); |
1984 | } |
1985 | |
1986 | txpriority = index; |
1987 | |
1988 | if (priv->ap_fw && sta && sta->deflink.ht_cap.ht_supported && !eapol_frame && |
1989 | ieee80211_is_data_qos(fc: wh->frame_control)) { |
1990 | tid = qos & 0xf; |
1991 | mwl8k_tx_count_packet(sta, tid); |
1992 | spin_lock(lock: &priv->stream_lock); |
1993 | stream = mwl8k_lookup_stream(hw, addr: sta->addr, tid); |
1994 | if (stream != NULL) { |
1995 | if (stream->state == AMPDU_STREAM_ACTIVE) { |
1996 | WARN_ON(!(qos & MWL8K_QOS_ACK_POLICY_BLOCKACK)); |
1997 | txpriority = (BA_QUEUE + stream->idx) % |
1998 | TOTAL_HW_TX_QUEUES; |
1999 | if (stream->idx <= 1) |
2000 | index = stream->idx + |
2001 | MWL8K_TX_WMM_QUEUES; |
2002 | |
2003 | } else if (stream->state == AMPDU_STREAM_NEW) { |
2004 | /* We get here if the driver sends us packets |
2005 | * after we've initiated a stream, but before |
2006 | * our ampdu_action routine has been called |
2007 | * with IEEE80211_AMPDU_TX_START to get the SSN |
2008 | * for the ADDBA request. So this packet can |
2009 | * go out with no risk of sequence number |
2010 | * mismatch. No special handling is required. |
2011 | */ |
2012 | } else { |
2013 | /* Drop packets that would go out after the |
2014 | * ADDBA request was sent but before the ADDBA |
2015 | * response is received. If we don't do this, |
2016 | * the recipient would probably receive it |
2017 | * after the ADDBA request with SSN 0. This |
2018 | * will cause the recipient's BA receive window |
2019 | * to shift, which would cause the subsequent |
2020 | * packets in the BA stream to be discarded. |
2021 | * mac80211 queues our packets for us in this |
2022 | * case, so this is really just a safety check. |
2023 | */ |
2024 | wiphy_warn(hw->wiphy, |
2025 | "Cannot send packet while ADDBA " |
2026 | "dialog is underway.\n" ); |
2027 | spin_unlock(lock: &priv->stream_lock); |
2028 | dev_kfree_skb(skb); |
2029 | return; |
2030 | } |
2031 | } else { |
2032 | /* Defer calling mwl8k_start_stream so that the current |
2033 | * skb can go out before the ADDBA request. This |
2034 | * prevents sequence number mismatch at the recepient |
2035 | * as described above. |
2036 | */ |
2037 | if (mwl8k_ampdu_allowed(sta, tid)) { |
2038 | stream = mwl8k_add_stream(hw, sta, tid); |
2039 | if (stream != NULL) |
2040 | start_ba_session = true; |
2041 | } |
2042 | } |
2043 | spin_unlock(lock: &priv->stream_lock); |
2044 | } else { |
2045 | qos &= ~MWL8K_QOS_ACK_POLICY_MASK; |
2046 | qos |= MWL8K_QOS_ACK_POLICY_NORMAL; |
2047 | } |
2048 | |
2049 | dma = dma_map_single(&priv->pdev->dev, skb->data, skb->len, |
2050 | DMA_TO_DEVICE); |
2051 | |
2052 | if (dma_mapping_error(dev: &priv->pdev->dev, dma_addr: dma)) { |
2053 | wiphy_debug(hw->wiphy, |
2054 | "failed to dma map skb, dropping TX frame.\n" ); |
2055 | if (start_ba_session) { |
2056 | spin_lock(lock: &priv->stream_lock); |
2057 | mwl8k_remove_stream(hw, stream); |
2058 | spin_unlock(lock: &priv->stream_lock); |
2059 | } |
2060 | dev_kfree_skb(skb); |
2061 | return; |
2062 | } |
2063 | |
2064 | spin_lock_bh(lock: &priv->tx_lock); |
2065 | |
2066 | txq = priv->txq + index; |
2067 | |
2068 | /* Mgmt frames that go out frequently are probe |
2069 | * responses. Other mgmt frames got out relatively |
2070 | * infrequently. Hence reserve 2 buffers so that |
2071 | * other mgmt frames do not get dropped due to an |
2072 | * already queued probe response in one of the |
2073 | * reserved buffers. |
2074 | */ |
2075 | |
2076 | if (txq->len >= MWL8K_TX_DESCS - 2) { |
2077 | if (!mgmtframe || txq->len == MWL8K_TX_DESCS) { |
2078 | if (start_ba_session) { |
2079 | spin_lock(lock: &priv->stream_lock); |
2080 | mwl8k_remove_stream(hw, stream); |
2081 | spin_unlock(lock: &priv->stream_lock); |
2082 | } |
2083 | mwl8k_tx_start(priv); |
2084 | spin_unlock_bh(lock: &priv->tx_lock); |
2085 | dma_unmap_single(&priv->pdev->dev, dma, skb->len, |
2086 | DMA_TO_DEVICE); |
2087 | dev_kfree_skb(skb); |
2088 | return; |
2089 | } |
2090 | } |
2091 | |
2092 | BUG_ON(txq->skb[txq->tail] != NULL); |
2093 | txq->skb[txq->tail] = skb; |
2094 | |
2095 | tx = txq->txd + txq->tail; |
2096 | tx->data_rate = txdatarate; |
2097 | tx->tx_priority = txpriority; |
2098 | tx->qos_control = cpu_to_le16(qos); |
2099 | tx->pkt_phys_addr = cpu_to_le32(dma); |
2100 | tx->pkt_len = cpu_to_le16(skb->len); |
2101 | tx->rate_info = 0; |
2102 | if (!priv->ap_fw && sta != NULL) |
2103 | tx->peer_id = MWL8K_STA(sta)->peer_id; |
2104 | else |
2105 | tx->peer_id = 0; |
2106 | |
2107 | if (priv->ap_fw && ieee80211_is_data(fc: wh->frame_control) && !eapol_frame) |
2108 | tx->timestamp = cpu_to_le32(ioread32(priv->regs + |
2109 | MWL8K_HW_TIMER_REGISTER)); |
2110 | else |
2111 | tx->timestamp = 0; |
2112 | |
2113 | wmb(); |
2114 | tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus); |
2115 | |
2116 | txq->len++; |
2117 | priv->pending_tx_pkts++; |
2118 | |
2119 | txq->tail++; |
2120 | if (txq->tail == MWL8K_TX_DESCS) |
2121 | txq->tail = 0; |
2122 | |
2123 | mwl8k_tx_start(priv); |
2124 | |
2125 | spin_unlock_bh(lock: &priv->tx_lock); |
2126 | |
2127 | /* Initiate the ampdu session here */ |
2128 | if (start_ba_session) { |
2129 | spin_lock(lock: &priv->stream_lock); |
2130 | if (mwl8k_start_stream(hw, stream)) |
2131 | mwl8k_remove_stream(hw, stream); |
2132 | spin_unlock(lock: &priv->stream_lock); |
2133 | } |
2134 | } |
2135 | |
2136 | |
2137 | /* |
2138 | * Firmware access. |
2139 | * |
2140 | * We have the following requirements for issuing firmware commands: |
2141 | * - Some commands require that the packet transmit path is idle when |
2142 | * the command is issued. (For simplicity, we'll just quiesce the |
2143 | * transmit path for every command.) |
2144 | * - There are certain sequences of commands that need to be issued to |
2145 | * the hardware sequentially, with no other intervening commands. |
2146 | * |
2147 | * This leads to an implementation of a "firmware lock" as a mutex that |
2148 | * can be taken recursively, and which is taken by both the low-level |
2149 | * command submission function (mwl8k_post_cmd) as well as any users of |
2150 | * that function that require issuing of an atomic sequence of commands, |
2151 | * and quiesces the transmit path whenever it's taken. |
2152 | */ |
2153 | static int mwl8k_fw_lock(struct ieee80211_hw *hw) |
2154 | { |
2155 | struct mwl8k_priv *priv = hw->priv; |
2156 | |
2157 | if (priv->fw_mutex_owner != current) { |
2158 | int rc; |
2159 | |
2160 | mutex_lock(&priv->fw_mutex); |
2161 | ieee80211_stop_queues(hw); |
2162 | |
2163 | rc = mwl8k_tx_wait_empty(hw); |
2164 | if (rc) { |
2165 | if (!priv->hw_restart_in_progress) |
2166 | ieee80211_wake_queues(hw); |
2167 | |
2168 | mutex_unlock(lock: &priv->fw_mutex); |
2169 | |
2170 | return rc; |
2171 | } |
2172 | |
2173 | priv->fw_mutex_owner = current; |
2174 | } |
2175 | |
2176 | priv->fw_mutex_depth++; |
2177 | |
2178 | return 0; |
2179 | } |
2180 | |
2181 | static void mwl8k_fw_unlock(struct ieee80211_hw *hw) |
2182 | { |
2183 | struct mwl8k_priv *priv = hw->priv; |
2184 | |
2185 | if (!--priv->fw_mutex_depth) { |
2186 | if (!priv->hw_restart_in_progress) |
2187 | ieee80211_wake_queues(hw); |
2188 | |
2189 | priv->fw_mutex_owner = NULL; |
2190 | mutex_unlock(lock: &priv->fw_mutex); |
2191 | } |
2192 | } |
2193 | |
2194 | static void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable, |
2195 | u32 bitmap); |
2196 | |
2197 | /* |
2198 | * Command processing. |
2199 | */ |
2200 | |
2201 | /* Timeout firmware commands after 10s */ |
2202 | #define MWL8K_CMD_TIMEOUT_MS 10000 |
2203 | |
2204 | static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) |
2205 | { |
2206 | DECLARE_COMPLETION_ONSTACK(cmd_wait); |
2207 | struct mwl8k_priv *priv = hw->priv; |
2208 | void __iomem *regs = priv->regs; |
2209 | dma_addr_t dma_addr; |
2210 | unsigned int dma_size; |
2211 | int rc; |
2212 | unsigned long timeout = 0; |
2213 | u8 buf[32]; |
2214 | u32 bitmap = 0; |
2215 | |
2216 | wiphy_dbg(hw->wiphy, "Posting %s [%d]\n" , |
2217 | mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), cmd->macid); |
2218 | |
2219 | /* Before posting firmware commands that could change the hardware |
2220 | * characteristics, make sure that all BSSes are stopped temporary. |
2221 | * Enable these stopped BSSes after completion of the commands |
2222 | */ |
2223 | |
2224 | rc = mwl8k_fw_lock(hw); |
2225 | if (rc) |
2226 | return rc; |
2227 | |
2228 | if (priv->ap_fw && priv->running_bsses) { |
2229 | switch (le16_to_cpu(cmd->code)) { |
2230 | case MWL8K_CMD_SET_RF_CHANNEL: |
2231 | case MWL8K_CMD_RADIO_CONTROL: |
2232 | case MWL8K_CMD_RF_TX_POWER: |
2233 | case MWL8K_CMD_TX_POWER: |
2234 | case MWL8K_CMD_RF_ANTENNA: |
2235 | case MWL8K_CMD_RTS_THRESHOLD: |
2236 | case MWL8K_CMD_MIMO_CONFIG: |
2237 | bitmap = priv->running_bsses; |
2238 | mwl8k_enable_bsses(hw, enable: false, bitmap); |
2239 | break; |
2240 | } |
2241 | } |
2242 | |
2243 | cmd->result = (__force __le16) 0xffff; |
2244 | dma_size = le16_to_cpu(cmd->length); |
2245 | dma_addr = dma_map_single(&priv->pdev->dev, cmd, dma_size, |
2246 | DMA_BIDIRECTIONAL); |
2247 | if (dma_mapping_error(dev: &priv->pdev->dev, dma_addr)) { |
2248 | rc = -ENOMEM; |
2249 | goto exit; |
2250 | } |
2251 | |
2252 | priv->hostcmd_wait = &cmd_wait; |
2253 | iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR); |
2254 | iowrite32(MWL8K_H2A_INT_DOORBELL, |
2255 | regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); |
2256 | iowrite32(MWL8K_H2A_INT_DUMMY, |
2257 | regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); |
2258 | |
2259 | timeout = wait_for_completion_timeout(x: &cmd_wait, |
2260 | timeout: msecs_to_jiffies(MWL8K_CMD_TIMEOUT_MS)); |
2261 | |
2262 | priv->hostcmd_wait = NULL; |
2263 | |
2264 | |
2265 | dma_unmap_single(&priv->pdev->dev, dma_addr, dma_size, |
2266 | DMA_BIDIRECTIONAL); |
2267 | |
2268 | if (!timeout) { |
2269 | wiphy_err(hw->wiphy, "Command %s timeout after %u ms\n" , |
2270 | mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), |
2271 | MWL8K_CMD_TIMEOUT_MS); |
2272 | rc = -ETIMEDOUT; |
2273 | } else { |
2274 | int ms; |
2275 | |
2276 | ms = MWL8K_CMD_TIMEOUT_MS - jiffies_to_msecs(j: timeout); |
2277 | |
2278 | rc = cmd->result ? -EINVAL : 0; |
2279 | if (rc) |
2280 | wiphy_err(hw->wiphy, "Command %s error 0x%x\n" , |
2281 | mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), |
2282 | le16_to_cpu(cmd->result)); |
2283 | else if (ms > 2000) |
2284 | wiphy_notice(hw->wiphy, "Command %s took %d ms\n" , |
2285 | mwl8k_cmd_name(cmd->code, |
2286 | buf, sizeof(buf)), |
2287 | ms); |
2288 | } |
2289 | |
2290 | exit: |
2291 | if (bitmap) |
2292 | mwl8k_enable_bsses(hw, enable: true, bitmap); |
2293 | |
2294 | mwl8k_fw_unlock(hw); |
2295 | |
2296 | return rc; |
2297 | } |
2298 | |
2299 | static int mwl8k_post_pervif_cmd(struct ieee80211_hw *hw, |
2300 | struct ieee80211_vif *vif, |
2301 | struct mwl8k_cmd_pkt *cmd) |
2302 | { |
2303 | if (vif != NULL) |
2304 | cmd->macid = MWL8K_VIF(vif)->macid; |
2305 | return mwl8k_post_cmd(hw, cmd); |
2306 | } |
2307 | |
2308 | /* |
2309 | * Setup code shared between STA and AP firmware images. |
2310 | */ |
2311 | static void mwl8k_setup_2ghz_band(struct ieee80211_hw *hw) |
2312 | { |
2313 | struct mwl8k_priv *priv = hw->priv; |
2314 | |
2315 | BUILD_BUG_ON(sizeof(priv->channels_24) != sizeof(mwl8k_channels_24)); |
2316 | memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24)); |
2317 | |
2318 | BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24)); |
2319 | memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24)); |
2320 | |
2321 | priv->band_24.band = NL80211_BAND_2GHZ; |
2322 | priv->band_24.channels = priv->channels_24; |
2323 | priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24); |
2324 | priv->band_24.bitrates = priv->rates_24; |
2325 | priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24); |
2326 | |
2327 | hw->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band_24; |
2328 | } |
2329 | |
2330 | static void mwl8k_setup_5ghz_band(struct ieee80211_hw *hw) |
2331 | { |
2332 | struct mwl8k_priv *priv = hw->priv; |
2333 | |
2334 | BUILD_BUG_ON(sizeof(priv->channels_50) != sizeof(mwl8k_channels_50)); |
2335 | memcpy(priv->channels_50, mwl8k_channels_50, sizeof(mwl8k_channels_50)); |
2336 | |
2337 | BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl8k_rates_50)); |
2338 | memcpy(priv->rates_50, mwl8k_rates_50, sizeof(mwl8k_rates_50)); |
2339 | |
2340 | priv->band_50.band = NL80211_BAND_5GHZ; |
2341 | priv->band_50.channels = priv->channels_50; |
2342 | priv->band_50.n_channels = ARRAY_SIZE(mwl8k_channels_50); |
2343 | priv->band_50.bitrates = priv->rates_50; |
2344 | priv->band_50.n_bitrates = ARRAY_SIZE(mwl8k_rates_50); |
2345 | |
2346 | hw->wiphy->bands[NL80211_BAND_5GHZ] = &priv->band_50; |
2347 | } |
2348 | |
2349 | /* |
2350 | * CMD_GET_HW_SPEC (STA version). |
2351 | */ |
2352 | struct mwl8k_cmd_get_hw_spec_sta { |
2353 | struct mwl8k_cmd_pkt ; |
2354 | __u8 hw_rev; |
2355 | __u8 host_interface; |
2356 | __le16 num_mcaddrs; |
2357 | __u8 perm_addr[ETH_ALEN]; |
2358 | __le16 region_code; |
2359 | __le32 fw_rev; |
2360 | __le32 ps_cookie; |
2361 | __le32 caps; |
2362 | __u8 mcs_bitmap[16]; |
2363 | __le32 rx_queue_ptr; |
2364 | __le32 num_tx_queues; |
2365 | __le32 tx_queue_ptrs[MWL8K_TX_WMM_QUEUES]; |
2366 | __le32 caps2; |
2367 | __le32 num_tx_desc_per_queue; |
2368 | __le32 total_rxd; |
2369 | } __packed; |
2370 | |
2371 | #define MWL8K_CAP_MAX_AMSDU 0x20000000 |
2372 | #define MWL8K_CAP_GREENFIELD 0x08000000 |
2373 | #define MWL8K_CAP_AMPDU 0x04000000 |
2374 | #define MWL8K_CAP_RX_STBC 0x01000000 |
2375 | #define MWL8K_CAP_TX_STBC 0x00800000 |
2376 | #define MWL8K_CAP_SHORTGI_40MHZ 0x00400000 |
2377 | #define MWL8K_CAP_SHORTGI_20MHZ 0x00200000 |
2378 | #define MWL8K_CAP_RX_ANTENNA_MASK 0x000e0000 |
2379 | #define MWL8K_CAP_TX_ANTENNA_MASK 0x0001c000 |
2380 | #define MWL8K_CAP_DELAY_BA 0x00003000 |
2381 | #define MWL8K_CAP_MIMO 0x00000200 |
2382 | #define MWL8K_CAP_40MHZ 0x00000100 |
2383 | #define MWL8K_CAP_BAND_MASK 0x00000007 |
2384 | #define MWL8K_CAP_5GHZ 0x00000004 |
2385 | #define MWL8K_CAP_2GHZ4 0x00000001 |
2386 | |
2387 | static void |
2388 | mwl8k_set_ht_caps(struct ieee80211_hw *hw, |
2389 | struct ieee80211_supported_band *band, u32 cap) |
2390 | { |
2391 | int rx_streams; |
2392 | int tx_streams; |
2393 | |
2394 | band->ht_cap.ht_supported = 1; |
2395 | |
2396 | if (cap & MWL8K_CAP_MAX_AMSDU) |
2397 | band->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; |
2398 | if (cap & MWL8K_CAP_GREENFIELD) |
2399 | band->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD; |
2400 | if (cap & MWL8K_CAP_AMPDU) { |
2401 | ieee80211_hw_set(hw, AMPDU_AGGREGATION); |
2402 | band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; |
2403 | band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; |
2404 | } |
2405 | if (cap & MWL8K_CAP_RX_STBC) |
2406 | band->ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC; |
2407 | if (cap & MWL8K_CAP_TX_STBC) |
2408 | band->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; |
2409 | if (cap & MWL8K_CAP_SHORTGI_40MHZ) |
2410 | band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; |
2411 | if (cap & MWL8K_CAP_SHORTGI_20MHZ) |
2412 | band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; |
2413 | if (cap & MWL8K_CAP_DELAY_BA) |
2414 | band->ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA; |
2415 | if (cap & MWL8K_CAP_40MHZ) |
2416 | band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; |
2417 | |
2418 | rx_streams = hweight32(cap & MWL8K_CAP_RX_ANTENNA_MASK); |
2419 | tx_streams = hweight32(cap & MWL8K_CAP_TX_ANTENNA_MASK); |
2420 | |
2421 | band->ht_cap.mcs.rx_mask[0] = 0xff; |
2422 | if (rx_streams >= 2) |
2423 | band->ht_cap.mcs.rx_mask[1] = 0xff; |
2424 | if (rx_streams >= 3) |
2425 | band->ht_cap.mcs.rx_mask[2] = 0xff; |
2426 | band->ht_cap.mcs.rx_mask[4] = 0x01; |
2427 | band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; |
2428 | |
2429 | if (rx_streams != tx_streams) { |
2430 | band->ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; |
2431 | band->ht_cap.mcs.tx_params |= (tx_streams - 1) << |
2432 | IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; |
2433 | } |
2434 | } |
2435 | |
2436 | static void |
2437 | mwl8k_set_caps(struct ieee80211_hw *hw, u32 caps) |
2438 | { |
2439 | struct mwl8k_priv *priv = hw->priv; |
2440 | |
2441 | if (priv->caps) |
2442 | return; |
2443 | |
2444 | if ((caps & MWL8K_CAP_2GHZ4) || !(caps & MWL8K_CAP_BAND_MASK)) { |
2445 | mwl8k_setup_2ghz_band(hw); |
2446 | if (caps & MWL8K_CAP_MIMO) |
2447 | mwl8k_set_ht_caps(hw, band: &priv->band_24, cap: caps); |
2448 | } |
2449 | |
2450 | if (caps & MWL8K_CAP_5GHZ) { |
2451 | mwl8k_setup_5ghz_band(hw); |
2452 | if (caps & MWL8K_CAP_MIMO) |
2453 | mwl8k_set_ht_caps(hw, band: &priv->band_50, cap: caps); |
2454 | } |
2455 | |
2456 | priv->caps = caps; |
2457 | } |
2458 | |
2459 | static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) |
2460 | { |
2461 | struct mwl8k_priv *priv = hw->priv; |
2462 | struct mwl8k_cmd_get_hw_spec_sta *cmd; |
2463 | int rc; |
2464 | int i; |
2465 | |
2466 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
2467 | if (cmd == NULL) |
2468 | return -ENOMEM; |
2469 | |
2470 | cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC); |
2471 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
2472 | |
2473 | memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr)); |
2474 | cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); |
2475 | cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma); |
2476 | cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv)); |
2477 | for (i = 0; i < mwl8k_tx_queues(priv); i++) |
2478 | cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma); |
2479 | cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); |
2480 | cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); |
2481 | |
2482 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
2483 | |
2484 | if (!rc) { |
2485 | SET_IEEE80211_PERM_ADDR(hw, addr: cmd->perm_addr); |
2486 | priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); |
2487 | priv->fw_rev = le32_to_cpu(cmd->fw_rev); |
2488 | priv->hw_rev = cmd->hw_rev; |
2489 | mwl8k_set_caps(hw, le32_to_cpu(cmd->caps)); |
2490 | priv->ap_macids_supported = 0x00000000; |
2491 | priv->sta_macids_supported = 0x00000001; |
2492 | } |
2493 | |
2494 | kfree(objp: cmd); |
2495 | return rc; |
2496 | } |
2497 | |
2498 | /* |
2499 | * CMD_GET_HW_SPEC (AP version). |
2500 | */ |
2501 | struct mwl8k_cmd_get_hw_spec_ap { |
2502 | struct mwl8k_cmd_pkt ; |
2503 | __u8 hw_rev; |
2504 | __u8 host_interface; |
2505 | __le16 num_wcb; |
2506 | __le16 num_mcaddrs; |
2507 | __u8 perm_addr[ETH_ALEN]; |
2508 | __le16 region_code; |
2509 | __le16 num_antenna; |
2510 | __le32 fw_rev; |
2511 | __le32 wcbbase0; |
2512 | __le32 rxwrptr; |
2513 | __le32 rxrdptr; |
2514 | __le32 ps_cookie; |
2515 | __le32 wcbbase1; |
2516 | __le32 wcbbase2; |
2517 | __le32 wcbbase3; |
2518 | __le32 fw_api_version; |
2519 | __le32 caps; |
2520 | __le32 num_of_ampdu_queues; |
2521 | __le32 wcbbase_ampdu[MWL8K_MAX_AMPDU_QUEUES]; |
2522 | } __packed; |
2523 | |
2524 | static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw) |
2525 | { |
2526 | struct mwl8k_priv *priv = hw->priv; |
2527 | struct mwl8k_cmd_get_hw_spec_ap *cmd; |
2528 | int rc, i; |
2529 | u32 api_version; |
2530 | |
2531 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
2532 | if (cmd == NULL) |
2533 | return -ENOMEM; |
2534 | |
2535 | cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC); |
2536 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
2537 | |
2538 | memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr)); |
2539 | cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); |
2540 | |
2541 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
2542 | |
2543 | if (!rc) { |
2544 | int off; |
2545 | |
2546 | api_version = le32_to_cpu(cmd->fw_api_version); |
2547 | if (priv->device_info->fw_api_ap != api_version) { |
2548 | printk(KERN_ERR "%s: Unsupported fw API version for %s." |
2549 | " Expected %d got %d.\n" , MWL8K_NAME, |
2550 | priv->device_info->part_name, |
2551 | priv->device_info->fw_api_ap, |
2552 | api_version); |
2553 | rc = -EINVAL; |
2554 | goto done; |
2555 | } |
2556 | SET_IEEE80211_PERM_ADDR(hw, addr: cmd->perm_addr); |
2557 | priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); |
2558 | priv->fw_rev = le32_to_cpu(cmd->fw_rev); |
2559 | priv->hw_rev = cmd->hw_rev; |
2560 | mwl8k_set_caps(hw, le32_to_cpu(cmd->caps)); |
2561 | priv->ap_macids_supported = 0x000000ff; |
2562 | priv->sta_macids_supported = 0x00000100; |
2563 | priv->num_ampdu_queues = le32_to_cpu(cmd->num_of_ampdu_queues); |
2564 | if (priv->num_ampdu_queues > MWL8K_MAX_AMPDU_QUEUES) { |
2565 | wiphy_warn(hw->wiphy, "fw reported %d ampdu queues" |
2566 | " but we only support %d.\n" , |
2567 | priv->num_ampdu_queues, |
2568 | MWL8K_MAX_AMPDU_QUEUES); |
2569 | priv->num_ampdu_queues = MWL8K_MAX_AMPDU_QUEUES; |
2570 | } |
2571 | off = le32_to_cpu(cmd->rxwrptr) & 0xffff; |
2572 | iowrite32(priv->rxq[0].rxd_dma, priv->sram + off); |
2573 | |
2574 | off = le32_to_cpu(cmd->rxrdptr) & 0xffff; |
2575 | iowrite32(priv->rxq[0].rxd_dma, priv->sram + off); |
2576 | |
2577 | priv->txq_offset[0] = le32_to_cpu(cmd->wcbbase0) & 0xffff; |
2578 | priv->txq_offset[1] = le32_to_cpu(cmd->wcbbase1) & 0xffff; |
2579 | priv->txq_offset[2] = le32_to_cpu(cmd->wcbbase2) & 0xffff; |
2580 | priv->txq_offset[3] = le32_to_cpu(cmd->wcbbase3) & 0xffff; |
2581 | |
2582 | for (i = 0; i < priv->num_ampdu_queues; i++) |
2583 | priv->txq_offset[i + MWL8K_TX_WMM_QUEUES] = |
2584 | le32_to_cpu(cmd->wcbbase_ampdu[i]) & 0xffff; |
2585 | } |
2586 | |
2587 | done: |
2588 | kfree(objp: cmd); |
2589 | return rc; |
2590 | } |
2591 | |
2592 | /* |
2593 | * CMD_SET_HW_SPEC. |
2594 | */ |
2595 | struct mwl8k_cmd_set_hw_spec { |
2596 | struct mwl8k_cmd_pkt ; |
2597 | __u8 hw_rev; |
2598 | __u8 host_interface; |
2599 | __le16 num_mcaddrs; |
2600 | __u8 perm_addr[ETH_ALEN]; |
2601 | __le16 region_code; |
2602 | __le32 fw_rev; |
2603 | __le32 ps_cookie; |
2604 | __le32 caps; |
2605 | __le32 rx_queue_ptr; |
2606 | __le32 num_tx_queues; |
2607 | __le32 tx_queue_ptrs[MWL8K_MAX_TX_QUEUES]; |
2608 | __le32 flags; |
2609 | __le32 num_tx_desc_per_queue; |
2610 | __le32 total_rxd; |
2611 | } __packed; |
2612 | |
2613 | /* If enabled, MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY will cause |
2614 | * packets to expire 500 ms after the timestamp in the tx descriptor. That is, |
2615 | * the packets that are queued for more than 500ms, will be dropped in the |
2616 | * hardware. This helps minimizing the issues caused due to head-of-line |
2617 | * blocking where a slow client can hog the bandwidth and affect traffic to a |
2618 | * faster client. |
2619 | */ |
2620 | #define MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY 0x00000400 |
2621 | #define MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR 0x00000200 |
2622 | #define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080 |
2623 | #define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP 0x00000020 |
2624 | #define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON 0x00000010 |
2625 | |
2626 | static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) |
2627 | { |
2628 | struct mwl8k_priv *priv = hw->priv; |
2629 | struct mwl8k_cmd_set_hw_spec *cmd; |
2630 | int rc; |
2631 | int i; |
2632 | |
2633 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
2634 | if (cmd == NULL) |
2635 | return -ENOMEM; |
2636 | |
2637 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_HW_SPEC); |
2638 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
2639 | |
2640 | cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); |
2641 | cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma); |
2642 | cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv)); |
2643 | |
2644 | /* |
2645 | * Mac80211 stack has Q0 as highest priority and Q3 as lowest in |
2646 | * that order. Firmware has Q3 as highest priority and Q0 as lowest |
2647 | * in that order. Map Q3 of mac80211 to Q0 of firmware so that the |
2648 | * priority is interpreted the right way in firmware. |
2649 | */ |
2650 | for (i = 0; i < mwl8k_tx_queues(priv); i++) { |
2651 | int j = mwl8k_tx_queues(priv) - 1 - i; |
2652 | cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[j].txd_dma); |
2653 | } |
2654 | |
2655 | cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT | |
2656 | MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP | |
2657 | MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON | |
2658 | MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY | |
2659 | MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR); |
2660 | cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); |
2661 | cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); |
2662 | |
2663 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
2664 | kfree(objp: cmd); |
2665 | |
2666 | return rc; |
2667 | } |
2668 | |
2669 | /* |
2670 | * CMD_MAC_MULTICAST_ADR. |
2671 | */ |
2672 | struct mwl8k_cmd_mac_multicast_adr { |
2673 | struct mwl8k_cmd_pkt ; |
2674 | __le16 action; |
2675 | __le16 numaddr; |
2676 | __u8 addr[][ETH_ALEN]; |
2677 | }; |
2678 | |
2679 | #define MWL8K_ENABLE_RX_DIRECTED 0x0001 |
2680 | #define MWL8K_ENABLE_RX_MULTICAST 0x0002 |
2681 | #define MWL8K_ENABLE_RX_ALL_MULTICAST 0x0004 |
2682 | #define MWL8K_ENABLE_RX_BROADCAST 0x0008 |
2683 | |
2684 | static struct mwl8k_cmd_pkt * |
2685 | __mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti, |
2686 | struct netdev_hw_addr_list *mc_list) |
2687 | { |
2688 | struct mwl8k_priv *priv = hw->priv; |
2689 | struct mwl8k_cmd_mac_multicast_adr *cmd; |
2690 | int size; |
2691 | int mc_count = 0; |
2692 | |
2693 | if (mc_list) |
2694 | mc_count = netdev_hw_addr_list_count(mc_list); |
2695 | |
2696 | if (allmulti || mc_count > priv->num_mcaddrs) { |
2697 | allmulti = 1; |
2698 | mc_count = 0; |
2699 | } |
2700 | |
2701 | size = sizeof(*cmd) + mc_count * ETH_ALEN; |
2702 | |
2703 | cmd = kzalloc(size, GFP_ATOMIC); |
2704 | if (cmd == NULL) |
2705 | return NULL; |
2706 | |
2707 | cmd->header.code = cpu_to_le16(MWL8K_CMD_MAC_MULTICAST_ADR); |
2708 | cmd->header.length = cpu_to_le16(size); |
2709 | cmd->action = cpu_to_le16(MWL8K_ENABLE_RX_DIRECTED | |
2710 | MWL8K_ENABLE_RX_BROADCAST); |
2711 | |
2712 | if (allmulti) { |
2713 | cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_ALL_MULTICAST); |
2714 | } else if (mc_count) { |
2715 | struct netdev_hw_addr *ha; |
2716 | int i = 0; |
2717 | |
2718 | cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST); |
2719 | cmd->numaddr = cpu_to_le16(mc_count); |
2720 | netdev_hw_addr_list_for_each(ha, mc_list) { |
2721 | memcpy(cmd->addr[i], ha->addr, ETH_ALEN); |
2722 | } |
2723 | } |
2724 | |
2725 | return &cmd->header; |
2726 | } |
2727 | |
2728 | /* |
2729 | * CMD_GET_STAT. |
2730 | */ |
2731 | struct mwl8k_cmd_get_stat { |
2732 | struct mwl8k_cmd_pkt ; |
2733 | __le32 stats[64]; |
2734 | } __packed; |
2735 | |
2736 | #define MWL8K_STAT_ACK_FAILURE 9 |
2737 | #define MWL8K_STAT_RTS_FAILURE 12 |
2738 | #define MWL8K_STAT_FCS_ERROR 24 |
2739 | #define MWL8K_STAT_RTS_SUCCESS 11 |
2740 | |
2741 | static int mwl8k_cmd_get_stat(struct ieee80211_hw *hw, |
2742 | struct ieee80211_low_level_stats *stats) |
2743 | { |
2744 | struct mwl8k_cmd_get_stat *cmd; |
2745 | int rc; |
2746 | |
2747 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
2748 | if (cmd == NULL) |
2749 | return -ENOMEM; |
2750 | |
2751 | cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_STAT); |
2752 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
2753 | |
2754 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
2755 | if (!rc) { |
2756 | stats->dot11ACKFailureCount = |
2757 | le32_to_cpu(cmd->stats[MWL8K_STAT_ACK_FAILURE]); |
2758 | stats->dot11RTSFailureCount = |
2759 | le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_FAILURE]); |
2760 | stats->dot11FCSErrorCount = |
2761 | le32_to_cpu(cmd->stats[MWL8K_STAT_FCS_ERROR]); |
2762 | stats->dot11RTSSuccessCount = |
2763 | le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_SUCCESS]); |
2764 | } |
2765 | kfree(objp: cmd); |
2766 | |
2767 | return rc; |
2768 | } |
2769 | |
2770 | /* |
2771 | * CMD_RADIO_CONTROL. |
2772 | */ |
2773 | struct mwl8k_cmd_radio_control { |
2774 | struct mwl8k_cmd_pkt ; |
2775 | __le16 action; |
2776 | __le16 control; |
2777 | __le16 radio_on; |
2778 | } __packed; |
2779 | |
2780 | static int |
2781 | mwl8k_cmd_radio_control(struct ieee80211_hw *hw, bool enable, bool force) |
2782 | { |
2783 | struct mwl8k_priv *priv = hw->priv; |
2784 | struct mwl8k_cmd_radio_control *cmd; |
2785 | int rc; |
2786 | |
2787 | if (enable == priv->radio_on && !force) |
2788 | return 0; |
2789 | |
2790 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
2791 | if (cmd == NULL) |
2792 | return -ENOMEM; |
2793 | |
2794 | cmd->header.code = cpu_to_le16(MWL8K_CMD_RADIO_CONTROL); |
2795 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
2796 | cmd->action = cpu_to_le16(MWL8K_CMD_SET); |
2797 | cmd->control = cpu_to_le16(priv->radio_short_preamble ? 3 : 1); |
2798 | cmd->radio_on = cpu_to_le16(enable ? 0x0001 : 0x0000); |
2799 | |
2800 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
2801 | kfree(objp: cmd); |
2802 | |
2803 | if (!rc) |
2804 | priv->radio_on = enable; |
2805 | |
2806 | return rc; |
2807 | } |
2808 | |
2809 | static int mwl8k_cmd_radio_disable(struct ieee80211_hw *hw) |
2810 | { |
2811 | return mwl8k_cmd_radio_control(hw, enable: 0, force: 0); |
2812 | } |
2813 | |
2814 | static int mwl8k_cmd_radio_enable(struct ieee80211_hw *hw) |
2815 | { |
2816 | return mwl8k_cmd_radio_control(hw, enable: 1, force: 0); |
2817 | } |
2818 | |
2819 | static int |
2820 | mwl8k_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble) |
2821 | { |
2822 | struct mwl8k_priv *priv = hw->priv; |
2823 | |
2824 | priv->radio_short_preamble = short_preamble; |
2825 | |
2826 | return mwl8k_cmd_radio_control(hw, enable: 1, force: 1); |
2827 | } |
2828 | |
2829 | /* |
2830 | * CMD_RF_TX_POWER. |
2831 | */ |
2832 | #define MWL8K_RF_TX_POWER_LEVEL_TOTAL 8 |
2833 | |
2834 | struct mwl8k_cmd_rf_tx_power { |
2835 | struct mwl8k_cmd_pkt ; |
2836 | __le16 action; |
2837 | __le16 support_level; |
2838 | __le16 current_level; |
2839 | __le16 reserved; |
2840 | __le16 power_level_list[MWL8K_RF_TX_POWER_LEVEL_TOTAL]; |
2841 | } __packed; |
2842 | |
2843 | static int mwl8k_cmd_rf_tx_power(struct ieee80211_hw *hw, int dBm) |
2844 | { |
2845 | struct mwl8k_cmd_rf_tx_power *cmd; |
2846 | int rc; |
2847 | |
2848 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
2849 | if (cmd == NULL) |
2850 | return -ENOMEM; |
2851 | |
2852 | cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_TX_POWER); |
2853 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
2854 | cmd->action = cpu_to_le16(MWL8K_CMD_SET); |
2855 | cmd->support_level = cpu_to_le16(dBm); |
2856 | |
2857 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
2858 | kfree(objp: cmd); |
2859 | |
2860 | return rc; |
2861 | } |
2862 | |
2863 | /* |
2864 | * CMD_TX_POWER. |
2865 | */ |
2866 | #define MWL8K_TX_POWER_LEVEL_TOTAL 12 |
2867 | |
2868 | struct mwl8k_cmd_tx_power { |
2869 | struct mwl8k_cmd_pkt ; |
2870 | __le16 action; |
2871 | __le16 band; |
2872 | __le16 channel; |
2873 | __le16 bw; |
2874 | __le16 sub_ch; |
2875 | __le16 power_level_list[MWL8K_TX_POWER_LEVEL_TOTAL]; |
2876 | } __packed; |
2877 | |
2878 | static int mwl8k_cmd_tx_power(struct ieee80211_hw *hw, |
2879 | struct ieee80211_conf *conf, |
2880 | unsigned short pwr) |
2881 | { |
2882 | struct ieee80211_channel *channel = conf->chandef.chan; |
2883 | enum nl80211_channel_type channel_type = |
2884 | cfg80211_get_chandef_type(chandef: &conf->chandef); |
2885 | struct mwl8k_cmd_tx_power *cmd; |
2886 | int rc; |
2887 | int i; |
2888 | |
2889 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
2890 | if (cmd == NULL) |
2891 | return -ENOMEM; |
2892 | |
2893 | cmd->header.code = cpu_to_le16(MWL8K_CMD_TX_POWER); |
2894 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
2895 | cmd->action = cpu_to_le16(MWL8K_CMD_SET_LIST); |
2896 | |
2897 | if (channel->band == NL80211_BAND_2GHZ) |
2898 | cmd->band = cpu_to_le16(0x1); |
2899 | else if (channel->band == NL80211_BAND_5GHZ) |
2900 | cmd->band = cpu_to_le16(0x4); |
2901 | |
2902 | cmd->channel = cpu_to_le16(channel->hw_value); |
2903 | |
2904 | if (channel_type == NL80211_CHAN_NO_HT || |
2905 | channel_type == NL80211_CHAN_HT20) { |
2906 | cmd->bw = cpu_to_le16(0x2); |
2907 | } else { |
2908 | cmd->bw = cpu_to_le16(0x4); |
2909 | if (channel_type == NL80211_CHAN_HT40MINUS) |
2910 | cmd->sub_ch = cpu_to_le16(0x3); |
2911 | else if (channel_type == NL80211_CHAN_HT40PLUS) |
2912 | cmd->sub_ch = cpu_to_le16(0x1); |
2913 | } |
2914 | |
2915 | for (i = 0; i < MWL8K_TX_POWER_LEVEL_TOTAL; i++) |
2916 | cmd->power_level_list[i] = cpu_to_le16(pwr); |
2917 | |
2918 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
2919 | kfree(objp: cmd); |
2920 | |
2921 | return rc; |
2922 | } |
2923 | |
2924 | /* |
2925 | * CMD_RF_ANTENNA. |
2926 | */ |
2927 | struct mwl8k_cmd_rf_antenna { |
2928 | struct mwl8k_cmd_pkt ; |
2929 | __le16 antenna; |
2930 | __le16 mode; |
2931 | } __packed; |
2932 | |
2933 | #define MWL8K_RF_ANTENNA_RX 1 |
2934 | #define MWL8K_RF_ANTENNA_TX 2 |
2935 | |
2936 | static int |
2937 | mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask) |
2938 | { |
2939 | struct mwl8k_cmd_rf_antenna *cmd; |
2940 | int rc; |
2941 | |
2942 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
2943 | if (cmd == NULL) |
2944 | return -ENOMEM; |
2945 | |
2946 | cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_ANTENNA); |
2947 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
2948 | cmd->antenna = cpu_to_le16(antenna); |
2949 | cmd->mode = cpu_to_le16(mask); |
2950 | |
2951 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
2952 | kfree(objp: cmd); |
2953 | |
2954 | return rc; |
2955 | } |
2956 | |
2957 | /* |
2958 | * CMD_SET_BEACON. |
2959 | */ |
2960 | struct mwl8k_cmd_set_beacon { |
2961 | struct mwl8k_cmd_pkt ; |
2962 | __le16 beacon_len; |
2963 | __u8 beacon[]; |
2964 | }; |
2965 | |
2966 | static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw, |
2967 | struct ieee80211_vif *vif, u8 *beacon, int len) |
2968 | { |
2969 | struct mwl8k_cmd_set_beacon *cmd; |
2970 | int rc; |
2971 | |
2972 | cmd = kzalloc(size: sizeof(*cmd) + len, GFP_KERNEL); |
2973 | if (cmd == NULL) |
2974 | return -ENOMEM; |
2975 | |
2976 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_BEACON); |
2977 | cmd->header.length = cpu_to_le16(sizeof(*cmd) + len); |
2978 | cmd->beacon_len = cpu_to_le16(len); |
2979 | memcpy(cmd->beacon, beacon, len); |
2980 | |
2981 | rc = mwl8k_post_pervif_cmd(hw, vif, cmd: &cmd->header); |
2982 | kfree(objp: cmd); |
2983 | |
2984 | return rc; |
2985 | } |
2986 | |
2987 | /* |
2988 | * CMD_SET_PRE_SCAN. |
2989 | */ |
2990 | struct mwl8k_cmd_set_pre_scan { |
2991 | struct mwl8k_cmd_pkt ; |
2992 | } __packed; |
2993 | |
2994 | static int mwl8k_cmd_set_pre_scan(struct ieee80211_hw *hw) |
2995 | { |
2996 | struct mwl8k_cmd_set_pre_scan *cmd; |
2997 | int rc; |
2998 | |
2999 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3000 | if (cmd == NULL) |
3001 | return -ENOMEM; |
3002 | |
3003 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_PRE_SCAN); |
3004 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3005 | |
3006 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3007 | kfree(objp: cmd); |
3008 | |
3009 | return rc; |
3010 | } |
3011 | |
3012 | /* |
3013 | * CMD_BBP_REG_ACCESS. |
3014 | */ |
3015 | struct mwl8k_cmd_bbp_reg_access { |
3016 | struct mwl8k_cmd_pkt ; |
3017 | __le16 action; |
3018 | __le16 offset; |
3019 | u8 value; |
3020 | u8 rsrv[3]; |
3021 | } __packed; |
3022 | |
3023 | static int |
3024 | mwl8k_cmd_bbp_reg_access(struct ieee80211_hw *hw, |
3025 | u16 action, |
3026 | u16 offset, |
3027 | u8 *value) |
3028 | { |
3029 | struct mwl8k_cmd_bbp_reg_access *cmd; |
3030 | int rc; |
3031 | |
3032 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3033 | if (cmd == NULL) |
3034 | return -ENOMEM; |
3035 | |
3036 | cmd->header.code = cpu_to_le16(MWL8K_CMD_BBP_REG_ACCESS); |
3037 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3038 | cmd->action = cpu_to_le16(action); |
3039 | cmd->offset = cpu_to_le16(offset); |
3040 | |
3041 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3042 | |
3043 | if (!rc) |
3044 | *value = cmd->value; |
3045 | else |
3046 | *value = 0; |
3047 | |
3048 | kfree(objp: cmd); |
3049 | |
3050 | return rc; |
3051 | } |
3052 | |
3053 | /* |
3054 | * CMD_SET_POST_SCAN. |
3055 | */ |
3056 | struct mwl8k_cmd_set_post_scan { |
3057 | struct mwl8k_cmd_pkt ; |
3058 | __le32 isibss; |
3059 | __u8 bssid[ETH_ALEN]; |
3060 | } __packed; |
3061 | |
3062 | static int |
3063 | mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac) |
3064 | { |
3065 | struct mwl8k_cmd_set_post_scan *cmd; |
3066 | int rc; |
3067 | |
3068 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3069 | if (cmd == NULL) |
3070 | return -ENOMEM; |
3071 | |
3072 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_POST_SCAN); |
3073 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3074 | cmd->isibss = 0; |
3075 | memcpy(cmd->bssid, mac, ETH_ALEN); |
3076 | |
3077 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3078 | kfree(objp: cmd); |
3079 | |
3080 | return rc; |
3081 | } |
3082 | |
3083 | static int freq_to_idx(struct mwl8k_priv *priv, int freq) |
3084 | { |
3085 | struct ieee80211_supported_band *sband; |
3086 | int band, ch, idx = 0; |
3087 | |
3088 | for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { |
3089 | sband = priv->hw->wiphy->bands[band]; |
3090 | if (!sband) |
3091 | continue; |
3092 | |
3093 | for (ch = 0; ch < sband->n_channels; ch++, idx++) |
3094 | if (sband->channels[ch].center_freq == freq) |
3095 | goto exit; |
3096 | } |
3097 | |
3098 | exit: |
3099 | return idx; |
3100 | } |
3101 | |
3102 | static void mwl8k_update_survey(struct mwl8k_priv *priv, |
3103 | struct ieee80211_channel *channel) |
3104 | { |
3105 | u32 cca_cnt, rx_rdy; |
3106 | s8 nf = 0, idx; |
3107 | struct survey_info *survey; |
3108 | |
3109 | idx = freq_to_idx(priv, freq: priv->acs_chan->center_freq); |
3110 | if (idx >= MWL8K_NUM_CHANS) { |
3111 | wiphy_err(priv->hw->wiphy, "Failed to update survey\n" ); |
3112 | return; |
3113 | } |
3114 | |
3115 | survey = &priv->survey[idx]; |
3116 | |
3117 | cca_cnt = ioread32(priv->regs + NOK_CCA_CNT_REG); |
3118 | cca_cnt /= 1000; /* uSecs to mSecs */ |
3119 | survey->time_busy = (u64) cca_cnt; |
3120 | |
3121 | rx_rdy = ioread32(priv->regs + BBU_RXRDY_CNT_REG); |
3122 | rx_rdy /= 1000; /* uSecs to mSecs */ |
3123 | survey->time_rx = (u64) rx_rdy; |
3124 | |
3125 | priv->channel_time = jiffies - priv->channel_time; |
3126 | survey->time = jiffies_to_msecs(j: priv->channel_time); |
3127 | |
3128 | survey->channel = channel; |
3129 | |
3130 | mwl8k_cmd_bbp_reg_access(hw: priv->hw, action: 0, BBU_AVG_NOISE_VAL, value: &nf); |
3131 | |
3132 | /* Make sure sign is negative else ACS at hostapd fails */ |
3133 | survey->noise = nf * -1; |
3134 | |
3135 | survey->filled = SURVEY_INFO_NOISE_DBM | |
3136 | SURVEY_INFO_TIME | |
3137 | SURVEY_INFO_TIME_BUSY | |
3138 | SURVEY_INFO_TIME_RX; |
3139 | } |
3140 | |
3141 | /* |
3142 | * CMD_SET_RF_CHANNEL. |
3143 | */ |
3144 | struct mwl8k_cmd_set_rf_channel { |
3145 | struct mwl8k_cmd_pkt ; |
3146 | __le16 action; |
3147 | __u8 current_channel; |
3148 | __le32 channel_flags; |
3149 | } __packed; |
3150 | |
3151 | static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, |
3152 | struct ieee80211_conf *conf) |
3153 | { |
3154 | struct ieee80211_channel *channel = conf->chandef.chan; |
3155 | enum nl80211_channel_type channel_type = |
3156 | cfg80211_get_chandef_type(chandef: &conf->chandef); |
3157 | struct mwl8k_cmd_set_rf_channel *cmd; |
3158 | struct mwl8k_priv *priv = hw->priv; |
3159 | int rc; |
3160 | |
3161 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3162 | if (cmd == NULL) |
3163 | return -ENOMEM; |
3164 | |
3165 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RF_CHANNEL); |
3166 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3167 | cmd->action = cpu_to_le16(MWL8K_CMD_SET); |
3168 | cmd->current_channel = channel->hw_value; |
3169 | |
3170 | if (channel->band == NL80211_BAND_2GHZ) |
3171 | cmd->channel_flags |= cpu_to_le32(0x00000001); |
3172 | else if (channel->band == NL80211_BAND_5GHZ) |
3173 | cmd->channel_flags |= cpu_to_le32(0x00000004); |
3174 | |
3175 | if (!priv->sw_scan_start) { |
3176 | if (channel_type == NL80211_CHAN_NO_HT || |
3177 | channel_type == NL80211_CHAN_HT20) |
3178 | cmd->channel_flags |= cpu_to_le32(0x00000080); |
3179 | else if (channel_type == NL80211_CHAN_HT40MINUS) |
3180 | cmd->channel_flags |= cpu_to_le32(0x000001900); |
3181 | else if (channel_type == NL80211_CHAN_HT40PLUS) |
3182 | cmd->channel_flags |= cpu_to_le32(0x000000900); |
3183 | } else { |
3184 | cmd->channel_flags |= cpu_to_le32(0x00000080); |
3185 | } |
3186 | |
3187 | if (priv->sw_scan_start) { |
3188 | /* Store current channel stats |
3189 | * before switching to newer one. |
3190 | * This will be processed only for AP fw. |
3191 | */ |
3192 | if (priv->channel_time != 0) |
3193 | mwl8k_update_survey(priv, channel: priv->acs_chan); |
3194 | |
3195 | priv->channel_time = jiffies; |
3196 | priv->acs_chan = channel; |
3197 | } |
3198 | |
3199 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3200 | kfree(objp: cmd); |
3201 | |
3202 | return rc; |
3203 | } |
3204 | |
3205 | /* |
3206 | * CMD_SET_AID. |
3207 | */ |
3208 | #define MWL8K_FRAME_PROT_DISABLED 0x00 |
3209 | #define MWL8K_FRAME_PROT_11G 0x07 |
3210 | #define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY 0x02 |
3211 | #define MWL8K_FRAME_PROT_11N_HT_ALL 0x06 |
3212 | |
3213 | struct mwl8k_cmd_update_set_aid { |
3214 | struct mwl8k_cmd_pkt ; |
3215 | __le16 aid; |
3216 | |
3217 | /* AP's MAC address (BSSID) */ |
3218 | __u8 bssid[ETH_ALEN]; |
3219 | __le16 protection_mode; |
3220 | __u8 supp_rates[14]; |
3221 | } __packed; |
3222 | |
3223 | static void legacy_rate_mask_to_array(u8 *rates, u32 mask) |
3224 | { |
3225 | int i; |
3226 | int j; |
3227 | |
3228 | /* |
3229 | * Clear nonstandard rate 4. |
3230 | */ |
3231 | mask &= 0x1fef; |
3232 | |
3233 | for (i = 0, j = 0; i < 13; i++) { |
3234 | if (mask & (1 << i)) |
3235 | rates[j++] = mwl8k_rates_24[i].hw_value; |
3236 | } |
3237 | } |
3238 | |
3239 | static int |
3240 | mwl8k_cmd_set_aid(struct ieee80211_hw *hw, |
3241 | struct ieee80211_vif *vif, u32 legacy_rate_mask) |
3242 | { |
3243 | struct mwl8k_cmd_update_set_aid *cmd; |
3244 | u16 prot_mode; |
3245 | int rc; |
3246 | |
3247 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3248 | if (cmd == NULL) |
3249 | return -ENOMEM; |
3250 | |
3251 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID); |
3252 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3253 | cmd->aid = cpu_to_le16(vif->cfg.aid); |
3254 | memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); |
3255 | |
3256 | if (vif->bss_conf.use_cts_prot) { |
3257 | prot_mode = MWL8K_FRAME_PROT_11G; |
3258 | } else { |
3259 | switch (vif->bss_conf.ht_operation_mode & |
3260 | IEEE80211_HT_OP_MODE_PROTECTION) { |
3261 | case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: |
3262 | prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY; |
3263 | break; |
3264 | case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: |
3265 | prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL; |
3266 | break; |
3267 | default: |
3268 | prot_mode = MWL8K_FRAME_PROT_DISABLED; |
3269 | break; |
3270 | } |
3271 | } |
3272 | cmd->protection_mode = cpu_to_le16(prot_mode); |
3273 | |
3274 | legacy_rate_mask_to_array(rates: cmd->supp_rates, mask: legacy_rate_mask); |
3275 | |
3276 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3277 | kfree(objp: cmd); |
3278 | |
3279 | return rc; |
3280 | } |
3281 | |
3282 | /* |
3283 | * CMD_SET_RATE. |
3284 | */ |
3285 | struct mwl8k_cmd_set_rate { |
3286 | struct mwl8k_cmd_pkt ; |
3287 | __u8 legacy_rates[14]; |
3288 | |
3289 | /* Bitmap for supported MCS codes. */ |
3290 | __u8 mcs_set[16]; |
3291 | __u8 reserved[16]; |
3292 | } __packed; |
3293 | |
3294 | static int |
3295 | mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif, |
3296 | u32 legacy_rate_mask, u8 *mcs_rates) |
3297 | { |
3298 | struct mwl8k_cmd_set_rate *cmd; |
3299 | int rc; |
3300 | |
3301 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3302 | if (cmd == NULL) |
3303 | return -ENOMEM; |
3304 | |
3305 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE); |
3306 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3307 | legacy_rate_mask_to_array(rates: cmd->legacy_rates, mask: legacy_rate_mask); |
3308 | memcpy(cmd->mcs_set, mcs_rates, 16); |
3309 | |
3310 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3311 | kfree(objp: cmd); |
3312 | |
3313 | return rc; |
3314 | } |
3315 | |
3316 | /* |
3317 | * CMD_FINALIZE_JOIN. |
3318 | */ |
3319 | #define MWL8K_FJ_BEACON_MAXLEN 128 |
3320 | |
3321 | struct mwl8k_cmd_finalize_join { |
3322 | struct mwl8k_cmd_pkt ; |
3323 | __le32 sleep_interval; /* Number of beacon periods to sleep */ |
3324 | __u8 beacon_data[MWL8K_FJ_BEACON_MAXLEN]; |
3325 | } __packed; |
3326 | |
3327 | static int mwl8k_cmd_finalize_join(struct ieee80211_hw *hw, void *frame, |
3328 | int framelen, int dtim) |
3329 | { |
3330 | struct mwl8k_cmd_finalize_join *cmd; |
3331 | struct ieee80211_mgmt *payload = frame; |
3332 | int payload_len; |
3333 | int rc; |
3334 | |
3335 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3336 | if (cmd == NULL) |
3337 | return -ENOMEM; |
3338 | |
3339 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN); |
3340 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3341 | cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1); |
3342 | |
3343 | payload_len = framelen - ieee80211_hdrlen(fc: payload->frame_control); |
3344 | if (payload_len < 0) |
3345 | payload_len = 0; |
3346 | else if (payload_len > MWL8K_FJ_BEACON_MAXLEN) |
3347 | payload_len = MWL8K_FJ_BEACON_MAXLEN; |
3348 | |
3349 | memcpy(cmd->beacon_data, &payload->u.beacon, payload_len); |
3350 | |
3351 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3352 | kfree(objp: cmd); |
3353 | |
3354 | return rc; |
3355 | } |
3356 | |
3357 | /* |
3358 | * CMD_SET_RTS_THRESHOLD. |
3359 | */ |
3360 | struct mwl8k_cmd_set_rts_threshold { |
3361 | struct mwl8k_cmd_pkt ; |
3362 | __le16 action; |
3363 | __le16 threshold; |
3364 | } __packed; |
3365 | |
3366 | static int |
3367 | mwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, int rts_thresh) |
3368 | { |
3369 | struct mwl8k_cmd_set_rts_threshold *cmd; |
3370 | int rc; |
3371 | |
3372 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3373 | if (cmd == NULL) |
3374 | return -ENOMEM; |
3375 | |
3376 | cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD); |
3377 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3378 | cmd->action = cpu_to_le16(MWL8K_CMD_SET); |
3379 | cmd->threshold = cpu_to_le16(rts_thresh); |
3380 | |
3381 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3382 | kfree(objp: cmd); |
3383 | |
3384 | return rc; |
3385 | } |
3386 | |
3387 | /* |
3388 | * CMD_SET_SLOT. |
3389 | */ |
3390 | struct mwl8k_cmd_set_slot { |
3391 | struct mwl8k_cmd_pkt ; |
3392 | __le16 action; |
3393 | __u8 short_slot; |
3394 | } __packed; |
3395 | |
3396 | static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time) |
3397 | { |
3398 | struct mwl8k_cmd_set_slot *cmd; |
3399 | int rc; |
3400 | |
3401 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3402 | if (cmd == NULL) |
3403 | return -ENOMEM; |
3404 | |
3405 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT); |
3406 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3407 | cmd->action = cpu_to_le16(MWL8K_CMD_SET); |
3408 | cmd->short_slot = short_slot_time; |
3409 | |
3410 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3411 | kfree(objp: cmd); |
3412 | |
3413 | return rc; |
3414 | } |
3415 | |
3416 | /* |
3417 | * CMD_SET_EDCA_PARAMS. |
3418 | */ |
3419 | struct mwl8k_cmd_set_edca_params { |
3420 | struct mwl8k_cmd_pkt ; |
3421 | |
3422 | /* See MWL8K_SET_EDCA_XXX below */ |
3423 | __le16 action; |
3424 | |
3425 | /* TX opportunity in units of 32 us */ |
3426 | __le16 txop; |
3427 | |
3428 | union { |
3429 | struct { |
3430 | /* Log exponent of max contention period: 0...15 */ |
3431 | __le32 log_cw_max; |
3432 | |
3433 | /* Log exponent of min contention period: 0...15 */ |
3434 | __le32 log_cw_min; |
3435 | |
3436 | /* Adaptive interframe spacing in units of 32us */ |
3437 | __u8 aifs; |
3438 | |
3439 | /* TX queue to configure */ |
3440 | __u8 txq; |
3441 | } ap; |
3442 | struct { |
3443 | /* Log exponent of max contention period: 0...15 */ |
3444 | __u8 log_cw_max; |
3445 | |
3446 | /* Log exponent of min contention period: 0...15 */ |
3447 | __u8 log_cw_min; |
3448 | |
3449 | /* Adaptive interframe spacing in units of 32us */ |
3450 | __u8 aifs; |
3451 | |
3452 | /* TX queue to configure */ |
3453 | __u8 txq; |
3454 | } sta; |
3455 | }; |
3456 | } __packed; |
3457 | |
3458 | #define MWL8K_SET_EDCA_CW 0x01 |
3459 | #define MWL8K_SET_EDCA_TXOP 0x02 |
3460 | #define MWL8K_SET_EDCA_AIFS 0x04 |
3461 | |
3462 | #define MWL8K_SET_EDCA_ALL (MWL8K_SET_EDCA_CW | \ |
3463 | MWL8K_SET_EDCA_TXOP | \ |
3464 | MWL8K_SET_EDCA_AIFS) |
3465 | |
3466 | static int |
3467 | mwl8k_cmd_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, |
3468 | __u16 cw_min, __u16 cw_max, |
3469 | __u8 aifs, __u16 txop) |
3470 | { |
3471 | struct mwl8k_priv *priv = hw->priv; |
3472 | struct mwl8k_cmd_set_edca_params *cmd; |
3473 | int rc; |
3474 | |
3475 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3476 | if (cmd == NULL) |
3477 | return -ENOMEM; |
3478 | |
3479 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS); |
3480 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3481 | cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL); |
3482 | cmd->txop = cpu_to_le16(txop); |
3483 | if (priv->ap_fw) { |
3484 | cmd->ap.log_cw_max = cpu_to_le32(ilog2(cw_max + 1)); |
3485 | cmd->ap.log_cw_min = cpu_to_le32(ilog2(cw_min + 1)); |
3486 | cmd->ap.aifs = aifs; |
3487 | cmd->ap.txq = qnum; |
3488 | } else { |
3489 | cmd->sta.log_cw_max = (u8)ilog2(cw_max + 1); |
3490 | cmd->sta.log_cw_min = (u8)ilog2(cw_min + 1); |
3491 | cmd->sta.aifs = aifs; |
3492 | cmd->sta.txq = qnum; |
3493 | } |
3494 | |
3495 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3496 | kfree(objp: cmd); |
3497 | |
3498 | return rc; |
3499 | } |
3500 | |
3501 | /* |
3502 | * CMD_SET_WMM_MODE. |
3503 | */ |
3504 | struct mwl8k_cmd_set_wmm_mode { |
3505 | struct mwl8k_cmd_pkt ; |
3506 | __le16 action; |
3507 | } __packed; |
3508 | |
3509 | static int mwl8k_cmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable) |
3510 | { |
3511 | struct mwl8k_priv *priv = hw->priv; |
3512 | struct mwl8k_cmd_set_wmm_mode *cmd; |
3513 | int rc; |
3514 | |
3515 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3516 | if (cmd == NULL) |
3517 | return -ENOMEM; |
3518 | |
3519 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_WMM_MODE); |
3520 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3521 | cmd->action = cpu_to_le16(!!enable); |
3522 | |
3523 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3524 | kfree(objp: cmd); |
3525 | |
3526 | if (!rc) |
3527 | priv->wmm_enabled = enable; |
3528 | |
3529 | return rc; |
3530 | } |
3531 | |
3532 | /* |
3533 | * CMD_MIMO_CONFIG. |
3534 | */ |
3535 | struct mwl8k_cmd_mimo_config { |
3536 | struct mwl8k_cmd_pkt ; |
3537 | __le32 action; |
3538 | __u8 rx_antenna_map; |
3539 | __u8 tx_antenna_map; |
3540 | } __packed; |
3541 | |
3542 | static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx) |
3543 | { |
3544 | struct mwl8k_cmd_mimo_config *cmd; |
3545 | int rc; |
3546 | |
3547 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3548 | if (cmd == NULL) |
3549 | return -ENOMEM; |
3550 | |
3551 | cmd->header.code = cpu_to_le16(MWL8K_CMD_MIMO_CONFIG); |
3552 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3553 | cmd->action = cpu_to_le32((u32)MWL8K_CMD_SET); |
3554 | cmd->rx_antenna_map = rx; |
3555 | cmd->tx_antenna_map = tx; |
3556 | |
3557 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3558 | kfree(objp: cmd); |
3559 | |
3560 | return rc; |
3561 | } |
3562 | |
3563 | /* |
3564 | * CMD_USE_FIXED_RATE (STA version). |
3565 | */ |
3566 | struct mwl8k_cmd_use_fixed_rate_sta { |
3567 | struct mwl8k_cmd_pkt ; |
3568 | __le32 action; |
3569 | __le32 allow_rate_drop; |
3570 | __le32 num_rates; |
3571 | struct { |
3572 | __le32 is_ht_rate; |
3573 | __le32 enable_retry; |
3574 | __le32 rate; |
3575 | __le32 retry_count; |
3576 | } rate_entry[8]; |
3577 | __le32 rate_type; |
3578 | __le32 reserved1; |
3579 | __le32 reserved2; |
3580 | } __packed; |
3581 | |
3582 | #define MWL8K_USE_AUTO_RATE 0x0002 |
3583 | #define MWL8K_UCAST_RATE 0 |
3584 | |
3585 | static int mwl8k_cmd_use_fixed_rate_sta(struct ieee80211_hw *hw) |
3586 | { |
3587 | struct mwl8k_cmd_use_fixed_rate_sta *cmd; |
3588 | int rc; |
3589 | |
3590 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3591 | if (cmd == NULL) |
3592 | return -ENOMEM; |
3593 | |
3594 | cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); |
3595 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3596 | cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); |
3597 | cmd->rate_type = cpu_to_le32(MWL8K_UCAST_RATE); |
3598 | |
3599 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3600 | kfree(objp: cmd); |
3601 | |
3602 | return rc; |
3603 | } |
3604 | |
3605 | /* |
3606 | * CMD_USE_FIXED_RATE (AP version). |
3607 | */ |
3608 | struct mwl8k_cmd_use_fixed_rate_ap { |
3609 | struct mwl8k_cmd_pkt ; |
3610 | __le32 action; |
3611 | __le32 allow_rate_drop; |
3612 | __le32 num_rates; |
3613 | struct mwl8k_rate_entry_ap { |
3614 | __le32 is_ht_rate; |
3615 | __le32 enable_retry; |
3616 | __le32 rate; |
3617 | __le32 retry_count; |
3618 | } rate_entry[4]; |
3619 | u8 multicast_rate; |
3620 | u8 multicast_rate_type; |
3621 | u8 management_rate; |
3622 | } __packed; |
3623 | |
3624 | static int |
3625 | mwl8k_cmd_use_fixed_rate_ap(struct ieee80211_hw *hw, int mcast, int mgmt) |
3626 | { |
3627 | struct mwl8k_cmd_use_fixed_rate_ap *cmd; |
3628 | int rc; |
3629 | |
3630 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3631 | if (cmd == NULL) |
3632 | return -ENOMEM; |
3633 | |
3634 | cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); |
3635 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3636 | cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); |
3637 | cmd->multicast_rate = mcast; |
3638 | cmd->management_rate = mgmt; |
3639 | |
3640 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3641 | kfree(objp: cmd); |
3642 | |
3643 | return rc; |
3644 | } |
3645 | |
3646 | /* |
3647 | * CMD_ENABLE_SNIFFER. |
3648 | */ |
3649 | struct mwl8k_cmd_enable_sniffer { |
3650 | struct mwl8k_cmd_pkt ; |
3651 | __le32 action; |
3652 | } __packed; |
3653 | |
3654 | static int mwl8k_cmd_enable_sniffer(struct ieee80211_hw *hw, bool enable) |
3655 | { |
3656 | struct mwl8k_cmd_enable_sniffer *cmd; |
3657 | int rc; |
3658 | |
3659 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3660 | if (cmd == NULL) |
3661 | return -ENOMEM; |
3662 | |
3663 | cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER); |
3664 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3665 | cmd->action = cpu_to_le32(!!enable); |
3666 | |
3667 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3668 | kfree(objp: cmd); |
3669 | |
3670 | return rc; |
3671 | } |
3672 | |
3673 | struct mwl8k_cmd_update_mac_addr { |
3674 | struct mwl8k_cmd_pkt ; |
3675 | union { |
3676 | struct { |
3677 | __le16 mac_type; |
3678 | __u8 mac_addr[ETH_ALEN]; |
3679 | } mbss; |
3680 | __u8 mac_addr[ETH_ALEN]; |
3681 | }; |
3682 | } __packed; |
3683 | |
3684 | #define MWL8K_MAC_TYPE_PRIMARY_CLIENT 0 |
3685 | #define MWL8K_MAC_TYPE_SECONDARY_CLIENT 1 |
3686 | #define MWL8K_MAC_TYPE_PRIMARY_AP 2 |
3687 | #define MWL8K_MAC_TYPE_SECONDARY_AP 3 |
3688 | |
3689 | static int mwl8k_cmd_update_mac_addr(struct ieee80211_hw *hw, |
3690 | struct ieee80211_vif *vif, u8 *mac, bool set) |
3691 | { |
3692 | struct mwl8k_priv *priv = hw->priv; |
3693 | struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); |
3694 | struct mwl8k_cmd_update_mac_addr *cmd; |
3695 | int mac_type; |
3696 | int rc; |
3697 | |
3698 | mac_type = MWL8K_MAC_TYPE_PRIMARY_AP; |
3699 | if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) { |
3700 | if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported)) |
3701 | if (priv->ap_fw) |
3702 | mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT; |
3703 | else |
3704 | mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT; |
3705 | else |
3706 | mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT; |
3707 | } else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) { |
3708 | if (mwl8k_vif->macid + 1 == ffs(priv->ap_macids_supported)) |
3709 | mac_type = MWL8K_MAC_TYPE_PRIMARY_AP; |
3710 | else |
3711 | mac_type = MWL8K_MAC_TYPE_SECONDARY_AP; |
3712 | } |
3713 | |
3714 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3715 | if (cmd == NULL) |
3716 | return -ENOMEM; |
3717 | |
3718 | if (set) |
3719 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR); |
3720 | else |
3721 | cmd->header.code = cpu_to_le16(MWL8K_CMD_DEL_MAC_ADDR); |
3722 | |
3723 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3724 | if (priv->ap_fw) { |
3725 | cmd->mbss.mac_type = cpu_to_le16(mac_type); |
3726 | memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN); |
3727 | } else { |
3728 | memcpy(cmd->mac_addr, mac, ETH_ALEN); |
3729 | } |
3730 | |
3731 | rc = mwl8k_post_pervif_cmd(hw, vif, cmd: &cmd->header); |
3732 | kfree(objp: cmd); |
3733 | |
3734 | return rc; |
3735 | } |
3736 | |
3737 | /* |
3738 | * MWL8K_CMD_SET_MAC_ADDR. |
3739 | */ |
3740 | static inline int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, |
3741 | struct ieee80211_vif *vif, u8 *mac) |
3742 | { |
3743 | return mwl8k_cmd_update_mac_addr(hw, vif, mac, set: true); |
3744 | } |
3745 | |
3746 | /* |
3747 | * MWL8K_CMD_DEL_MAC_ADDR. |
3748 | */ |
3749 | static inline int mwl8k_cmd_del_mac_addr(struct ieee80211_hw *hw, |
3750 | struct ieee80211_vif *vif, u8 *mac) |
3751 | { |
3752 | return mwl8k_cmd_update_mac_addr(hw, vif, mac, set: false); |
3753 | } |
3754 | |
3755 | /* |
3756 | * CMD_SET_RATEADAPT_MODE. |
3757 | */ |
3758 | struct mwl8k_cmd_set_rate_adapt_mode { |
3759 | struct mwl8k_cmd_pkt ; |
3760 | __le16 action; |
3761 | __le16 mode; |
3762 | } __packed; |
3763 | |
3764 | static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode) |
3765 | { |
3766 | struct mwl8k_cmd_set_rate_adapt_mode *cmd; |
3767 | int rc; |
3768 | |
3769 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3770 | if (cmd == NULL) |
3771 | return -ENOMEM; |
3772 | |
3773 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATEADAPT_MODE); |
3774 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3775 | cmd->action = cpu_to_le16(MWL8K_CMD_SET); |
3776 | cmd->mode = cpu_to_le16(mode); |
3777 | |
3778 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3779 | kfree(objp: cmd); |
3780 | |
3781 | return rc; |
3782 | } |
3783 | |
3784 | /* |
3785 | * CMD_GET_WATCHDOG_BITMAP. |
3786 | */ |
3787 | struct mwl8k_cmd_get_watchdog_bitmap { |
3788 | struct mwl8k_cmd_pkt ; |
3789 | u8 bitmap; |
3790 | } __packed; |
3791 | |
3792 | static int mwl8k_cmd_get_watchdog_bitmap(struct ieee80211_hw *hw, u8 *bitmap) |
3793 | { |
3794 | struct mwl8k_cmd_get_watchdog_bitmap *cmd; |
3795 | int rc; |
3796 | |
3797 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3798 | if (cmd == NULL) |
3799 | return -ENOMEM; |
3800 | |
3801 | cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_WATCHDOG_BITMAP); |
3802 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3803 | |
3804 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
3805 | if (!rc) |
3806 | *bitmap = cmd->bitmap; |
3807 | |
3808 | kfree(objp: cmd); |
3809 | |
3810 | return rc; |
3811 | } |
3812 | |
3813 | #define MWL8K_WMM_QUEUE_NUMBER 3 |
3814 | |
3815 | static void mwl8k_destroy_ba(struct ieee80211_hw *hw, |
3816 | u8 idx); |
3817 | |
3818 | static void mwl8k_watchdog_ba_events(struct work_struct *work) |
3819 | { |
3820 | int rc; |
3821 | u8 bitmap = 0, stream_index; |
3822 | struct mwl8k_ampdu_stream *streams; |
3823 | struct mwl8k_priv *priv = |
3824 | container_of(work, struct mwl8k_priv, watchdog_ba_handle); |
3825 | struct ieee80211_hw *hw = priv->hw; |
3826 | int i; |
3827 | u32 status = 0; |
3828 | |
3829 | mwl8k_fw_lock(hw); |
3830 | |
3831 | rc = mwl8k_cmd_get_watchdog_bitmap(hw: priv->hw, bitmap: &bitmap); |
3832 | if (rc) |
3833 | goto done; |
3834 | |
3835 | spin_lock(lock: &priv->stream_lock); |
3836 | |
3837 | /* the bitmap is the hw queue number. Map it to the ampdu queue. */ |
3838 | for (i = 0; i < TOTAL_HW_TX_QUEUES; i++) { |
3839 | if (bitmap & (1 << i)) { |
3840 | stream_index = (i + MWL8K_WMM_QUEUE_NUMBER) % |
3841 | TOTAL_HW_TX_QUEUES; |
3842 | streams = &priv->ampdu[stream_index]; |
3843 | if (streams->state == AMPDU_STREAM_ACTIVE) { |
3844 | ieee80211_stop_tx_ba_session(sta: streams->sta, |
3845 | tid: streams->tid); |
3846 | spin_unlock(lock: &priv->stream_lock); |
3847 | mwl8k_destroy_ba(hw, idx: stream_index); |
3848 | spin_lock(lock: &priv->stream_lock); |
3849 | } |
3850 | } |
3851 | } |
3852 | |
3853 | spin_unlock(lock: &priv->stream_lock); |
3854 | done: |
3855 | atomic_dec(v: &priv->watchdog_event_pending); |
3856 | status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); |
3857 | iowrite32((status | MWL8K_A2H_INT_BA_WATCHDOG), |
3858 | priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); |
3859 | mwl8k_fw_unlock(hw); |
3860 | return; |
3861 | } |
3862 | |
3863 | |
3864 | /* |
3865 | * CMD_BSS_START. |
3866 | */ |
3867 | struct mwl8k_cmd_bss_start { |
3868 | struct mwl8k_cmd_pkt ; |
3869 | __le32 enable; |
3870 | } __packed; |
3871 | |
3872 | static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, |
3873 | struct ieee80211_vif *vif, int enable) |
3874 | { |
3875 | struct mwl8k_cmd_bss_start *cmd; |
3876 | struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); |
3877 | struct mwl8k_priv *priv = hw->priv; |
3878 | int rc; |
3879 | |
3880 | if (enable && (priv->running_bsses & (1 << mwl8k_vif->macid))) |
3881 | return 0; |
3882 | |
3883 | if (!enable && !(priv->running_bsses & (1 << mwl8k_vif->macid))) |
3884 | return 0; |
3885 | |
3886 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3887 | if (cmd == NULL) |
3888 | return -ENOMEM; |
3889 | |
3890 | cmd->header.code = cpu_to_le16(MWL8K_CMD_BSS_START); |
3891 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3892 | cmd->enable = cpu_to_le32(enable); |
3893 | |
3894 | rc = mwl8k_post_pervif_cmd(hw, vif, cmd: &cmd->header); |
3895 | kfree(objp: cmd); |
3896 | |
3897 | if (!rc) { |
3898 | if (enable) |
3899 | priv->running_bsses |= (1 << mwl8k_vif->macid); |
3900 | else |
3901 | priv->running_bsses &= ~(1 << mwl8k_vif->macid); |
3902 | } |
3903 | return rc; |
3904 | } |
3905 | |
3906 | static void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable, u32 bitmap) |
3907 | { |
3908 | struct mwl8k_priv *priv = hw->priv; |
3909 | struct mwl8k_vif *mwl8k_vif, *tmp_vif; |
3910 | struct ieee80211_vif *vif; |
3911 | |
3912 | list_for_each_entry_safe(mwl8k_vif, tmp_vif, &priv->vif_list, list) { |
3913 | vif = mwl8k_vif->vif; |
3914 | |
3915 | if (!(bitmap & (1 << mwl8k_vif->macid))) |
3916 | continue; |
3917 | |
3918 | if (vif->type == NL80211_IFTYPE_AP) |
3919 | mwl8k_cmd_bss_start(hw, vif, enable); |
3920 | } |
3921 | } |
3922 | /* |
3923 | * CMD_BASTREAM. |
3924 | */ |
3925 | |
3926 | /* |
3927 | * UPSTREAM is tx direction |
3928 | */ |
3929 | #define BASTREAM_FLAG_DIRECTION_UPSTREAM 0x00 |
3930 | #define BASTREAM_FLAG_IMMEDIATE_TYPE 0x01 |
3931 | |
3932 | enum ba_stream_action_type { |
3933 | MWL8K_BA_CREATE, |
3934 | MWL8K_BA_UPDATE, |
3935 | MWL8K_BA_DESTROY, |
3936 | MWL8K_BA_FLUSH, |
3937 | MWL8K_BA_CHECK, |
3938 | }; |
3939 | |
3940 | |
3941 | struct mwl8k_create_ba_stream { |
3942 | __le32 flags; |
3943 | __le32 idle_thrs; |
3944 | __le32 bar_thrs; |
3945 | __le32 window_size; |
3946 | u8 peer_mac_addr[6]; |
3947 | u8 dialog_token; |
3948 | u8 tid; |
3949 | u8 queue_id; |
3950 | u8 param_info; |
3951 | __le32 ba_context; |
3952 | u8 reset_seq_no_flag; |
3953 | __le16 curr_seq_no; |
3954 | u8 sta_src_mac_addr[6]; |
3955 | } __packed; |
3956 | |
3957 | struct mwl8k_destroy_ba_stream { |
3958 | __le32 flags; |
3959 | __le32 ba_context; |
3960 | } __packed; |
3961 | |
3962 | struct mwl8k_cmd_bastream { |
3963 | struct mwl8k_cmd_pkt ; |
3964 | __le32 action; |
3965 | union { |
3966 | struct mwl8k_create_ba_stream create_params; |
3967 | struct mwl8k_destroy_ba_stream destroy_params; |
3968 | }; |
3969 | } __packed; |
3970 | |
3971 | static int |
3972 | mwl8k_check_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream, |
3973 | struct ieee80211_vif *vif) |
3974 | { |
3975 | struct mwl8k_cmd_bastream *cmd; |
3976 | int rc; |
3977 | |
3978 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
3979 | if (cmd == NULL) |
3980 | return -ENOMEM; |
3981 | |
3982 | cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM); |
3983 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
3984 | |
3985 | cmd->action = cpu_to_le32(MWL8K_BA_CHECK); |
3986 | |
3987 | cmd->create_params.queue_id = stream->idx; |
3988 | memcpy(&cmd->create_params.peer_mac_addr[0], stream->sta->addr, |
3989 | ETH_ALEN); |
3990 | cmd->create_params.tid = stream->tid; |
3991 | |
3992 | cmd->create_params.flags = |
3993 | cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE) | |
3994 | cpu_to_le32(BASTREAM_FLAG_DIRECTION_UPSTREAM); |
3995 | |
3996 | rc = mwl8k_post_pervif_cmd(hw, vif, cmd: &cmd->header); |
3997 | |
3998 | kfree(objp: cmd); |
3999 | |
4000 | return rc; |
4001 | } |
4002 | |
4003 | static int |
4004 | mwl8k_create_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream, |
4005 | u8 buf_size, struct ieee80211_vif *vif) |
4006 | { |
4007 | struct mwl8k_cmd_bastream *cmd; |
4008 | int rc; |
4009 | |
4010 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
4011 | if (cmd == NULL) |
4012 | return -ENOMEM; |
4013 | |
4014 | |
4015 | cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM); |
4016 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
4017 | |
4018 | cmd->action = cpu_to_le32(MWL8K_BA_CREATE); |
4019 | |
4020 | cmd->create_params.bar_thrs = cpu_to_le32((u32)buf_size); |
4021 | cmd->create_params.window_size = cpu_to_le32((u32)buf_size); |
4022 | cmd->create_params.queue_id = stream->idx; |
4023 | |
4024 | memcpy(cmd->create_params.peer_mac_addr, stream->sta->addr, ETH_ALEN); |
4025 | cmd->create_params.tid = stream->tid; |
4026 | cmd->create_params.curr_seq_no = cpu_to_le16(0); |
4027 | cmd->create_params.reset_seq_no_flag = 1; |
4028 | |
4029 | cmd->create_params.param_info = |
4030 | (stream->sta->deflink.ht_cap.ampdu_factor & |
4031 | IEEE80211_HT_AMPDU_PARM_FACTOR) | |
4032 | ((stream->sta->deflink.ht_cap.ampdu_density << 2) & |
4033 | IEEE80211_HT_AMPDU_PARM_DENSITY); |
4034 | |
4035 | cmd->create_params.flags = |
4036 | cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE | |
4037 | BASTREAM_FLAG_DIRECTION_UPSTREAM); |
4038 | |
4039 | rc = mwl8k_post_pervif_cmd(hw, vif, cmd: &cmd->header); |
4040 | |
4041 | wiphy_debug(hw->wiphy, "Created a BA stream for %pM : tid %d\n" , |
4042 | stream->sta->addr, stream->tid); |
4043 | kfree(objp: cmd); |
4044 | |
4045 | return rc; |
4046 | } |
4047 | |
4048 | static void mwl8k_destroy_ba(struct ieee80211_hw *hw, |
4049 | u8 idx) |
4050 | { |
4051 | struct mwl8k_cmd_bastream *cmd; |
4052 | |
4053 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
4054 | if (cmd == NULL) |
4055 | return; |
4056 | |
4057 | cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM); |
4058 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
4059 | cmd->action = cpu_to_le32(MWL8K_BA_DESTROY); |
4060 | |
4061 | cmd->destroy_params.ba_context = cpu_to_le32(idx); |
4062 | mwl8k_post_cmd(hw, cmd: &cmd->header); |
4063 | |
4064 | wiphy_debug(hw->wiphy, "Deleted BA stream index %d\n" , idx); |
4065 | |
4066 | kfree(objp: cmd); |
4067 | } |
4068 | |
4069 | /* |
4070 | * CMD_SET_NEW_STN. |
4071 | */ |
4072 | struct mwl8k_cmd_set_new_stn { |
4073 | struct mwl8k_cmd_pkt ; |
4074 | __le16 aid; |
4075 | __u8 mac_addr[6]; |
4076 | __le16 stn_id; |
4077 | __le16 action; |
4078 | __le16 rsvd; |
4079 | __le32 legacy_rates; |
4080 | __u8 ht_rates[4]; |
4081 | __le16 cap_info; |
4082 | __le16 ht_capabilities_info; |
4083 | __u8 mac_ht_param_info; |
4084 | __u8 rev; |
4085 | __u8 control_channel; |
4086 | __u8 add_channel; |
4087 | __le16 op_mode; |
4088 | __le16 stbc; |
4089 | __u8 add_qos_info; |
4090 | __u8 is_qos_sta; |
4091 | __le32 fw_sta_ptr; |
4092 | } __packed; |
4093 | |
4094 | #define MWL8K_STA_ACTION_ADD 0 |
4095 | #define MWL8K_STA_ACTION_REMOVE 2 |
4096 | |
4097 | static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw, |
4098 | struct ieee80211_vif *vif, |
4099 | struct ieee80211_sta *sta) |
4100 | { |
4101 | struct mwl8k_cmd_set_new_stn *cmd; |
4102 | u32 rates; |
4103 | int rc; |
4104 | |
4105 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
4106 | if (cmd == NULL) |
4107 | return -ENOMEM; |
4108 | |
4109 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); |
4110 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
4111 | cmd->aid = cpu_to_le16(sta->aid); |
4112 | memcpy(cmd->mac_addr, sta->addr, ETH_ALEN); |
4113 | cmd->stn_id = cpu_to_le16(sta->aid); |
4114 | cmd->action = cpu_to_le16(MWL8K_STA_ACTION_ADD); |
4115 | if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ) |
4116 | rates = sta->deflink.supp_rates[NL80211_BAND_2GHZ]; |
4117 | else |
4118 | rates = sta->deflink.supp_rates[NL80211_BAND_5GHZ] << 5; |
4119 | cmd->legacy_rates = cpu_to_le32(rates); |
4120 | if (sta->deflink.ht_cap.ht_supported) { |
4121 | cmd->ht_rates[0] = sta->deflink.ht_cap.mcs.rx_mask[0]; |
4122 | cmd->ht_rates[1] = sta->deflink.ht_cap.mcs.rx_mask[1]; |
4123 | cmd->ht_rates[2] = sta->deflink.ht_cap.mcs.rx_mask[2]; |
4124 | cmd->ht_rates[3] = sta->deflink.ht_cap.mcs.rx_mask[3]; |
4125 | cmd->ht_capabilities_info = cpu_to_le16(sta->deflink.ht_cap.cap); |
4126 | cmd->mac_ht_param_info = (sta->deflink.ht_cap.ampdu_factor & 3) | |
4127 | ((sta->deflink.ht_cap.ampdu_density & 7) << 2); |
4128 | cmd->is_qos_sta = 1; |
4129 | } |
4130 | |
4131 | rc = mwl8k_post_pervif_cmd(hw, vif, cmd: &cmd->header); |
4132 | kfree(objp: cmd); |
4133 | |
4134 | return rc; |
4135 | } |
4136 | |
4137 | static int mwl8k_cmd_set_new_stn_add_self(struct ieee80211_hw *hw, |
4138 | struct ieee80211_vif *vif) |
4139 | { |
4140 | struct mwl8k_cmd_set_new_stn *cmd; |
4141 | int rc; |
4142 | |
4143 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
4144 | if (cmd == NULL) |
4145 | return -ENOMEM; |
4146 | |
4147 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); |
4148 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
4149 | memcpy(cmd->mac_addr, vif->addr, ETH_ALEN); |
4150 | |
4151 | rc = mwl8k_post_pervif_cmd(hw, vif, cmd: &cmd->header); |
4152 | kfree(objp: cmd); |
4153 | |
4154 | return rc; |
4155 | } |
4156 | |
4157 | static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw, |
4158 | struct ieee80211_vif *vif, u8 *addr) |
4159 | { |
4160 | struct mwl8k_cmd_set_new_stn *cmd; |
4161 | struct mwl8k_priv *priv = hw->priv; |
4162 | int rc, i; |
4163 | u8 idx; |
4164 | |
4165 | spin_lock(lock: &priv->stream_lock); |
4166 | /* Destroy any active ampdu streams for this sta */ |
4167 | for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) { |
4168 | struct mwl8k_ampdu_stream *s; |
4169 | s = &priv->ampdu[i]; |
4170 | if (s->state != AMPDU_NO_STREAM) { |
4171 | if (memcmp(p: s->sta->addr, q: addr, ETH_ALEN) == 0) { |
4172 | if (s->state == AMPDU_STREAM_ACTIVE) { |
4173 | idx = s->idx; |
4174 | spin_unlock(lock: &priv->stream_lock); |
4175 | mwl8k_destroy_ba(hw, idx); |
4176 | spin_lock(lock: &priv->stream_lock); |
4177 | } else if (s->state == AMPDU_STREAM_NEW) { |
4178 | mwl8k_remove_stream(hw, stream: s); |
4179 | } |
4180 | } |
4181 | } |
4182 | } |
4183 | |
4184 | spin_unlock(lock: &priv->stream_lock); |
4185 | |
4186 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
4187 | if (cmd == NULL) |
4188 | return -ENOMEM; |
4189 | |
4190 | cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); |
4191 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
4192 | memcpy(cmd->mac_addr, addr, ETH_ALEN); |
4193 | cmd->action = cpu_to_le16(MWL8K_STA_ACTION_REMOVE); |
4194 | |
4195 | rc = mwl8k_post_pervif_cmd(hw, vif, cmd: &cmd->header); |
4196 | kfree(objp: cmd); |
4197 | |
4198 | return rc; |
4199 | } |
4200 | |
4201 | /* |
4202 | * CMD_UPDATE_ENCRYPTION. |
4203 | */ |
4204 | |
4205 | #define MAX_ENCR_KEY_LENGTH 16 |
4206 | #define MIC_KEY_LENGTH 8 |
4207 | |
4208 | struct mwl8k_cmd_update_encryption { |
4209 | struct mwl8k_cmd_pkt ; |
4210 | |
4211 | __le32 action; |
4212 | __le32 reserved; |
4213 | __u8 mac_addr[6]; |
4214 | __u8 encr_type; |
4215 | |
4216 | } __packed; |
4217 | |
4218 | struct mwl8k_cmd_set_key { |
4219 | struct mwl8k_cmd_pkt ; |
4220 | |
4221 | __le32 action; |
4222 | __le32 reserved; |
4223 | __le16 length; |
4224 | __le16 key_type_id; |
4225 | __le32 key_info; |
4226 | __le32 key_id; |
4227 | __le16 key_len; |
4228 | struct { |
4229 | __u8 key_material[MAX_ENCR_KEY_LENGTH]; |
4230 | __u8 tkip_tx_mic_key[MIC_KEY_LENGTH]; |
4231 | __u8 tkip_rx_mic_key[MIC_KEY_LENGTH]; |
4232 | } tkip; |
4233 | __le16 tkip_rsc_low; |
4234 | __le32 tkip_rsc_high; |
4235 | __le16 tkip_tsc_low; |
4236 | __le32 tkip_tsc_high; |
4237 | __u8 mac_addr[6]; |
4238 | } __packed; |
4239 | |
4240 | enum { |
4241 | MWL8K_ENCR_ENABLE, |
4242 | MWL8K_ENCR_SET_KEY, |
4243 | MWL8K_ENCR_REMOVE_KEY, |
4244 | MWL8K_ENCR_SET_GROUP_KEY, |
4245 | }; |
4246 | |
4247 | #define MWL8K_UPDATE_ENCRYPTION_TYPE_WEP 0 |
4248 | #define MWL8K_UPDATE_ENCRYPTION_TYPE_DISABLE 1 |
4249 | #define MWL8K_UPDATE_ENCRYPTION_TYPE_TKIP 4 |
4250 | #define MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED 7 |
4251 | #define MWL8K_UPDATE_ENCRYPTION_TYPE_AES 8 |
4252 | |
4253 | enum { |
4254 | MWL8K_ALG_WEP, |
4255 | MWL8K_ALG_TKIP, |
4256 | MWL8K_ALG_CCMP, |
4257 | }; |
4258 | |
4259 | #define MWL8K_KEY_FLAG_TXGROUPKEY 0x00000004 |
4260 | #define MWL8K_KEY_FLAG_PAIRWISE 0x00000008 |
4261 | #define MWL8K_KEY_FLAG_TSC_VALID 0x00000040 |
4262 | #define MWL8K_KEY_FLAG_WEP_TXKEY 0x01000000 |
4263 | #define MWL8K_KEY_FLAG_MICKEY_VALID 0x02000000 |
4264 | |
4265 | static int mwl8k_cmd_update_encryption_enable(struct ieee80211_hw *hw, |
4266 | struct ieee80211_vif *vif, |
4267 | u8 *addr, |
4268 | u8 encr_type) |
4269 | { |
4270 | struct mwl8k_cmd_update_encryption *cmd; |
4271 | int rc; |
4272 | |
4273 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
4274 | if (cmd == NULL) |
4275 | return -ENOMEM; |
4276 | |
4277 | cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION); |
4278 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
4279 | cmd->action = cpu_to_le32(MWL8K_ENCR_ENABLE); |
4280 | memcpy(cmd->mac_addr, addr, ETH_ALEN); |
4281 | cmd->encr_type = encr_type; |
4282 | |
4283 | rc = mwl8k_post_pervif_cmd(hw, vif, cmd: &cmd->header); |
4284 | kfree(objp: cmd); |
4285 | |
4286 | return rc; |
4287 | } |
4288 | |
4289 | static int mwl8k_encryption_set_cmd_info(struct mwl8k_cmd_set_key *cmd, |
4290 | u8 *addr, |
4291 | struct ieee80211_key_conf *key) |
4292 | { |
4293 | cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION); |
4294 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
4295 | cmd->length = cpu_to_le16(sizeof(*cmd) - |
4296 | offsetof(struct mwl8k_cmd_set_key, length)); |
4297 | cmd->key_id = cpu_to_le32(key->keyidx); |
4298 | cmd->key_len = cpu_to_le16(key->keylen); |
4299 | memcpy(cmd->mac_addr, addr, ETH_ALEN); |
4300 | |
4301 | switch (key->cipher) { |
4302 | case WLAN_CIPHER_SUITE_WEP40: |
4303 | case WLAN_CIPHER_SUITE_WEP104: |
4304 | cmd->key_type_id = cpu_to_le16(MWL8K_ALG_WEP); |
4305 | if (key->keyidx == 0) |
4306 | cmd->key_info = cpu_to_le32(MWL8K_KEY_FLAG_WEP_TXKEY); |
4307 | |
4308 | break; |
4309 | case WLAN_CIPHER_SUITE_TKIP: |
4310 | cmd->key_type_id = cpu_to_le16(MWL8K_ALG_TKIP); |
4311 | cmd->key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) |
4312 | ? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE) |
4313 | : cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY); |
4314 | cmd->key_info |= cpu_to_le32(MWL8K_KEY_FLAG_MICKEY_VALID |
4315 | | MWL8K_KEY_FLAG_TSC_VALID); |
4316 | break; |
4317 | case WLAN_CIPHER_SUITE_CCMP: |
4318 | cmd->key_type_id = cpu_to_le16(MWL8K_ALG_CCMP); |
4319 | cmd->key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) |
4320 | ? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE) |
4321 | : cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY); |
4322 | break; |
4323 | default: |
4324 | return -ENOTSUPP; |
4325 | } |
4326 | |
4327 | return 0; |
4328 | } |
4329 | |
4330 | static int mwl8k_cmd_encryption_set_key(struct ieee80211_hw *hw, |
4331 | struct ieee80211_vif *vif, |
4332 | u8 *addr, |
4333 | struct ieee80211_key_conf *key) |
4334 | { |
4335 | struct mwl8k_cmd_set_key *cmd; |
4336 | int rc; |
4337 | int keymlen; |
4338 | u32 action; |
4339 | u8 idx; |
4340 | struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); |
4341 | |
4342 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
4343 | if (cmd == NULL) |
4344 | return -ENOMEM; |
4345 | |
4346 | rc = mwl8k_encryption_set_cmd_info(cmd, addr, key); |
4347 | if (rc < 0) |
4348 | goto done; |
4349 | |
4350 | idx = key->keyidx; |
4351 | |
4352 | if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) |
4353 | action = MWL8K_ENCR_SET_KEY; |
4354 | else |
4355 | action = MWL8K_ENCR_SET_GROUP_KEY; |
4356 | |
4357 | switch (key->cipher) { |
4358 | case WLAN_CIPHER_SUITE_WEP40: |
4359 | case WLAN_CIPHER_SUITE_WEP104: |
4360 | if (!mwl8k_vif->wep_key_conf[idx].enabled) { |
4361 | memcpy(mwl8k_vif->wep_key_conf[idx].key, key, |
4362 | sizeof(*key) + key->keylen); |
4363 | mwl8k_vif->wep_key_conf[idx].enabled = 1; |
4364 | } |
4365 | |
4366 | keymlen = key->keylen; |
4367 | action = MWL8K_ENCR_SET_KEY; |
4368 | break; |
4369 | case WLAN_CIPHER_SUITE_TKIP: |
4370 | keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH; |
4371 | break; |
4372 | case WLAN_CIPHER_SUITE_CCMP: |
4373 | keymlen = key->keylen; |
4374 | break; |
4375 | default: |
4376 | rc = -ENOTSUPP; |
4377 | goto done; |
4378 | } |
4379 | |
4380 | memcpy(&cmd->tkip, key->key, keymlen); |
4381 | cmd->action = cpu_to_le32(action); |
4382 | |
4383 | rc = mwl8k_post_pervif_cmd(hw, vif, cmd: &cmd->header); |
4384 | done: |
4385 | kfree(objp: cmd); |
4386 | |
4387 | return rc; |
4388 | } |
4389 | |
4390 | static int mwl8k_cmd_encryption_remove_key(struct ieee80211_hw *hw, |
4391 | struct ieee80211_vif *vif, |
4392 | u8 *addr, |
4393 | struct ieee80211_key_conf *key) |
4394 | { |
4395 | struct mwl8k_cmd_set_key *cmd; |
4396 | int rc; |
4397 | struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); |
4398 | |
4399 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
4400 | if (cmd == NULL) |
4401 | return -ENOMEM; |
4402 | |
4403 | rc = mwl8k_encryption_set_cmd_info(cmd, addr, key); |
4404 | if (rc < 0) |
4405 | goto done; |
4406 | |
4407 | if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || |
4408 | key->cipher == WLAN_CIPHER_SUITE_WEP104) |
4409 | mwl8k_vif->wep_key_conf[key->keyidx].enabled = 0; |
4410 | |
4411 | cmd->action = cpu_to_le32(MWL8K_ENCR_REMOVE_KEY); |
4412 | |
4413 | rc = mwl8k_post_pervif_cmd(hw, vif, cmd: &cmd->header); |
4414 | done: |
4415 | kfree(objp: cmd); |
4416 | |
4417 | return rc; |
4418 | } |
4419 | |
4420 | static int mwl8k_set_key(struct ieee80211_hw *hw, |
4421 | enum set_key_cmd cmd_param, |
4422 | struct ieee80211_vif *vif, |
4423 | struct ieee80211_sta *sta, |
4424 | struct ieee80211_key_conf *key) |
4425 | { |
4426 | int rc = 0; |
4427 | u8 encr_type; |
4428 | u8 *addr; |
4429 | struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); |
4430 | struct mwl8k_priv *priv = hw->priv; |
4431 | |
4432 | if (vif->type == NL80211_IFTYPE_STATION && !priv->ap_fw) |
4433 | return -EOPNOTSUPP; |
4434 | |
4435 | if (sta == NULL) |
4436 | addr = vif->addr; |
4437 | else |
4438 | addr = sta->addr; |
4439 | |
4440 | if (cmd_param == SET_KEY) { |
4441 | rc = mwl8k_cmd_encryption_set_key(hw, vif, addr, key); |
4442 | if (rc) |
4443 | goto out; |
4444 | |
4445 | if ((key->cipher == WLAN_CIPHER_SUITE_WEP40) |
4446 | || (key->cipher == WLAN_CIPHER_SUITE_WEP104)) |
4447 | encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_WEP; |
4448 | else |
4449 | encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED; |
4450 | |
4451 | rc = mwl8k_cmd_update_encryption_enable(hw, vif, addr, |
4452 | encr_type); |
4453 | if (rc) |
4454 | goto out; |
4455 | |
4456 | mwl8k_vif->is_hw_crypto_enabled = true; |
4457 | |
4458 | } else { |
4459 | rc = mwl8k_cmd_encryption_remove_key(hw, vif, addr, key); |
4460 | |
4461 | if (rc) |
4462 | goto out; |
4463 | } |
4464 | out: |
4465 | return rc; |
4466 | } |
4467 | |
4468 | /* |
4469 | * CMD_UPDATE_STADB. |
4470 | */ |
4471 | struct ewc_ht_info { |
4472 | __le16 control1; |
4473 | __le16 control2; |
4474 | __le16 control3; |
4475 | } __packed; |
4476 | |
4477 | struct peer_capability_info { |
4478 | /* Peer type - AP vs. STA. */ |
4479 | __u8 peer_type; |
4480 | |
4481 | /* Basic 802.11 capabilities from assoc resp. */ |
4482 | __le16 basic_caps; |
4483 | |
4484 | /* Set if peer supports 802.11n high throughput (HT). */ |
4485 | __u8 ht_support; |
4486 | |
4487 | /* Valid if HT is supported. */ |
4488 | __le16 ht_caps; |
4489 | __u8 extended_ht_caps; |
4490 | struct ewc_ht_info ewc_info; |
4491 | |
4492 | /* Legacy rate table. Intersection of our rates and peer rates. */ |
4493 | __u8 legacy_rates[12]; |
4494 | |
4495 | /* HT rate table. Intersection of our rates and peer rates. */ |
4496 | __u8 ht_rates[16]; |
4497 | __u8 pad[16]; |
4498 | |
4499 | /* If set, interoperability mode, no proprietary extensions. */ |
4500 | __u8 interop; |
4501 | __u8 pad2; |
4502 | __u8 station_id; |
4503 | __le16 amsdu_enabled; |
4504 | } __packed; |
4505 | |
4506 | struct mwl8k_cmd_update_stadb { |
4507 | struct mwl8k_cmd_pkt ; |
4508 | |
4509 | /* See STADB_ACTION_TYPE */ |
4510 | __le32 action; |
4511 | |
4512 | /* Peer MAC address */ |
4513 | __u8 peer_addr[ETH_ALEN]; |
4514 | |
4515 | __le32 reserved; |
4516 | |
4517 | /* Peer info - valid during add/update. */ |
4518 | struct peer_capability_info peer_info; |
4519 | } __packed; |
4520 | |
4521 | #define MWL8K_STA_DB_MODIFY_ENTRY 1 |
4522 | #define MWL8K_STA_DB_DEL_ENTRY 2 |
4523 | |
4524 | /* Peer Entry flags - used to define the type of the peer node */ |
4525 | #define MWL8K_PEER_TYPE_ACCESSPOINT 2 |
4526 | |
4527 | static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, |
4528 | struct ieee80211_vif *vif, |
4529 | struct ieee80211_sta *sta) |
4530 | { |
4531 | struct mwl8k_cmd_update_stadb *cmd; |
4532 | struct peer_capability_info *p; |
4533 | u32 rates; |
4534 | int rc; |
4535 | |
4536 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
4537 | if (cmd == NULL) |
4538 | return -ENOMEM; |
4539 | |
4540 | cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); |
4541 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
4542 | cmd->action = cpu_to_le32(MWL8K_STA_DB_MODIFY_ENTRY); |
4543 | memcpy(cmd->peer_addr, sta->addr, ETH_ALEN); |
4544 | |
4545 | p = &cmd->peer_info; |
4546 | p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT; |
4547 | p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability); |
4548 | p->ht_support = sta->deflink.ht_cap.ht_supported; |
4549 | p->ht_caps = cpu_to_le16(sta->deflink.ht_cap.cap); |
4550 | p->extended_ht_caps = (sta->deflink.ht_cap.ampdu_factor & 3) | |
4551 | ((sta->deflink.ht_cap.ampdu_density & 7) << 2); |
4552 | if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ) |
4553 | rates = sta->deflink.supp_rates[NL80211_BAND_2GHZ]; |
4554 | else |
4555 | rates = sta->deflink.supp_rates[NL80211_BAND_5GHZ] << 5; |
4556 | legacy_rate_mask_to_array(rates: p->legacy_rates, mask: rates); |
4557 | memcpy(p->ht_rates, &sta->deflink.ht_cap.mcs, 16); |
4558 | p->interop = 1; |
4559 | p->amsdu_enabled = 0; |
4560 | |
4561 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
4562 | if (!rc) |
4563 | rc = p->station_id; |
4564 | kfree(objp: cmd); |
4565 | |
4566 | return rc; |
4567 | } |
4568 | |
4569 | static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw, |
4570 | struct ieee80211_vif *vif, u8 *addr) |
4571 | { |
4572 | struct mwl8k_cmd_update_stadb *cmd; |
4573 | int rc; |
4574 | |
4575 | cmd = kzalloc(size: sizeof(*cmd), GFP_KERNEL); |
4576 | if (cmd == NULL) |
4577 | return -ENOMEM; |
4578 | |
4579 | cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); |
4580 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); |
4581 | cmd->action = cpu_to_le32(MWL8K_STA_DB_DEL_ENTRY); |
4582 | memcpy(cmd->peer_addr, addr, ETH_ALEN); |
4583 | |
4584 | rc = mwl8k_post_cmd(hw, cmd: &cmd->header); |
4585 | kfree(objp: cmd); |
4586 | |
4587 | return rc; |
4588 | } |
4589 | |
4590 | |
4591 | /* |
4592 | * Interrupt handling. |
4593 | */ |
4594 | static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) |
4595 | { |
4596 | struct ieee80211_hw *hw = dev_id; |
4597 | struct mwl8k_priv *priv = hw->priv; |
4598 | u32 status; |
4599 | |
4600 | status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); |
4601 | if (!status) |
4602 | return IRQ_NONE; |
4603 | |
4604 | if (status & MWL8K_A2H_INT_TX_DONE) { |
4605 | status &= ~MWL8K_A2H_INT_TX_DONE; |
4606 | tasklet_schedule(t: &priv->poll_tx_task); |
4607 | } |
4608 | |
4609 | if (status & MWL8K_A2H_INT_RX_READY) { |
4610 | status &= ~MWL8K_A2H_INT_RX_READY; |
4611 | tasklet_schedule(t: &priv->poll_rx_task); |
4612 | } |
4613 | |
4614 | if (status & MWL8K_A2H_INT_BA_WATCHDOG) { |
4615 | iowrite32(~MWL8K_A2H_INT_BA_WATCHDOG, |
4616 | priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); |
4617 | |
4618 | atomic_inc(v: &priv->watchdog_event_pending); |
4619 | status &= ~MWL8K_A2H_INT_BA_WATCHDOG; |
4620 | ieee80211_queue_work(hw, work: &priv->watchdog_ba_handle); |
4621 | } |
4622 | |
4623 | if (status) |
4624 | iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); |
4625 | |
4626 | if (status & MWL8K_A2H_INT_OPC_DONE) { |
4627 | if (priv->hostcmd_wait != NULL) |
4628 | complete(priv->hostcmd_wait); |
4629 | } |
4630 | |
4631 | if (status & MWL8K_A2H_INT_QUEUE_EMPTY) { |
4632 | if (!mutex_is_locked(lock: &priv->fw_mutex) && |
4633 | priv->radio_on && priv->pending_tx_pkts) |
4634 | mwl8k_tx_start(priv); |
4635 | } |
4636 | |
4637 | return IRQ_HANDLED; |
4638 | } |
4639 | |
4640 | static void mwl8k_tx_poll(struct tasklet_struct *t) |
4641 | { |
4642 | struct mwl8k_priv *priv = from_tasklet(priv, t, poll_tx_task); |
4643 | struct ieee80211_hw *hw = pci_get_drvdata(pdev: priv->pdev); |
4644 | int limit; |
4645 | int i; |
4646 | |
4647 | limit = 32; |
4648 | |
4649 | spin_lock(lock: &priv->tx_lock); |
4650 | |
4651 | for (i = 0; i < mwl8k_tx_queues(priv); i++) |
4652 | limit -= mwl8k_txq_reclaim(hw, index: i, limit, force: 0); |
4653 | |
4654 | if (!priv->pending_tx_pkts && priv->tx_wait != NULL) { |
4655 | complete(priv->tx_wait); |
4656 | priv->tx_wait = NULL; |
4657 | } |
4658 | |
4659 | spin_unlock(lock: &priv->tx_lock); |
4660 | |
4661 | if (limit) { |
4662 | writel(val: ~MWL8K_A2H_INT_TX_DONE, |
4663 | addr: priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); |
4664 | } else { |
4665 | tasklet_schedule(t: &priv->poll_tx_task); |
4666 | } |
4667 | } |
4668 | |
4669 | static void mwl8k_rx_poll(struct tasklet_struct *t) |
4670 | { |
4671 | struct mwl8k_priv *priv = from_tasklet(priv, t, poll_rx_task); |
4672 | struct ieee80211_hw *hw = pci_get_drvdata(pdev: priv->pdev); |
4673 | int limit; |
4674 | |
4675 | limit = 32; |
4676 | limit -= rxq_process(hw, index: 0, limit); |
4677 | limit -= rxq_refill(hw, index: 0, limit); |
4678 | |
4679 | if (limit) { |
4680 | writel(val: ~MWL8K_A2H_INT_RX_READY, |
4681 | addr: priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); |
4682 | } else { |
4683 | tasklet_schedule(t: &priv->poll_rx_task); |
4684 | } |
4685 | } |
4686 | |
4687 | |
4688 | /* |
4689 | * Core driver operations. |
4690 | */ |
4691 | static void mwl8k_tx(struct ieee80211_hw *hw, |
4692 | struct ieee80211_tx_control *control, |
4693 | struct sk_buff *skb) |
4694 | { |
4695 | struct mwl8k_priv *priv = hw->priv; |
4696 | int index = skb_get_queue_mapping(skb); |
4697 | |
4698 | if (!priv->radio_on) { |
4699 | wiphy_debug(hw->wiphy, |
4700 | "dropped TX frame since radio disabled\n" ); |
4701 | dev_kfree_skb(skb); |
4702 | return; |
4703 | } |
4704 | |
4705 | mwl8k_txq_xmit(hw, index, sta: control->sta, skb); |
4706 | } |
4707 | |
4708 | static int mwl8k_start(struct ieee80211_hw *hw) |
4709 | { |
4710 | struct mwl8k_priv *priv = hw->priv; |
4711 | int rc; |
4712 | |
4713 | rc = request_irq(irq: priv->pdev->irq, handler: mwl8k_interrupt, |
4714 | IRQF_SHARED, MWL8K_NAME, dev: hw); |
4715 | if (rc) { |
4716 | priv->irq = -1; |
4717 | wiphy_err(hw->wiphy, "failed to register IRQ handler\n" ); |
4718 | return -EIO; |
4719 | } |
4720 | priv->irq = priv->pdev->irq; |
4721 | |
4722 | /* Enable TX reclaim and RX tasklets. */ |
4723 | tasklet_enable(t: &priv->poll_tx_task); |
4724 | tasklet_enable(t: &priv->poll_rx_task); |
4725 | |
4726 | /* Enable interrupts */ |
4727 | iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); |
4728 | iowrite32(MWL8K_A2H_EVENTS, |
4729 | priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); |
4730 | |
4731 | rc = mwl8k_fw_lock(hw); |
4732 | if (!rc) { |
4733 | rc = mwl8k_cmd_radio_enable(hw); |
4734 | |
4735 | if (!priv->ap_fw) { |
4736 | if (!rc) |
4737 | rc = mwl8k_cmd_enable_sniffer(hw, enable: 0); |
4738 | |
4739 | if (!rc) |
4740 | rc = mwl8k_cmd_set_pre_scan(hw); |
4741 | |
4742 | if (!rc) |
4743 | rc = mwl8k_cmd_set_post_scan(hw, |
4744 | mac: "\x00\x00\x00\x00\x00\x00" ); |
4745 | } |
4746 | |
4747 | if (!rc) |
4748 | rc = mwl8k_cmd_set_rateadapt_mode(hw, mode: 0); |
4749 | |
4750 | if (!rc) |
4751 | rc = mwl8k_cmd_set_wmm_mode(hw, enable: 0); |
4752 | |
4753 | mwl8k_fw_unlock(hw); |
4754 | } |
4755 | |
4756 | if (rc) { |
4757 | iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); |
4758 | free_irq(priv->pdev->irq, hw); |
4759 | priv->irq = -1; |
4760 | tasklet_disable(t: &priv->poll_tx_task); |
4761 | tasklet_disable(t: &priv->poll_rx_task); |
4762 | } else { |
4763 | ieee80211_wake_queues(hw); |
4764 | } |
4765 | |
4766 | return rc; |
4767 | } |
4768 | |
4769 | static void mwl8k_stop(struct ieee80211_hw *hw) |
4770 | { |
4771 | struct mwl8k_priv *priv = hw->priv; |
4772 | int i; |
4773 | |
4774 | if (!priv->hw_restart_in_progress) |
4775 | mwl8k_cmd_radio_disable(hw); |
4776 | |
4777 | ieee80211_stop_queues(hw); |
4778 | |
4779 | /* Disable interrupts */ |
4780 | iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); |
4781 | if (priv->irq != -1) { |
4782 | free_irq(priv->pdev->irq, hw); |
4783 | priv->irq = -1; |
4784 | } |
4785 | |
4786 | /* Stop finalize join worker */ |
4787 | cancel_work_sync(work: &priv->finalize_join_worker); |
4788 | cancel_work_sync(work: &priv->watchdog_ba_handle); |
4789 | if (priv->beacon_skb != NULL) |
4790 | dev_kfree_skb(priv->beacon_skb); |
4791 | |
4792 | /* Stop TX reclaim and RX tasklets. */ |
4793 | tasklet_disable(t: &priv->poll_tx_task); |
4794 | tasklet_disable(t: &priv->poll_rx_task); |
4795 | |
4796 | /* Return all skbs to mac80211 */ |
4797 | for (i = 0; i < mwl8k_tx_queues(priv); i++) |
4798 | mwl8k_txq_reclaim(hw, index: i, INT_MAX, force: 1); |
4799 | } |
4800 | |
4801 | static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image); |
4802 | |
4803 | static int mwl8k_add_interface(struct ieee80211_hw *hw, |
4804 | struct ieee80211_vif *vif) |
4805 | { |
4806 | struct mwl8k_priv *priv = hw->priv; |
4807 | struct mwl8k_vif *mwl8k_vif; |
4808 | u32 macids_supported; |
4809 | int macid, rc; |
4810 | struct mwl8k_device_info *di; |
4811 | |
4812 | /* |
4813 | * Reject interface creation if sniffer mode is active, as |
4814 | * STA operation is mutually exclusive with hardware sniffer |
4815 | * mode. (Sniffer mode is only used on STA firmware.) |
4816 | */ |
4817 | if (priv->sniffer_enabled) { |
4818 | wiphy_info(hw->wiphy, |
4819 | "unable to create STA interface because sniffer mode is enabled\n" ); |
4820 | return -EINVAL; |
4821 | } |
4822 | |
4823 | di = priv->device_info; |
4824 | switch (vif->type) { |
4825 | case NL80211_IFTYPE_AP: |
4826 | if (!priv->ap_fw && di->fw_image_ap) { |
4827 | /* we must load the ap fw to meet this request */ |
4828 | if (!list_empty(head: &priv->vif_list)) |
4829 | return -EBUSY; |
4830 | rc = mwl8k_reload_firmware(hw, fw_image: di->fw_image_ap); |
4831 | if (rc) |
4832 | return rc; |
4833 | } |
4834 | macids_supported = priv->ap_macids_supported; |
4835 | break; |
4836 | case NL80211_IFTYPE_STATION: |
4837 | if (priv->ap_fw && di->fw_image_sta) { |
4838 | if (!list_empty(head: &priv->vif_list)) { |
4839 | wiphy_warn(hw->wiphy, "AP interface is running.\n" |
4840 | "Adding STA interface for WDS" ); |
4841 | } else { |
4842 | /* we must load the sta fw to |
4843 | * meet this request. |
4844 | */ |
4845 | rc = mwl8k_reload_firmware(hw, |
4846 | fw_image: di->fw_image_sta); |
4847 | if (rc) |
4848 | return rc; |
4849 | } |
4850 | } |
4851 | macids_supported = priv->sta_macids_supported; |
4852 | break; |
4853 | default: |
4854 | return -EINVAL; |
4855 | } |
4856 | |
4857 | macid = ffs(macids_supported & ~priv->macids_used); |
4858 | if (!macid--) |
4859 | return -EBUSY; |
4860 | |
4861 | /* Setup driver private area. */ |
4862 | mwl8k_vif = MWL8K_VIF(vif); |
4863 | memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); |
4864 | mwl8k_vif->vif = vif; |
4865 | mwl8k_vif->macid = macid; |
4866 | mwl8k_vif->seqno = 0; |
4867 | memcpy(mwl8k_vif->bssid, vif->addr, ETH_ALEN); |
4868 | mwl8k_vif->is_hw_crypto_enabled = false; |
4869 | |
4870 | /* Set the mac address. */ |
4871 | mwl8k_cmd_set_mac_addr(hw, vif, mac: vif->addr); |
4872 | |
4873 | if (vif->type == NL80211_IFTYPE_AP) |
4874 | mwl8k_cmd_set_new_stn_add_self(hw, vif); |
4875 | |
4876 | priv->macids_used |= 1 << mwl8k_vif->macid; |
4877 | list_add_tail(new: &mwl8k_vif->list, head: &priv->vif_list); |
4878 | |
4879 | return 0; |
4880 | } |
4881 | |
4882 | static void mwl8k_remove_vif(struct mwl8k_priv *priv, struct mwl8k_vif *vif) |
4883 | { |
4884 | /* Has ieee80211_restart_hw re-added the removed interfaces? */ |
4885 | if (!priv->macids_used) |
4886 | return; |
4887 | |
4888 | priv->macids_used &= ~(1 << vif->macid); |
4889 | list_del(entry: &vif->list); |
4890 | } |
4891 | |
4892 | static void mwl8k_remove_interface(struct ieee80211_hw *hw, |
4893 | struct ieee80211_vif *vif) |
4894 | { |
4895 | struct mwl8k_priv *priv = hw->priv; |
4896 | struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); |
4897 | |
4898 | if (vif->type == NL80211_IFTYPE_AP) |
4899 | mwl8k_cmd_set_new_stn_del(hw, vif, addr: vif->addr); |
4900 | |
4901 | mwl8k_cmd_del_mac_addr(hw, vif, mac: vif->addr); |
4902 | |
4903 | mwl8k_remove_vif(priv, vif: mwl8k_vif); |
4904 | } |
4905 | |
4906 | static void mwl8k_hw_restart_work(struct work_struct *work) |
4907 | { |
4908 | struct mwl8k_priv *priv = |
4909 | container_of(work, struct mwl8k_priv, fw_reload); |
4910 | struct ieee80211_hw *hw = priv->hw; |
4911 | struct mwl8k_device_info *di; |
4912 | int rc; |
4913 | |
4914 | /* If some command is waiting for a response, clear it */ |
4915 | if (priv->hostcmd_wait != NULL) { |
4916 | complete(priv->hostcmd_wait); |
4917 | priv->hostcmd_wait = NULL; |
4918 | } |
4919 | |
4920 | priv->hw_restart_owner = current; |
4921 | di = priv->device_info; |
4922 | mwl8k_fw_lock(hw); |
4923 | |
4924 | if (priv->ap_fw) |
4925 | rc = mwl8k_reload_firmware(hw, fw_image: di->fw_image_ap); |
4926 | else |
4927 | rc = mwl8k_reload_firmware(hw, fw_image: di->fw_image_sta); |
4928 | |
4929 | if (rc) |
4930 | goto fail; |
4931 | |
4932 | priv->hw_restart_owner = NULL; |
4933 | priv->hw_restart_in_progress = false; |
4934 | |
4935 | /* |
4936 | * This unlock will wake up the queues and |
4937 | * also opens the command path for other |
4938 | * commands |
4939 | */ |
4940 | mwl8k_fw_unlock(hw); |
4941 | |
4942 | ieee80211_restart_hw(hw); |
4943 | |
4944 | wiphy_err(hw->wiphy, "Firmware restarted successfully\n" ); |
4945 | |
4946 | return; |
4947 | fail: |
4948 | mwl8k_fw_unlock(hw); |
4949 | |
4950 | wiphy_err(hw->wiphy, "Firmware restart failed\n" ); |
4951 | } |
4952 | |
4953 | static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) |
4954 | { |
4955 | struct ieee80211_conf *conf = &hw->conf; |
4956 | struct mwl8k_priv *priv = hw->priv; |
4957 | int rc; |
4958 | |
4959 | rc = mwl8k_fw_lock(hw); |
4960 | if (rc) |
4961 | return rc; |
4962 | |
4963 | if (conf->flags & IEEE80211_CONF_IDLE) |
4964 | rc = mwl8k_cmd_radio_disable(hw); |
4965 | else |
4966 | rc = mwl8k_cmd_radio_enable(hw); |
4967 | if (rc) |
4968 | goto out; |
4969 | |
4970 | if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { |
4971 | rc = mwl8k_cmd_set_rf_channel(hw, conf); |
4972 | if (rc) |
4973 | goto out; |
4974 | } |
4975 | |
4976 | if (conf->power_level > 18) |
4977 | conf->power_level = 18; |
4978 | |
4979 | if (priv->ap_fw) { |
4980 | |
4981 | if (conf->flags & IEEE80211_CONF_CHANGE_POWER) { |
4982 | rc = mwl8k_cmd_tx_power(hw, conf, pwr: conf->power_level); |
4983 | if (rc) |
4984 | goto out; |
4985 | } |
4986 | |
4987 | |
4988 | } else { |
4989 | rc = mwl8k_cmd_rf_tx_power(hw, dBm: conf->power_level); |
4990 | if (rc) |
4991 | goto out; |
4992 | rc = mwl8k_cmd_mimo_config(hw, rx: 0x7, tx: 0x7); |
4993 | } |
4994 | |
4995 | out: |
4996 | mwl8k_fw_unlock(hw); |
4997 | |
4998 | return rc; |
4999 | } |
5000 | |
5001 | static void |
5002 | mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, |
5003 | struct ieee80211_bss_conf *info, u32 changed) |
5004 | { |
5005 | struct mwl8k_priv *priv = hw->priv; |
5006 | u32 ap_legacy_rates = 0; |
5007 | u8 ap_mcs_rates[16]; |
5008 | int rc; |
5009 | |
5010 | if (mwl8k_fw_lock(hw)) |
5011 | return; |
5012 | |
5013 | /* |
5014 | * No need to capture a beacon if we're no longer associated. |
5015 | */ |
5016 | if ((changed & BSS_CHANGED_ASSOC) && !vif->cfg.assoc) |
5017 | priv->capture_beacon = false; |
5018 | |
5019 | /* |
5020 | * Get the AP's legacy and MCS rates. |
5021 | */ |
5022 | if (vif->cfg.assoc) { |
5023 | struct ieee80211_sta *ap; |
5024 | |
5025 | rcu_read_lock(); |
5026 | |
5027 | ap = ieee80211_find_sta(vif, addr: vif->bss_conf.bssid); |
5028 | if (ap == NULL) { |
5029 | rcu_read_unlock(); |
5030 | goto out; |
5031 | } |
5032 | |
5033 | if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ) { |
5034 | ap_legacy_rates = ap->deflink.supp_rates[NL80211_BAND_2GHZ]; |
5035 | } else { |
5036 | ap_legacy_rates = |
5037 | ap->deflink.supp_rates[NL80211_BAND_5GHZ] << 5; |
5038 | } |
5039 | memcpy(ap_mcs_rates, &ap->deflink.ht_cap.mcs, 16); |
5040 | |
5041 | rcu_read_unlock(); |
5042 | |
5043 | if (changed & BSS_CHANGED_ASSOC) { |
5044 | if (!priv->ap_fw) { |
5045 | rc = mwl8k_cmd_set_rate(hw, vif, |
5046 | legacy_rate_mask: ap_legacy_rates, |
5047 | mcs_rates: ap_mcs_rates); |
5048 | if (rc) |
5049 | goto out; |
5050 | |
5051 | rc = mwl8k_cmd_use_fixed_rate_sta(hw); |
5052 | if (rc) |
5053 | goto out; |
5054 | } else { |
5055 | int idx; |
5056 | int rate; |
5057 | |
5058 | /* Use AP firmware specific rate command. |
5059 | */ |
5060 | idx = ffs(vif->bss_conf.basic_rates); |
5061 | if (idx) |
5062 | idx--; |
5063 | |
5064 | if (hw->conf.chandef.chan->band == |
5065 | NL80211_BAND_2GHZ) |
5066 | rate = mwl8k_rates_24[idx].hw_value; |
5067 | else |
5068 | rate = mwl8k_rates_50[idx].hw_value; |
5069 | |
5070 | mwl8k_cmd_use_fixed_rate_ap(hw, mcast: rate, mgmt: rate); |
5071 | } |
5072 | } |
5073 | } |
5074 | |
5075 | if (changed & BSS_CHANGED_ERP_PREAMBLE) { |
5076 | rc = mwl8k_set_radio_preamble(hw, |
5077 | short_preamble: vif->bss_conf.use_short_preamble); |
5078 | if (rc) |
5079 | goto out; |
5080 | } |
5081 | |
5082 | if ((changed & BSS_CHANGED_ERP_SLOT) && !priv->ap_fw) { |
5083 | rc = mwl8k_cmd_set_slot(hw, short_slot_time: vif->bss_conf.use_short_slot); |
5084 | if (rc) |
5085 | goto out; |
5086 | } |
5087 | |
5088 | if (vif->cfg.assoc && !priv->ap_fw && |
5089 | (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT | |
5090 | BSS_CHANGED_HT))) { |
5091 | rc = mwl8k_cmd_set_aid(hw, vif, legacy_rate_mask: ap_legacy_rates); |
5092 | if (rc) |
5093 | goto out; |
5094 | } |
5095 | |
5096 | if (vif->cfg.assoc && |
5097 | (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INT))) { |
5098 | /* |
5099 | * Finalize the join. Tell rx handler to process |
5100 | * next beacon from our BSSID. |
5101 | */ |
5102 | memcpy(priv->capture_bssid, vif->bss_conf.bssid, ETH_ALEN); |
5103 | priv->capture_beacon = true; |
5104 | } |
5105 | |
5106 | out: |
5107 | mwl8k_fw_unlock(hw); |
5108 | } |
5109 | |
5110 | static void |
5111 | mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, |
5112 | struct ieee80211_bss_conf *info, u32 changed) |
5113 | { |
5114 | int rc; |
5115 | |
5116 | if (mwl8k_fw_lock(hw)) |
5117 | return; |
5118 | |
5119 | if (changed & BSS_CHANGED_ERP_PREAMBLE) { |
5120 | rc = mwl8k_set_radio_preamble(hw, |
5121 | short_preamble: vif->bss_conf.use_short_preamble); |
5122 | if (rc) |
5123 | goto out; |
5124 | } |
5125 | |
5126 | if (changed & BSS_CHANGED_BASIC_RATES) { |
5127 | int idx; |
5128 | int rate; |
5129 | |
5130 | /* |
5131 | * Use lowest supported basic rate for multicasts |
5132 | * and management frames (such as probe responses -- |
5133 | * beacons will always go out at 1 Mb/s). |
5134 | */ |
5135 | idx = ffs(vif->bss_conf.basic_rates); |
5136 | if (idx) |
5137 | idx--; |
5138 | |
5139 | if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ) |
5140 | rate = mwl8k_rates_24[idx].hw_value; |
5141 | else |
5142 | rate = mwl8k_rates_50[idx].hw_value; |
5143 | |
5144 | mwl8k_cmd_use_fixed_rate_ap(hw, mcast: rate, mgmt: rate); |
5145 | } |
5146 | |
5147 | if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) { |
5148 | struct sk_buff *skb; |
5149 | |
5150 | skb = ieee80211_beacon_get(hw, vif, link_id: 0); |
5151 | if (skb != NULL) { |
5152 | mwl8k_cmd_set_beacon(hw, vif, beacon: skb->data, len: skb->len); |
5153 | kfree_skb(skb); |
5154 | } |
5155 | } |
5156 | |
5157 | if (changed & BSS_CHANGED_BEACON_ENABLED) |
5158 | mwl8k_cmd_bss_start(hw, vif, enable: info->enable_beacon); |
5159 | |
5160 | out: |
5161 | mwl8k_fw_unlock(hw); |
5162 | } |
5163 | |
5164 | static void |
5165 | mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, |
5166 | struct ieee80211_bss_conf *info, u64 changed) |
5167 | { |
5168 | if (vif->type == NL80211_IFTYPE_STATION) |
5169 | mwl8k_bss_info_changed_sta(hw, vif, info, changed); |
5170 | if (vif->type == NL80211_IFTYPE_AP) |
5171 | mwl8k_bss_info_changed_ap(hw, vif, info, changed); |
5172 | } |
5173 | |
5174 | static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, |
5175 | struct netdev_hw_addr_list *mc_list) |
5176 | { |
5177 | struct mwl8k_cmd_pkt *cmd; |
5178 | |
5179 | /* |
5180 | * Synthesize and return a command packet that programs the |
5181 | * hardware multicast address filter. At this point we don't |
5182 | * know whether FIF_ALLMULTI is being requested, but if it is, |
5183 | * we'll end up throwing this packet away and creating a new |
5184 | * one in mwl8k_configure_filter(). |
5185 | */ |
5186 | cmd = __mwl8k_cmd_mac_multicast_adr(hw, allmulti: 0, mc_list); |
5187 | |
5188 | return (unsigned long)cmd; |
5189 | } |
5190 | |
5191 | static int |
5192 | mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw, |
5193 | unsigned int changed_flags, |
5194 | unsigned int *total_flags) |
5195 | { |
5196 | struct mwl8k_priv *priv = hw->priv; |
5197 | |
5198 | /* |
5199 | * Hardware sniffer mode is mutually exclusive with STA |
5200 | * operation, so refuse to enable sniffer mode if a STA |
5201 | * interface is active. |
5202 | */ |
5203 | if (!list_empty(head: &priv->vif_list)) { |
5204 | if (net_ratelimit()) |
5205 | wiphy_info(hw->wiphy, |
5206 | "not enabling sniffer mode because STA interface is active\n" ); |
5207 | return 0; |
5208 | } |
5209 | |
5210 | if (!priv->sniffer_enabled) { |
5211 | if (mwl8k_cmd_enable_sniffer(hw, enable: 1)) |
5212 | return 0; |
5213 | priv->sniffer_enabled = true; |
5214 | } |
5215 | |
5216 | *total_flags &= FIF_ALLMULTI | |
5217 | FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL | |
5218 | FIF_OTHER_BSS; |
5219 | |
5220 | return 1; |
5221 | } |
5222 | |
5223 | static struct mwl8k_vif *mwl8k_first_vif(struct mwl8k_priv *priv) |
5224 | { |
5225 | if (!list_empty(head: &priv->vif_list)) |
5226 | return list_entry(priv->vif_list.next, struct mwl8k_vif, list); |
5227 | |
5228 | return NULL; |
5229 | } |
5230 | |
5231 | static void mwl8k_configure_filter(struct ieee80211_hw *hw, |
5232 | unsigned int changed_flags, |
5233 | unsigned int *total_flags, |
5234 | u64 multicast) |
5235 | { |
5236 | struct mwl8k_priv *priv = hw->priv; |
5237 | struct mwl8k_cmd_pkt *cmd = (void *)(unsigned long)multicast; |
5238 | |
5239 | /* |
5240 | * AP firmware doesn't allow fine-grained control over |
5241 | * the receive filter. |
5242 | */ |
5243 | if (priv->ap_fw) { |
5244 | *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC; |
5245 | kfree(objp: cmd); |
5246 | return; |
5247 | } |
5248 | |
5249 | /* |
5250 | * Enable hardware sniffer mode if FIF_CONTROL or |
5251 | * FIF_OTHER_BSS is requested. |
5252 | */ |
5253 | if (*total_flags & (FIF_CONTROL | FIF_OTHER_BSS) && |
5254 | mwl8k_configure_filter_sniffer(hw, changed_flags, total_flags)) { |
5255 | kfree(objp: cmd); |
5256 | return; |
5257 | } |
5258 | |
5259 | /* Clear unsupported feature flags */ |
5260 | *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC; |
5261 | |
5262 | if (mwl8k_fw_lock(hw)) { |
5263 | kfree(objp: cmd); |
5264 | return; |
5265 | } |
5266 | |
5267 | if (priv->sniffer_enabled) { |
5268 | mwl8k_cmd_enable_sniffer(hw, enable: 0); |
5269 | priv->sniffer_enabled = false; |
5270 | } |
5271 | |
5272 | if (changed_flags & FIF_BCN_PRBRESP_PROMISC) { |
5273 | if (*total_flags & FIF_BCN_PRBRESP_PROMISC) { |
5274 | /* |
5275 | * Disable the BSS filter. |
5276 | */ |
5277 | mwl8k_cmd_set_pre_scan(hw); |
5278 | } else { |
5279 | struct mwl8k_vif *mwl8k_vif; |
5280 | const u8 *bssid; |
5281 | |
5282 | /* |
5283 | * Enable the BSS filter. |
5284 | * |
5285 | * If there is an active STA interface, use that |
5286 | * interface's BSSID, otherwise use a dummy one |
5287 | * (where the OUI part needs to be nonzero for |
5288 | * the BSSID to be accepted by POST_SCAN). |
5289 | */ |
5290 | mwl8k_vif = mwl8k_first_vif(priv); |
5291 | if (mwl8k_vif != NULL) |
5292 | bssid = mwl8k_vif->vif->bss_conf.bssid; |
5293 | else |
5294 | bssid = "\x01\x00\x00\x00\x00\x00" ; |
5295 | |
5296 | mwl8k_cmd_set_post_scan(hw, mac: bssid); |
5297 | } |
5298 | } |
5299 | |
5300 | /* |
5301 | * If FIF_ALLMULTI is being requested, throw away the command |
5302 | * packet that ->prepare_multicast() built and replace it with |
5303 | * a command packet that enables reception of all multicast |
5304 | * packets. |
5305 | */ |
5306 | if (*total_flags & FIF_ALLMULTI) { |
5307 | kfree(objp: cmd); |
5308 | cmd = __mwl8k_cmd_mac_multicast_adr(hw, allmulti: 1, NULL); |
5309 | } |
5310 | |
5311 | if (cmd != NULL) { |
5312 | mwl8k_post_cmd(hw, cmd); |
5313 | kfree(objp: cmd); |
5314 | } |
5315 | |
5316 | mwl8k_fw_unlock(hw); |
5317 | } |
5318 | |
5319 | static int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) |
5320 | { |
5321 | return mwl8k_cmd_set_rts_threshold(hw, rts_thresh: value); |
5322 | } |
5323 | |
5324 | static int mwl8k_sta_remove(struct ieee80211_hw *hw, |
5325 | struct ieee80211_vif *vif, |
5326 | struct ieee80211_sta *sta) |
5327 | { |
5328 | struct mwl8k_priv *priv = hw->priv; |
5329 | |
5330 | if (priv->ap_fw) |
5331 | return mwl8k_cmd_set_new_stn_del(hw, vif, addr: sta->addr); |
5332 | else |
5333 | return mwl8k_cmd_update_stadb_del(hw, vif, addr: sta->addr); |
5334 | } |
5335 | |
5336 | static int mwl8k_sta_add(struct ieee80211_hw *hw, |
5337 | struct ieee80211_vif *vif, |
5338 | struct ieee80211_sta *sta) |
5339 | { |
5340 | struct mwl8k_priv *priv = hw->priv; |
5341 | int ret; |
5342 | int i; |
5343 | struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); |
5344 | struct ieee80211_key_conf *key; |
5345 | |
5346 | if (!priv->ap_fw) { |
5347 | ret = mwl8k_cmd_update_stadb_add(hw, vif, sta); |
5348 | if (ret >= 0) { |
5349 | MWL8K_STA(sta)->peer_id = ret; |
5350 | if (sta->deflink.ht_cap.ht_supported) |
5351 | MWL8K_STA(sta)->is_ampdu_allowed = true; |
5352 | ret = 0; |
5353 | } |
5354 | |
5355 | } else { |
5356 | ret = mwl8k_cmd_set_new_stn_add(hw, vif, sta); |
5357 | } |
5358 | |
5359 | for (i = 0; i < NUM_WEP_KEYS; i++) { |
5360 | key = IEEE80211_KEY_CONF(mwl8k_vif->wep_key_conf[i].key); |
5361 | if (mwl8k_vif->wep_key_conf[i].enabled) |
5362 | mwl8k_set_key(hw, cmd_param: SET_KEY, vif, sta, key); |
5363 | } |
5364 | return ret; |
5365 | } |
5366 | |
5367 | static int mwl8k_conf_tx(struct ieee80211_hw *hw, |
5368 | struct ieee80211_vif *vif, |
5369 | unsigned int link_id, u16 queue, |
5370 | const struct ieee80211_tx_queue_params *params) |
5371 | { |
5372 | struct mwl8k_priv *priv = hw->priv; |
5373 | int rc; |
5374 | |
5375 | rc = mwl8k_fw_lock(hw); |
5376 | if (!rc) { |
5377 | BUG_ON(queue > MWL8K_TX_WMM_QUEUES - 1); |
5378 | memcpy(&priv->wmm_params[queue], params, sizeof(*params)); |
5379 | |
5380 | if (!priv->wmm_enabled) |
5381 | rc = mwl8k_cmd_set_wmm_mode(hw, enable: 1); |
5382 | |
5383 | if (!rc) { |
5384 | int q = MWL8K_TX_WMM_QUEUES - 1 - queue; |
5385 | rc = mwl8k_cmd_set_edca_params(hw, qnum: q, |
5386 | cw_min: params->cw_min, |
5387 | cw_max: params->cw_max, |
5388 | aifs: params->aifs, |
5389 | txop: params->txop); |
5390 | } |
5391 | |
5392 | mwl8k_fw_unlock(hw); |
5393 | } |
5394 | |
5395 | return rc; |
5396 | } |
5397 | |
5398 | static int mwl8k_get_stats(struct ieee80211_hw *hw, |
5399 | struct ieee80211_low_level_stats *stats) |
5400 | { |
5401 | return mwl8k_cmd_get_stat(hw, stats); |
5402 | } |
5403 | |
5404 | static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx, |
5405 | struct survey_info *survey) |
5406 | { |
5407 | struct mwl8k_priv *priv = hw->priv; |
5408 | struct ieee80211_conf *conf = &hw->conf; |
5409 | struct ieee80211_supported_band *sband; |
5410 | |
5411 | if (priv->ap_fw) { |
5412 | sband = hw->wiphy->bands[NL80211_BAND_2GHZ]; |
5413 | |
5414 | if (sband && idx >= sband->n_channels) { |
5415 | idx -= sband->n_channels; |
5416 | sband = NULL; |
5417 | } |
5418 | |
5419 | if (!sband) |
5420 | sband = hw->wiphy->bands[NL80211_BAND_5GHZ]; |
5421 | |
5422 | if (!sband || idx >= sband->n_channels) |
5423 | return -ENOENT; |
5424 | |
5425 | memcpy(survey, &priv->survey[idx], sizeof(*survey)); |
5426 | survey->channel = &sband->channels[idx]; |
5427 | |
5428 | return 0; |
5429 | } |
5430 | |
5431 | if (idx != 0) |
5432 | return -ENOENT; |
5433 | |
5434 | survey->channel = conf->chandef.chan; |
5435 | survey->filled = SURVEY_INFO_NOISE_DBM; |
5436 | survey->noise = priv->noise; |
5437 | |
5438 | return 0; |
5439 | } |
5440 | |
5441 | #define MAX_AMPDU_ATTEMPTS 5 |
5442 | |
5443 | static int |
5444 | mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, |
5445 | struct ieee80211_ampdu_params *params) |
5446 | { |
5447 | struct ieee80211_sta *sta = params->sta; |
5448 | enum ieee80211_ampdu_mlme_action action = params->action; |
5449 | u16 tid = params->tid; |
5450 | u16 *ssn = ¶ms->ssn; |
5451 | u8 buf_size = params->buf_size; |
5452 | int i, rc = 0; |
5453 | struct mwl8k_priv *priv = hw->priv; |
5454 | struct mwl8k_ampdu_stream *stream; |
5455 | u8 *addr = sta->addr, idx; |
5456 | struct mwl8k_sta *sta_info = MWL8K_STA(sta); |
5457 | |
5458 | if (!ieee80211_hw_check(hw, AMPDU_AGGREGATION)) |
5459 | return -ENOTSUPP; |
5460 | |
5461 | spin_lock(lock: &priv->stream_lock); |
5462 | stream = mwl8k_lookup_stream(hw, addr, tid); |
5463 | |
5464 | switch (action) { |
5465 | case IEEE80211_AMPDU_RX_START: |
5466 | case IEEE80211_AMPDU_RX_STOP: |
5467 | break; |
5468 | case IEEE80211_AMPDU_TX_START: |
5469 | /* By the time we get here the hw queues may contain outgoing |
5470 | * packets for this RA/TID that are not part of this BA |
5471 | * session. The hw will assign sequence numbers to these |
5472 | * packets as they go out. So if we query the hw for its next |
5473 | * sequence number and use that for the SSN here, it may end up |
5474 | * being wrong, which will lead to sequence number mismatch at |
5475 | * the recipient. To avoid this, we reset the sequence number |
5476 | * to O for the first MPDU in this BA stream. |
5477 | */ |
5478 | *ssn = 0; |
5479 | if (stream == NULL) { |
5480 | /* This means that somebody outside this driver called |
5481 | * ieee80211_start_tx_ba_session. This is unexpected |
5482 | * because we do our own rate control. Just warn and |
5483 | * move on. |
5484 | */ |
5485 | wiphy_warn(hw->wiphy, "Unexpected call to %s. " |
5486 | "Proceeding anyway.\n" , __func__); |
5487 | stream = mwl8k_add_stream(hw, sta, tid); |
5488 | } |
5489 | if (stream == NULL) { |
5490 | wiphy_debug(hw->wiphy, "no free AMPDU streams\n" ); |
5491 | rc = -EBUSY; |
5492 | break; |
5493 | } |
5494 | stream->state = AMPDU_STREAM_IN_PROGRESS; |
5495 | |
5496 | /* Release the lock before we do the time consuming stuff */ |
5497 | spin_unlock(lock: &priv->stream_lock); |
5498 | for (i = 0; i < MAX_AMPDU_ATTEMPTS; i++) { |
5499 | |
5500 | /* Check if link is still valid */ |
5501 | if (!sta_info->is_ampdu_allowed) { |
5502 | spin_lock(lock: &priv->stream_lock); |
5503 | mwl8k_remove_stream(hw, stream); |
5504 | spin_unlock(lock: &priv->stream_lock); |
5505 | return -EBUSY; |
5506 | } |
5507 | |
5508 | rc = mwl8k_check_ba(hw, stream, vif); |
5509 | |
5510 | /* If HW restart is in progress mwl8k_post_cmd will |
5511 | * return -EBUSY. Avoid retrying mwl8k_check_ba in |
5512 | * such cases |
5513 | */ |
5514 | if (!rc || rc == -EBUSY) |
5515 | break; |
5516 | /* |
5517 | * HW queues take time to be flushed, give them |
5518 | * sufficient time |
5519 | */ |
5520 | |
5521 | msleep(msecs: 1000); |
5522 | } |
5523 | spin_lock(lock: &priv->stream_lock); |
5524 | if (rc) { |
5525 | wiphy_err(hw->wiphy, "Stream for tid %d busy after %d" |
5526 | " attempts\n" , tid, MAX_AMPDU_ATTEMPTS); |
5527 | mwl8k_remove_stream(hw, stream); |
5528 | rc = -EBUSY; |
5529 | break; |
5530 | } |
5531 | rc = IEEE80211_AMPDU_TX_START_IMMEDIATE; |
5532 | break; |
5533 | case IEEE80211_AMPDU_TX_STOP_CONT: |
5534 | case IEEE80211_AMPDU_TX_STOP_FLUSH: |
5535 | case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: |
5536 | if (stream) { |
5537 | if (stream->state == AMPDU_STREAM_ACTIVE) { |
5538 | idx = stream->idx; |
5539 | spin_unlock(lock: &priv->stream_lock); |
5540 | mwl8k_destroy_ba(hw, idx); |
5541 | spin_lock(lock: &priv->stream_lock); |
5542 | } |
5543 | mwl8k_remove_stream(hw, stream); |
5544 | } |
5545 | ieee80211_stop_tx_ba_cb_irqsafe(vif, ra: addr, tid); |
5546 | break; |
5547 | case IEEE80211_AMPDU_TX_OPERATIONAL: |
5548 | BUG_ON(stream == NULL); |
5549 | BUG_ON(stream->state != AMPDU_STREAM_IN_PROGRESS); |
5550 | spin_unlock(lock: &priv->stream_lock); |
5551 | rc = mwl8k_create_ba(hw, stream, buf_size, vif); |
5552 | spin_lock(lock: &priv->stream_lock); |
5553 | if (!rc) |
5554 | stream->state = AMPDU_STREAM_ACTIVE; |
5555 | else { |
5556 | idx = stream->idx; |
5557 | spin_unlock(lock: &priv->stream_lock); |
5558 | mwl8k_destroy_ba(hw, idx); |
5559 | spin_lock(lock: &priv->stream_lock); |
5560 | wiphy_debug(hw->wiphy, |
5561 | "Failed adding stream for sta %pM tid %d\n" , |
5562 | addr, tid); |
5563 | mwl8k_remove_stream(hw, stream); |
5564 | } |
5565 | break; |
5566 | |
5567 | default: |
5568 | rc = -ENOTSUPP; |
5569 | } |
5570 | |
5571 | spin_unlock(lock: &priv->stream_lock); |
5572 | return rc; |
5573 | } |
5574 | |
5575 | static void mwl8k_sw_scan_start(struct ieee80211_hw *hw, |
5576 | struct ieee80211_vif *vif, |
5577 | const u8 *mac_addr) |
5578 | { |
5579 | struct mwl8k_priv *priv = hw->priv; |
5580 | u8 tmp; |
5581 | |
5582 | if (!priv->ap_fw) |
5583 | return; |
5584 | |
5585 | /* clear all stats */ |
5586 | priv->channel_time = 0; |
5587 | ioread32(priv->regs + BBU_RXRDY_CNT_REG); |
5588 | ioread32(priv->regs + NOK_CCA_CNT_REG); |
5589 | mwl8k_cmd_bbp_reg_access(hw: priv->hw, action: 0, BBU_AVG_NOISE_VAL, value: &tmp); |
5590 | |
5591 | priv->sw_scan_start = true; |
5592 | } |
5593 | |
5594 | static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw, |
5595 | struct ieee80211_vif *vif) |
5596 | { |
5597 | struct mwl8k_priv *priv = hw->priv; |
5598 | u8 tmp; |
5599 | |
5600 | if (!priv->ap_fw) |
5601 | return; |
5602 | |
5603 | priv->sw_scan_start = false; |
5604 | |
5605 | /* clear all stats */ |
5606 | priv->channel_time = 0; |
5607 | ioread32(priv->regs + BBU_RXRDY_CNT_REG); |
5608 | ioread32(priv->regs + NOK_CCA_CNT_REG); |
5609 | mwl8k_cmd_bbp_reg_access(hw: priv->hw, action: 0, BBU_AVG_NOISE_VAL, value: &tmp); |
5610 | } |
5611 | |
5612 | static const struct ieee80211_ops mwl8k_ops = { |
5613 | .add_chanctx = ieee80211_emulate_add_chanctx, |
5614 | .remove_chanctx = ieee80211_emulate_remove_chanctx, |
5615 | .change_chanctx = ieee80211_emulate_change_chanctx, |
5616 | .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, |
5617 | .tx = mwl8k_tx, |
5618 | .wake_tx_queue = ieee80211_handle_wake_tx_queue, |
5619 | .start = mwl8k_start, |
5620 | .stop = mwl8k_stop, |
5621 | .add_interface = mwl8k_add_interface, |
5622 | .remove_interface = mwl8k_remove_interface, |
5623 | .config = mwl8k_config, |
5624 | .bss_info_changed = mwl8k_bss_info_changed, |
5625 | .prepare_multicast = mwl8k_prepare_multicast, |
5626 | .configure_filter = mwl8k_configure_filter, |
5627 | .set_key = mwl8k_set_key, |
5628 | .set_rts_threshold = mwl8k_set_rts_threshold, |
5629 | .sta_add = mwl8k_sta_add, |
5630 | .sta_remove = mwl8k_sta_remove, |
5631 | .conf_tx = mwl8k_conf_tx, |
5632 | .get_stats = mwl8k_get_stats, |
5633 | .get_survey = mwl8k_get_survey, |
5634 | .ampdu_action = mwl8k_ampdu_action, |
5635 | .sw_scan_start = mwl8k_sw_scan_start, |
5636 | .sw_scan_complete = mwl8k_sw_scan_complete, |
5637 | }; |
5638 | |
5639 | static void mwl8k_finalize_join_worker(struct work_struct *work) |
5640 | { |
5641 | struct mwl8k_priv *priv = |
5642 | container_of(work, struct mwl8k_priv, finalize_join_worker); |
5643 | struct sk_buff *skb = priv->beacon_skb; |
5644 | struct ieee80211_mgmt *mgmt = (void *)skb->data; |
5645 | int len = skb->len - offsetof(struct ieee80211_mgmt, u.beacon.variable); |
5646 | const u8 *tim = cfg80211_find_ie(eid: WLAN_EID_TIM, |
5647 | ies: mgmt->u.beacon.variable, len); |
5648 | int dtim_period = 1; |
5649 | |
5650 | if (tim && tim[1] >= 2) |
5651 | dtim_period = tim[3]; |
5652 | |
5653 | mwl8k_cmd_finalize_join(hw: priv->hw, frame: skb->data, framelen: skb->len, dtim: dtim_period); |
5654 | |
5655 | dev_kfree_skb(skb); |
5656 | priv->beacon_skb = NULL; |
5657 | } |
5658 | |
5659 | enum { |
5660 | MWL8363 = 0, |
5661 | MWL8687, |
5662 | MWL8366, |
5663 | MWL8764, |
5664 | }; |
5665 | |
5666 | #define MWL8K_8366_AP_FW_API 3 |
5667 | #define _MWL8K_8366_AP_FW(api) "mwl8k/fmimage_8366_ap-" #api ".fw" |
5668 | #define MWL8K_8366_AP_FW(api) _MWL8K_8366_AP_FW(api) |
5669 | |
5670 | #define MWL8K_8764_AP_FW_API 1 |
5671 | #define _MWL8K_8764_AP_FW(api) "mwl8k/fmimage_8764_ap-" #api ".fw" |
5672 | #define MWL8K_8764_AP_FW(api) _MWL8K_8764_AP_FW(api) |
5673 | |
5674 | static struct mwl8k_device_info mwl8k_info_tbl[] = { |
5675 | [MWL8363] = { |
5676 | .part_name = "88w8363" , |
5677 | .helper_image = "mwl8k/helper_8363.fw" , |
5678 | .fw_image_sta = "mwl8k/fmimage_8363.fw" , |
5679 | }, |
5680 | [MWL8687] = { |
5681 | .part_name = "88w8687" , |
5682 | .helper_image = "mwl8k/helper_8687.fw" , |
5683 | .fw_image_sta = "mwl8k/fmimage_8687.fw" , |
5684 | }, |
5685 | [MWL8366] = { |
5686 | .part_name = "88w8366" , |
5687 | .helper_image = "mwl8k/helper_8366.fw" , |
5688 | .fw_image_sta = "mwl8k/fmimage_8366.fw" , |
5689 | .fw_image_ap = MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API), |
5690 | .fw_api_ap = MWL8K_8366_AP_FW_API, |
5691 | .ap_rxd_ops = &rxd_ap_ops, |
5692 | }, |
5693 | [MWL8764] = { |
5694 | .part_name = "88w8764" , |
5695 | .fw_image_ap = MWL8K_8764_AP_FW(MWL8K_8764_AP_FW_API), |
5696 | .fw_api_ap = MWL8K_8764_AP_FW_API, |
5697 | .ap_rxd_ops = &rxd_ap_ops, |
5698 | }, |
5699 | }; |
5700 | |
5701 | MODULE_FIRMWARE("mwl8k/helper_8363.fw" ); |
5702 | MODULE_FIRMWARE("mwl8k/fmimage_8363.fw" ); |
5703 | MODULE_FIRMWARE("mwl8k/helper_8687.fw" ); |
5704 | MODULE_FIRMWARE("mwl8k/fmimage_8687.fw" ); |
5705 | MODULE_FIRMWARE("mwl8k/helper_8366.fw" ); |
5706 | MODULE_FIRMWARE("mwl8k/fmimage_8366.fw" ); |
5707 | MODULE_FIRMWARE(MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API)); |
5708 | |
5709 | static const struct pci_device_id mwl8k_pci_id_table[] = { |
5710 | { PCI_VDEVICE(MARVELL, 0x2a0a), .driver_data = MWL8363, }, |
5711 | { PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, }, |
5712 | { PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, }, |
5713 | { PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, }, |
5714 | { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, }, |
5715 | { PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, }, |
5716 | { PCI_VDEVICE(MARVELL, 0x2a41), .driver_data = MWL8366, }, |
5717 | { PCI_VDEVICE(MARVELL, 0x2a42), .driver_data = MWL8366, }, |
5718 | { PCI_VDEVICE(MARVELL, 0x2a43), .driver_data = MWL8366, }, |
5719 | { PCI_VDEVICE(MARVELL, 0x2b36), .driver_data = MWL8764, }, |
5720 | { }, |
5721 | }; |
5722 | MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table); |
5723 | |
5724 | static int mwl8k_request_alt_fw(struct mwl8k_priv *priv) |
5725 | { |
5726 | int rc; |
5727 | printk(KERN_ERR "%s: Error requesting preferred fw %s.\n" |
5728 | "Trying alternative firmware %s\n" , pci_name(priv->pdev), |
5729 | priv->fw_pref, priv->fw_alt); |
5730 | rc = mwl8k_request_fw(priv, fname: priv->fw_alt, fw: &priv->fw_ucode, nowait: true); |
5731 | if (rc) { |
5732 | printk(KERN_ERR "%s: Error requesting alt fw %s\n" , |
5733 | pci_name(priv->pdev), priv->fw_alt); |
5734 | return rc; |
5735 | } |
5736 | return 0; |
5737 | } |
5738 | |
5739 | static int mwl8k_firmware_load_success(struct mwl8k_priv *priv); |
5740 | static void mwl8k_fw_state_machine(const struct firmware *fw, void *context) |
5741 | { |
5742 | struct mwl8k_priv *priv = context; |
5743 | struct mwl8k_device_info *di = priv->device_info; |
5744 | int rc; |
5745 | |
5746 | switch (priv->fw_state) { |
5747 | case FW_STATE_INIT: |
5748 | if (!fw) { |
5749 | printk(KERN_ERR "%s: Error requesting helper fw %s\n" , |
5750 | pci_name(priv->pdev), di->helper_image); |
5751 | goto fail; |
5752 | } |
5753 | priv->fw_helper = fw; |
5754 | rc = mwl8k_request_fw(priv, fname: priv->fw_pref, fw: &priv->fw_ucode, |
5755 | nowait: true); |
5756 | if (rc && priv->fw_alt) { |
5757 | rc = mwl8k_request_alt_fw(priv); |
5758 | if (rc) |
5759 | goto fail; |
5760 | priv->fw_state = FW_STATE_LOADING_ALT; |
5761 | } else if (rc) |
5762 | goto fail; |
5763 | else |
5764 | priv->fw_state = FW_STATE_LOADING_PREF; |
5765 | break; |
5766 | |
5767 | case FW_STATE_LOADING_PREF: |
5768 | if (!fw) { |
5769 | if (priv->fw_alt) { |
5770 | rc = mwl8k_request_alt_fw(priv); |
5771 | if (rc) |
5772 | goto fail; |
5773 | priv->fw_state = FW_STATE_LOADING_ALT; |
5774 | } else |
5775 | goto fail; |
5776 | } else { |
5777 | priv->fw_ucode = fw; |
5778 | rc = mwl8k_firmware_load_success(priv); |
5779 | if (rc) |
5780 | goto fail; |
5781 | else |
5782 | complete(&priv->firmware_loading_complete); |
5783 | } |
5784 | break; |
5785 | |
5786 | case FW_STATE_LOADING_ALT: |
5787 | if (!fw) { |
5788 | printk(KERN_ERR "%s: Error requesting alt fw %s\n" , |
5789 | pci_name(priv->pdev), di->helper_image); |
5790 | goto fail; |
5791 | } |
5792 | priv->fw_ucode = fw; |
5793 | rc = mwl8k_firmware_load_success(priv); |
5794 | if (rc) |
5795 | goto fail; |
5796 | else |
5797 | complete(&priv->firmware_loading_complete); |
5798 | break; |
5799 | |
5800 | default: |
5801 | printk(KERN_ERR "%s: Unexpected firmware loading state: %d\n" , |
5802 | MWL8K_NAME, priv->fw_state); |
5803 | BUG_ON(1); |
5804 | } |
5805 | |
5806 | return; |
5807 | |
5808 | fail: |
5809 | priv->fw_state = FW_STATE_ERROR; |
5810 | complete(&priv->firmware_loading_complete); |
5811 | mwl8k_release_firmware(priv); |
5812 | device_release_driver(dev: &priv->pdev->dev); |
5813 | } |
5814 | |
5815 | #define MAX_RESTART_ATTEMPTS 1 |
5816 | static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image, |
5817 | bool nowait) |
5818 | { |
5819 | struct mwl8k_priv *priv = hw->priv; |
5820 | int rc; |
5821 | int count = MAX_RESTART_ATTEMPTS; |
5822 | |
5823 | retry: |
5824 | /* Reset firmware and hardware */ |
5825 | mwl8k_hw_reset(priv); |
5826 | |
5827 | /* Ask userland hotplug daemon for the device firmware */ |
5828 | rc = mwl8k_request_firmware(priv, fw_image, nowait); |
5829 | if (rc) { |
5830 | wiphy_err(hw->wiphy, "Firmware files not found\n" ); |
5831 | return rc; |
5832 | } |
5833 | |
5834 | if (nowait) |
5835 | return rc; |
5836 | |
5837 | /* Load firmware into hardware */ |
5838 | rc = mwl8k_load_firmware(hw); |
5839 | if (rc) |
5840 | wiphy_err(hw->wiphy, "Cannot start firmware\n" ); |
5841 | |
5842 | /* Reclaim memory once firmware is successfully loaded */ |
5843 | mwl8k_release_firmware(priv); |
5844 | |
5845 | if (rc && count) { |
5846 | /* FW did not start successfully; |
5847 | * lets try one more time |
5848 | */ |
5849 | count--; |
5850 | wiphy_err(hw->wiphy, "Trying to reload the firmware again\n" ); |
5851 | msleep(msecs: 20); |
5852 | goto retry; |
5853 | } |
5854 | |
5855 | return rc; |
5856 | } |
5857 | |
5858 | static int mwl8k_init_txqs(struct ieee80211_hw *hw) |
5859 | { |
5860 | struct mwl8k_priv *priv = hw->priv; |
5861 | int rc = 0; |
5862 | int i; |
5863 | |
5864 | for (i = 0; i < mwl8k_tx_queues(priv); i++) { |
5865 | rc = mwl8k_txq_init(hw, index: i); |
5866 | if (rc) |
5867 | break; |
5868 | if (priv->ap_fw) |
5869 | iowrite32(priv->txq[i].txd_dma, |
5870 | priv->sram + priv->txq_offset[i]); |
5871 | } |
5872 | return rc; |
5873 | } |
5874 | |
5875 | /* initialize hw after successfully loading a firmware image */ |
5876 | static int mwl8k_probe_hw(struct ieee80211_hw *hw) |
5877 | { |
5878 | struct mwl8k_priv *priv = hw->priv; |
5879 | int rc = 0; |
5880 | int i; |
5881 | |
5882 | if (priv->ap_fw) { |
5883 | priv->rxd_ops = priv->device_info->ap_rxd_ops; |
5884 | if (priv->rxd_ops == NULL) { |
5885 | wiphy_err(hw->wiphy, |
5886 | "Driver does not have AP firmware image support for this hardware\n" ); |
5887 | rc = -ENOENT; |
5888 | goto err_stop_firmware; |
5889 | } |
5890 | } else { |
5891 | priv->rxd_ops = &rxd_sta_ops; |
5892 | } |
5893 | |
5894 | priv->sniffer_enabled = false; |
5895 | priv->wmm_enabled = false; |
5896 | priv->pending_tx_pkts = 0; |
5897 | atomic_set(v: &priv->watchdog_event_pending, i: 0); |
5898 | |
5899 | rc = mwl8k_rxq_init(hw, index: 0); |
5900 | if (rc) |
5901 | goto err_stop_firmware; |
5902 | rxq_refill(hw, index: 0, INT_MAX); |
5903 | |
5904 | /* For the sta firmware, we need to know the dma addresses of tx queues |
5905 | * before sending MWL8K_CMD_GET_HW_SPEC. So we must initialize them |
5906 | * prior to issuing this command. But for the AP case, we learn the |
5907 | * total number of queues from the result CMD_GET_HW_SPEC, so for this |
5908 | * case we must initialize the tx queues after. |
5909 | */ |
5910 | priv->num_ampdu_queues = 0; |
5911 | if (!priv->ap_fw) { |
5912 | rc = mwl8k_init_txqs(hw); |
5913 | if (rc) |
5914 | goto err_free_queues; |
5915 | } |
5916 | |
5917 | iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); |
5918 | iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); |
5919 | iowrite32(MWL8K_A2H_INT_TX_DONE|MWL8K_A2H_INT_RX_READY| |
5920 | MWL8K_A2H_INT_BA_WATCHDOG, |
5921 | priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); |
5922 | iowrite32(MWL8K_A2H_INT_OPC_DONE, |
5923 | priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); |
5924 | |
5925 | rc = request_irq(irq: priv->pdev->irq, handler: mwl8k_interrupt, |
5926 | IRQF_SHARED, MWL8K_NAME, dev: hw); |
5927 | if (rc) { |
5928 | wiphy_err(hw->wiphy, "failed to register IRQ handler\n" ); |
5929 | goto err_free_queues; |
5930 | } |
5931 | |
5932 | /* |
5933 | * When hw restart is requested, |
5934 | * mac80211 will take care of clearing |
5935 | * the ampdu streams, so do not clear |
5936 | * the ampdu state here |
5937 | */ |
5938 | if (!priv->hw_restart_in_progress) |
5939 | memset(priv->ampdu, 0, sizeof(priv->ampdu)); |
5940 | |
5941 | /* |
5942 | * Temporarily enable interrupts. Initial firmware host |
5943 | * commands use interrupts and avoid polling. Disable |
5944 | * interrupts when done. |
5945 | */ |
5946 | iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); |
5947 | |
5948 | /* Get config data, mac addrs etc */ |
5949 | if (priv->ap_fw) { |
5950 | rc = mwl8k_cmd_get_hw_spec_ap(hw); |
5951 | if (!rc) |
5952 | rc = mwl8k_init_txqs(hw); |
5953 | if (!rc) |
5954 | rc = mwl8k_cmd_set_hw_spec(hw); |
5955 | } else { |
5956 | rc = mwl8k_cmd_get_hw_spec_sta(hw); |
5957 | } |
5958 | if (rc) { |
5959 | wiphy_err(hw->wiphy, "Cannot initialise firmware\n" ); |
5960 | goto err_free_irq; |
5961 | } |
5962 | |
5963 | /* Turn radio off */ |
5964 | rc = mwl8k_cmd_radio_disable(hw); |
5965 | if (rc) { |
5966 | wiphy_err(hw->wiphy, "Cannot disable\n" ); |
5967 | goto err_free_irq; |
5968 | } |
5969 | |
5970 | /* Clear MAC address */ |
5971 | rc = mwl8k_cmd_set_mac_addr(hw, NULL, mac: "\x00\x00\x00\x00\x00\x00" ); |
5972 | if (rc) { |
5973 | wiphy_err(hw->wiphy, "Cannot clear MAC address\n" ); |
5974 | goto err_free_irq; |
5975 | } |
5976 | |
5977 | /* Configure Antennas */ |
5978 | rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, mask: 0x3); |
5979 | if (rc) |
5980 | wiphy_warn(hw->wiphy, "failed to set # of RX antennas" ); |
5981 | rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, mask: 0x7); |
5982 | if (rc) |
5983 | wiphy_warn(hw->wiphy, "failed to set # of TX antennas" ); |
5984 | |
5985 | |
5986 | /* Disable interrupts */ |
5987 | iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); |
5988 | free_irq(priv->pdev->irq, hw); |
5989 | |
5990 | wiphy_info(hw->wiphy, "%s v%d, %pm, %s firmware %u.%u.%u.%u\n" , |
5991 | priv->device_info->part_name, |
5992 | priv->hw_rev, hw->wiphy->perm_addr, |
5993 | priv->ap_fw ? "AP" : "STA" , |
5994 | (priv->fw_rev >> 24) & 0xff, (priv->fw_rev >> 16) & 0xff, |
5995 | (priv->fw_rev >> 8) & 0xff, priv->fw_rev & 0xff); |
5996 | |
5997 | return 0; |
5998 | |
5999 | err_free_irq: |
6000 | iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); |
6001 | free_irq(priv->pdev->irq, hw); |
6002 | |
6003 | err_free_queues: |
6004 | for (i = 0; i < mwl8k_tx_queues(priv); i++) |
6005 | mwl8k_txq_deinit(hw, index: i); |
6006 | mwl8k_rxq_deinit(hw, index: 0); |
6007 | |
6008 | err_stop_firmware: |
6009 | mwl8k_hw_reset(priv); |
6010 | |
6011 | return rc; |
6012 | } |
6013 | |
6014 | /* |
6015 | * invoke mwl8k_reload_firmware to change the firmware image after the device |
6016 | * has already been registered |
6017 | */ |
6018 | static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image) |
6019 | { |
6020 | int i, rc = 0; |
6021 | struct mwl8k_priv *priv = hw->priv; |
6022 | struct mwl8k_vif *vif, *tmp_vif; |
6023 | |
6024 | mwl8k_stop(hw); |
6025 | mwl8k_rxq_deinit(hw, index: 0); |
6026 | |
6027 | /* |
6028 | * All the existing interfaces are re-added by the ieee80211_reconfig; |
6029 | * which means driver should remove existing interfaces before calling |
6030 | * ieee80211_restart_hw |
6031 | */ |
6032 | if (priv->hw_restart_in_progress) |
6033 | list_for_each_entry_safe(vif, tmp_vif, &priv->vif_list, list) |
6034 | mwl8k_remove_vif(priv, vif); |
6035 | |
6036 | for (i = 0; i < mwl8k_tx_queues(priv); i++) |
6037 | mwl8k_txq_deinit(hw, index: i); |
6038 | |
6039 | rc = mwl8k_init_firmware(hw, fw_image, nowait: false); |
6040 | if (rc) |
6041 | goto fail; |
6042 | |
6043 | rc = mwl8k_probe_hw(hw); |
6044 | if (rc) |
6045 | goto fail; |
6046 | |
6047 | if (priv->hw_restart_in_progress) |
6048 | return rc; |
6049 | |
6050 | rc = mwl8k_start(hw); |
6051 | if (rc) |
6052 | goto fail; |
6053 | |
6054 | rc = mwl8k_config(hw, changed: ~0); |
6055 | if (rc) |
6056 | goto fail; |
6057 | |
6058 | for (i = 0; i < MWL8K_TX_WMM_QUEUES; i++) { |
6059 | rc = mwl8k_conf_tx(hw, NULL, link_id: 0, queue: i, params: &priv->wmm_params[i]); |
6060 | if (rc) |
6061 | goto fail; |
6062 | } |
6063 | |
6064 | return rc; |
6065 | |
6066 | fail: |
6067 | printk(KERN_WARNING "mwl8k: Failed to reload firmware image.\n" ); |
6068 | return rc; |
6069 | } |
6070 | |
6071 | static const struct ieee80211_iface_limit ap_if_limits[] = { |
6072 | { .max = 8, .types = BIT(NL80211_IFTYPE_AP) }, |
6073 | { .max = 1, .types = BIT(NL80211_IFTYPE_STATION) }, |
6074 | }; |
6075 | |
6076 | static const struct ieee80211_iface_combination ap_if_comb = { |
6077 | .limits = ap_if_limits, |
6078 | .n_limits = ARRAY_SIZE(ap_if_limits), |
6079 | .max_interfaces = 8, |
6080 | .num_different_channels = 1, |
6081 | }; |
6082 | |
6083 | |
6084 | static int mwl8k_firmware_load_success(struct mwl8k_priv *priv) |
6085 | { |
6086 | struct ieee80211_hw *hw = priv->hw; |
6087 | int i, rc; |
6088 | |
6089 | rc = mwl8k_load_firmware(hw); |
6090 | mwl8k_release_firmware(priv); |
6091 | if (rc) { |
6092 | wiphy_err(hw->wiphy, "Cannot start firmware\n" ); |
6093 | return rc; |
6094 | } |
6095 | |
6096 | /* |
6097 | * Extra headroom is the size of the required DMA header |
6098 | * minus the size of the smallest 802.11 frame (CTS frame). |
6099 | */ |
6100 | hw->extra_tx_headroom = |
6101 | sizeof(struct mwl8k_dma_data) - sizeof(struct ieee80211_cts); |
6102 | |
6103 | hw->extra_tx_headroom -= priv->ap_fw ? REDUCED_TX_HEADROOM : 0; |
6104 | |
6105 | hw->queues = MWL8K_TX_WMM_QUEUES; |
6106 | |
6107 | /* Set rssi values to dBm */ |
6108 | ieee80211_hw_set(hw, SIGNAL_DBM); |
6109 | ieee80211_hw_set(hw, HAS_RATE_CONTROL); |
6110 | |
6111 | /* |
6112 | * Ask mac80211 to not to trigger PS mode |
6113 | * based on PM bit of incoming frames. |
6114 | */ |
6115 | if (priv->ap_fw) |
6116 | ieee80211_hw_set(hw, AP_LINK_PS); |
6117 | |
6118 | hw->vif_data_size = sizeof(struct mwl8k_vif); |
6119 | hw->sta_data_size = sizeof(struct mwl8k_sta); |
6120 | |
6121 | priv->macids_used = 0; |
6122 | INIT_LIST_HEAD(list: &priv->vif_list); |
6123 | |
6124 | /* Set default radio state and preamble */ |
6125 | priv->radio_on = false; |
6126 | priv->radio_short_preamble = false; |
6127 | |
6128 | /* Finalize join worker */ |
6129 | INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker); |
6130 | /* Handle watchdog ba events */ |
6131 | INIT_WORK(&priv->watchdog_ba_handle, mwl8k_watchdog_ba_events); |
6132 | /* To reload the firmware if it crashes */ |
6133 | INIT_WORK(&priv->fw_reload, mwl8k_hw_restart_work); |
6134 | |
6135 | /* TX reclaim and RX tasklets. */ |
6136 | tasklet_setup(t: &priv->poll_tx_task, callback: mwl8k_tx_poll); |
6137 | tasklet_disable(t: &priv->poll_tx_task); |
6138 | tasklet_setup(t: &priv->poll_rx_task, callback: mwl8k_rx_poll); |
6139 | tasklet_disable(t: &priv->poll_rx_task); |
6140 | |
6141 | /* Power management cookie */ |
6142 | priv->cookie = dma_alloc_coherent(dev: &priv->pdev->dev, size: 4, |
6143 | dma_handle: &priv->cookie_dma, GFP_KERNEL); |
6144 | if (priv->cookie == NULL) |
6145 | return -ENOMEM; |
6146 | |
6147 | mutex_init(&priv->fw_mutex); |
6148 | priv->fw_mutex_owner = NULL; |
6149 | priv->fw_mutex_depth = 0; |
6150 | priv->hostcmd_wait = NULL; |
6151 | |
6152 | spin_lock_init(&priv->tx_lock); |
6153 | |
6154 | spin_lock_init(&priv->stream_lock); |
6155 | |
6156 | priv->tx_wait = NULL; |
6157 | |
6158 | rc = mwl8k_probe_hw(hw); |
6159 | if (rc) |
6160 | goto err_free_cookie; |
6161 | |
6162 | hw->wiphy->interface_modes = 0; |
6163 | |
6164 | if (priv->ap_macids_supported || priv->device_info->fw_image_ap) { |
6165 | hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP); |
6166 | hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); |
6167 | hw->wiphy->iface_combinations = &ap_if_comb; |
6168 | hw->wiphy->n_iface_combinations = 1; |
6169 | } |
6170 | |
6171 | if (priv->sta_macids_supported || priv->device_info->fw_image_sta) |
6172 | hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); |
6173 | |
6174 | wiphy_ext_feature_set(wiphy: hw->wiphy, ftidx: NL80211_EXT_FEATURE_CQM_RSSI_LIST); |
6175 | |
6176 | rc = ieee80211_register_hw(hw); |
6177 | if (rc) { |
6178 | wiphy_err(hw->wiphy, "Cannot register device\n" ); |
6179 | goto err_unprobe_hw; |
6180 | } |
6181 | |
6182 | return 0; |
6183 | |
6184 | err_unprobe_hw: |
6185 | for (i = 0; i < mwl8k_tx_queues(priv); i++) |
6186 | mwl8k_txq_deinit(hw, index: i); |
6187 | mwl8k_rxq_deinit(hw, index: 0); |
6188 | |
6189 | err_free_cookie: |
6190 | if (priv->cookie != NULL) |
6191 | dma_free_coherent(dev: &priv->pdev->dev, size: 4, cpu_addr: priv->cookie, |
6192 | dma_handle: priv->cookie_dma); |
6193 | |
6194 | return rc; |
6195 | } |
6196 | static int mwl8k_probe(struct pci_dev *pdev, |
6197 | const struct pci_device_id *id) |
6198 | { |
6199 | static int printed_version; |
6200 | struct ieee80211_hw *hw; |
6201 | struct mwl8k_priv *priv; |
6202 | struct mwl8k_device_info *di; |
6203 | int rc; |
6204 | |
6205 | if (!printed_version) { |
6206 | printk(KERN_INFO "%s version %s\n" , MWL8K_DESC, MWL8K_VERSION); |
6207 | printed_version = 1; |
6208 | } |
6209 | |
6210 | |
6211 | rc = pci_enable_device(dev: pdev); |
6212 | if (rc) { |
6213 | printk(KERN_ERR "%s: Cannot enable new PCI device\n" , |
6214 | MWL8K_NAME); |
6215 | return rc; |
6216 | } |
6217 | |
6218 | rc = pci_request_regions(pdev, MWL8K_NAME); |
6219 | if (rc) { |
6220 | printk(KERN_ERR "%s: Cannot obtain PCI resources\n" , |
6221 | MWL8K_NAME); |
6222 | goto err_disable_device; |
6223 | } |
6224 | |
6225 | pci_set_master(dev: pdev); |
6226 | |
6227 | |
6228 | hw = ieee80211_alloc_hw(priv_data_len: sizeof(*priv), ops: &mwl8k_ops); |
6229 | if (hw == NULL) { |
6230 | printk(KERN_ERR "%s: ieee80211 alloc failed\n" , MWL8K_NAME); |
6231 | rc = -ENOMEM; |
6232 | goto err_free_reg; |
6233 | } |
6234 | |
6235 | SET_IEEE80211_DEV(hw, dev: &pdev->dev); |
6236 | pci_set_drvdata(pdev, data: hw); |
6237 | |
6238 | priv = hw->priv; |
6239 | priv->hw = hw; |
6240 | priv->pdev = pdev; |
6241 | priv->device_info = &mwl8k_info_tbl[id->driver_data]; |
6242 | |
6243 | if (id->driver_data == MWL8764) |
6244 | priv->is_8764 = true; |
6245 | |
6246 | priv->sram = pci_iomap(dev: pdev, bar: 0, max: 0x10000); |
6247 | if (priv->sram == NULL) { |
6248 | wiphy_err(hw->wiphy, "Cannot map device SRAM\n" ); |
6249 | rc = -EIO; |
6250 | goto err_iounmap; |
6251 | } |
6252 | |
6253 | /* |
6254 | * If BAR0 is a 32 bit BAR, the register BAR will be BAR1. |
6255 | * If BAR0 is a 64 bit BAR, the register BAR will be BAR2. |
6256 | */ |
6257 | priv->regs = pci_iomap(dev: pdev, bar: 1, max: 0x10000); |
6258 | if (priv->regs == NULL) { |
6259 | priv->regs = pci_iomap(dev: pdev, bar: 2, max: 0x10000); |
6260 | if (priv->regs == NULL) { |
6261 | wiphy_err(hw->wiphy, "Cannot map device registers\n" ); |
6262 | rc = -EIO; |
6263 | goto err_iounmap; |
6264 | } |
6265 | } |
6266 | |
6267 | /* |
6268 | * Choose the initial fw image depending on user input. If a second |
6269 | * image is available, make it the alternative image that will be |
6270 | * loaded if the first one fails. |
6271 | */ |
6272 | init_completion(x: &priv->firmware_loading_complete); |
6273 | di = priv->device_info; |
6274 | if (ap_mode_default && di->fw_image_ap) { |
6275 | priv->fw_pref = di->fw_image_ap; |
6276 | priv->fw_alt = di->fw_image_sta; |
6277 | } else if (!ap_mode_default && di->fw_image_sta) { |
6278 | priv->fw_pref = di->fw_image_sta; |
6279 | priv->fw_alt = di->fw_image_ap; |
6280 | } else if (ap_mode_default && !di->fw_image_ap && di->fw_image_sta) { |
6281 | printk(KERN_WARNING "AP fw is unavailable. Using STA fw." ); |
6282 | priv->fw_pref = di->fw_image_sta; |
6283 | } else if (!ap_mode_default && !di->fw_image_sta && di->fw_image_ap) { |
6284 | printk(KERN_WARNING "STA fw is unavailable. Using AP fw." ); |
6285 | priv->fw_pref = di->fw_image_ap; |
6286 | } |
6287 | rc = mwl8k_init_firmware(hw, fw_image: priv->fw_pref, nowait: true); |
6288 | if (rc) |
6289 | goto err_stop_firmware; |
6290 | |
6291 | priv->hw_restart_in_progress = false; |
6292 | |
6293 | priv->running_bsses = 0; |
6294 | |
6295 | return rc; |
6296 | |
6297 | err_stop_firmware: |
6298 | mwl8k_hw_reset(priv); |
6299 | |
6300 | err_iounmap: |
6301 | if (priv->regs != NULL) |
6302 | pci_iounmap(dev: pdev, priv->regs); |
6303 | |
6304 | if (priv->sram != NULL) |
6305 | pci_iounmap(dev: pdev, priv->sram); |
6306 | |
6307 | ieee80211_free_hw(hw); |
6308 | |
6309 | err_free_reg: |
6310 | pci_release_regions(pdev); |
6311 | |
6312 | err_disable_device: |
6313 | pci_disable_device(dev: pdev); |
6314 | |
6315 | return rc; |
6316 | } |
6317 | |
6318 | static void mwl8k_remove(struct pci_dev *pdev) |
6319 | { |
6320 | struct ieee80211_hw *hw = pci_get_drvdata(pdev); |
6321 | struct mwl8k_priv *priv; |
6322 | int i; |
6323 | |
6324 | if (hw == NULL) |
6325 | return; |
6326 | priv = hw->priv; |
6327 | |
6328 | wait_for_completion(&priv->firmware_loading_complete); |
6329 | |
6330 | if (priv->fw_state == FW_STATE_ERROR) { |
6331 | mwl8k_hw_reset(priv); |
6332 | goto unmap; |
6333 | } |
6334 | |
6335 | ieee80211_stop_queues(hw); |
6336 | |
6337 | ieee80211_unregister_hw(hw); |
6338 | |
6339 | /* Remove TX reclaim and RX tasklets. */ |
6340 | tasklet_kill(t: &priv->poll_tx_task); |
6341 | tasklet_kill(t: &priv->poll_rx_task); |
6342 | |
6343 | /* Stop hardware */ |
6344 | mwl8k_hw_reset(priv); |
6345 | |
6346 | /* Return all skbs to mac80211 */ |
6347 | for (i = 0; i < mwl8k_tx_queues(priv); i++) |
6348 | mwl8k_txq_reclaim(hw, index: i, INT_MAX, force: 1); |
6349 | |
6350 | for (i = 0; i < mwl8k_tx_queues(priv); i++) |
6351 | mwl8k_txq_deinit(hw, index: i); |
6352 | |
6353 | mwl8k_rxq_deinit(hw, index: 0); |
6354 | |
6355 | dma_free_coherent(dev: &priv->pdev->dev, size: 4, cpu_addr: priv->cookie, dma_handle: priv->cookie_dma); |
6356 | |
6357 | unmap: |
6358 | pci_iounmap(dev: pdev, priv->regs); |
6359 | pci_iounmap(dev: pdev, priv->sram); |
6360 | ieee80211_free_hw(hw); |
6361 | pci_release_regions(pdev); |
6362 | pci_disable_device(dev: pdev); |
6363 | } |
6364 | |
6365 | static struct pci_driver mwl8k_driver = { |
6366 | .name = MWL8K_NAME, |
6367 | .id_table = mwl8k_pci_id_table, |
6368 | .probe = mwl8k_probe, |
6369 | .remove = mwl8k_remove, |
6370 | }; |
6371 | |
6372 | module_pci_driver(mwl8k_driver); |
6373 | |
6374 | MODULE_DESCRIPTION(MWL8K_DESC); |
6375 | MODULE_VERSION(MWL8K_VERSION); |
6376 | MODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com>" ); |
6377 | MODULE_LICENSE("GPL" ); |
6378 | |