blob: 53581ee8b79fd2cce6b6e3ac174f773493823c3f [file] [log] [blame]
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001/*
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02002 * drivers/net/wireless/mwl8k.c
3 * Driver for Marvell TOPDOG 802.11 Wireless cards
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004 *
Lennert Buytenheka5fb2972010-01-12 13:51:38 +01005 * Copyright (C) 2008, 2009, 2010 Marvell Semiconductor Inc.
Lennert Buytenheka66098d2009-03-10 10:13:33 +01006 *
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/init.h>
13#include <linux/module.h>
14#include <linux/kernel.h>
Lennert Buytenhek3d76e822009-10-22 20:20:16 +020015#include <linux/sched.h>
Lennert Buytenheka66098d2009-03-10 10:13:33 +010016#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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090022#include <linux/slab.h>
Lennert Buytenheka66098d2009-03-10 10:13:33 +010023#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
Lennert Buytenheka5fb2972010-01-12 13:51:38 +010030#define MWL8K_VERSION "0.12"
Lennert Buytenheka66098d2009-03-10 10:13:33 +010031
Brian Cavagnolo0863ade2010-11-12 17:23:50 -080032/* Module parameters */
33static unsigned ap_mode_default;
34module_param(ap_mode_default, bool, 0);
35MODULE_PARM_DESC(ap_mode_default,
36 "Set to 1 to make ap mode the default instead of sta mode");
37
Lennert Buytenheka66098d2009-03-10 10:13:33 +010038/* Register definitions */
39#define MWL8K_HIU_GEN_PTR 0x00000c10
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +020040#define MWL8K_MODE_STA 0x0000005a
41#define MWL8K_MODE_AP 0x000000a5
Lennert Buytenheka66098d2009-03-10 10:13:33 +010042#define MWL8K_HIU_INT_CODE 0x00000c14
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +020043#define MWL8K_FWSTA_READY 0xf0f1f2f4
44#define MWL8K_FWAP_READY 0xf1f2f4a5
45#define MWL8K_INT_CODE_CMD_FINISHED 0x00000005
Lennert Buytenheka66098d2009-03-10 10:13:33 +010046#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
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +020054#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)
Lennert Buytenheka66098d2009-03-10 10:13:33 +010058
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
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +020065#define MWL8K_A2H_INT_DUMMY (1 << 20)
66#define MWL8K_A2H_INT_CHNL_SWITCHED (1 << 11)
67#define MWL8K_A2H_INT_QUEUE_EMPTY (1 << 10)
68#define MWL8K_A2H_INT_RADAR_DETECT (1 << 7)
69#define MWL8K_A2H_INT_RADIO_ON (1 << 6)
70#define MWL8K_A2H_INT_RADIO_OFF (1 << 5)
71#define MWL8K_A2H_INT_MAC_EVENT (1 << 3)
72#define MWL8K_A2H_INT_OPC_DONE (1 << 2)
73#define MWL8K_A2H_INT_RX_READY (1 << 1)
74#define MWL8K_A2H_INT_TX_DONE (1 << 0)
Lennert Buytenheka66098d2009-03-10 10:13:33 +010075
76#define MWL8K_A2H_EVENTS (MWL8K_A2H_INT_DUMMY | \
77 MWL8K_A2H_INT_CHNL_SWITCHED | \
78 MWL8K_A2H_INT_QUEUE_EMPTY | \
79 MWL8K_A2H_INT_RADAR_DETECT | \
80 MWL8K_A2H_INT_RADIO_ON | \
81 MWL8K_A2H_INT_RADIO_OFF | \
82 MWL8K_A2H_INT_MAC_EVENT | \
83 MWL8K_A2H_INT_OPC_DONE | \
84 MWL8K_A2H_INT_RX_READY | \
85 MWL8K_A2H_INT_TX_DONE)
86
Lennert Buytenheka66098d2009-03-10 10:13:33 +010087#define MWL8K_RX_QUEUES 1
Brian Cavagnoloe6007072011-03-17 11:58:44 -070088#define MWL8K_TX_WMM_QUEUES 4
Brian Cavagnolo8a7a5782011-03-17 11:58:42 -070089#define MWL8K_MAX_AMPDU_QUEUES 8
Brian Cavagnoloe6007072011-03-17 11:58:44 -070090#define MWL8K_MAX_TX_QUEUES (MWL8K_TX_WMM_QUEUES + MWL8K_MAX_AMPDU_QUEUES)
91#define mwl8k_tx_queues(priv) (MWL8K_TX_WMM_QUEUES + (priv)->num_ampdu_queues)
Lennert Buytenheka66098d2009-03-10 10:13:33 +010092
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +020093struct rxd_ops {
94 int rxd_size;
95 void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr);
96 void (*rxd_refill)(void *rxd, dma_addr_t addr, int len);
Lennert Buytenhek20f09c32009-11-30 18:12:08 +010097 int (*rxd_process)(void *rxd, struct ieee80211_rx_status *status,
John W. Linville0d462bb2010-07-28 14:04:24 -040098 __le16 *qos, s8 *noise);
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +020099};
100
Lennert Buytenhek45a390d2009-10-22 20:20:47 +0200101struct mwl8k_device_info {
Lennert Buytenheka74b2952009-10-22 20:20:50 +0200102 char *part_name;
103 char *helper_image;
Brian Cavagnolo0863ade2010-11-12 17:23:50 -0800104 char *fw_image_sta;
105 char *fw_image_ap;
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100106 struct rxd_ops *ap_rxd_ops;
Brian Cavagnolo952a0e92010-11-12 17:23:51 -0800107 u32 fw_api_ap;
Lennert Buytenhek45a390d2009-10-22 20:20:47 +0200108};
109
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100110struct mwl8k_rx_queue {
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200111 int rxd_count;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100112
113 /* hw receives here */
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200114 int head;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100115
116 /* refill descs here */
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200117 int tail;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100118
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200119 void *rxd;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200120 dma_addr_t rxd_dma;
Lennert Buytenhek788838e2009-10-22 20:20:56 +0200121 struct {
122 struct sk_buff *skb;
FUJITA Tomonori53b1b3e2010-04-02 13:10:38 +0900123 DEFINE_DMA_UNMAP_ADDR(dma);
Lennert Buytenhek788838e2009-10-22 20:20:56 +0200124 } *buf;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100125};
126
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100127struct mwl8k_tx_queue {
128 /* hw transmits here */
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200129 int head;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100130
131 /* sw appends here */
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200132 int tail;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100133
Kalle Valo8ccbc3b2010-02-07 10:20:52 +0200134 unsigned int len;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200135 struct mwl8k_tx_desc *txd;
136 dma_addr_t txd_dma;
137 struct sk_buff **skb;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100138};
139
Nishant Sarmukadam5faa1af2011-03-17 11:58:43 -0700140struct mwl8k_ampdu_stream {
141 struct ieee80211_sta *sta;
142 u8 tid;
143 u8 state;
144 u8 idx;
145 u8 txq_idx; /* index of this stream in priv->txq */
146};
147
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100148struct mwl8k_priv {
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100149 struct ieee80211_hw *hw;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100150 struct pci_dev *pdev;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100151
Lennert Buytenhek45a390d2009-10-22 20:20:47 +0200152 struct mwl8k_device_info *device_info;
153
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +0100154 void __iomem *sram;
155 void __iomem *regs;
156
157 /* firmware */
Brian Cavagnolod1f9e412010-11-12 17:23:53 -0800158 const struct firmware *fw_helper;
159 const struct firmware *fw_ucode;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100160
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +0100161 /* hardware/firmware parameters */
162 bool ap_fw;
163 struct rxd_ops *rxd_ops;
Lennert Buytenhek777ad372010-01-12 13:48:04 +0100164 struct ieee80211_supported_band band_24;
165 struct ieee80211_channel channels_24[14];
166 struct ieee80211_rate rates_24[14];
Lennert Buytenhek4eae9ed2010-01-12 13:48:32 +0100167 struct ieee80211_supported_band band_50;
168 struct ieee80211_channel channels_50[4];
169 struct ieee80211_rate rates_50[9];
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +0100170 u32 ap_macids_supported;
171 u32 sta_macids_supported;
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +0100172
Brian Cavagnolo8a7a5782011-03-17 11:58:42 -0700173 /* Ampdu stream information */
174 u8 num_ampdu_queues;
175
Lennert Buytenhek618952a2009-08-18 03:18:01 +0200176 /* firmware access */
177 struct mutex fw_mutex;
178 struct task_struct *fw_mutex_owner;
179 int fw_mutex_depth;
Lennert Buytenhek618952a2009-08-18 03:18:01 +0200180 struct completion *hostcmd_wait;
181
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100182 /* lock held over TX and TX reap */
183 spinlock_t tx_lock;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100184
Lennert Buytenhek88de754a2009-10-22 20:19:37 +0200185 /* TX quiesce completion, protected by fw_mutex and tx_lock */
186 struct completion *tx_wait;
187
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +0100188 /* List of interfaces. */
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +0100189 u32 macids_used;
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +0100190 struct list_head vif_list;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100191
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100192 /* power management status cookie from firmware */
193 u32 *cookie;
194 dma_addr_t cookie_dma;
195
196 u16 num_mcaddrs;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100197 u8 hw_rev;
Lennert Buytenhek2aa7b012009-08-24 15:48:07 +0200198 u32 fw_rev;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100199
200 /*
201 * Running count of TX packets in flight, to avoid
202 * iterating over the transmit rings each time.
203 */
204 int pending_tx_pkts;
205
206 struct mwl8k_rx_queue rxq[MWL8K_RX_QUEUES];
Brian Cavagnoloe6007072011-03-17 11:58:44 -0700207 struct mwl8k_tx_queue txq[MWL8K_MAX_TX_QUEUES];
208 u32 txq_offset[MWL8K_MAX_TX_QUEUES];
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100209
Lennert Buytenhekc46563b2009-07-16 12:14:58 +0200210 bool radio_on;
Lennert Buytenhek68ce3882009-07-16 12:26:57 +0200211 bool radio_short_preamble;
Lennert Buytenheka43c49a2009-10-22 20:20:32 +0200212 bool sniffer_enabled;
Lennert Buytenhek0439b1f2009-07-16 12:34:02 +0200213 bool wmm_enabled;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100214
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100215 /* XXX need to convert this to handle multiple interfaces */
216 bool capture_beacon;
Lennert Buytenhekd89173f2009-07-16 09:54:27 +0200217 u8 capture_bssid[ETH_ALEN];
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100218 struct sk_buff *beacon_skb;
219
220 /*
221 * This FJ worker has to be global as it is scheduled from the
222 * RX handler. At this point we don't know which interface it
223 * belongs to until the list of bssids waiting to complete join
224 * is checked.
225 */
226 struct work_struct finalize_join_worker;
227
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +0100228 /* Tasklet to perform TX reclaim. */
229 struct tasklet_struct poll_tx_task;
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +0100230
231 /* Tasklet to perform RX. */
232 struct tasklet_struct poll_rx_task;
John W. Linville0d462bb2010-07-28 14:04:24 -0400233
234 /* Most recently reported noise in dBm */
235 s8 noise;
Brian Cavagnolo0863ade2010-11-12 17:23:50 -0800236
237 /*
238 * preserve the queue configurations so they can be restored if/when
239 * the firmware image is swapped.
240 */
Brian Cavagnoloe6007072011-03-17 11:58:44 -0700241 struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_WMM_QUEUES];
Brian Cavagnolo99020472010-11-12 17:23:52 -0800242
243 /* async firmware loading state */
244 unsigned fw_state;
245 char *fw_pref;
246 char *fw_alt;
247 struct completion firmware_loading_complete;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100248};
249
Nishant Sarmukadame53d9b92010-12-30 11:23:32 -0800250#define MAX_WEP_KEY_LEN 13
251#define NUM_WEP_KEYS 4
252
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100253/* Per interface specific private data */
254struct mwl8k_vif {
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +0100255 struct list_head list;
256 struct ieee80211_vif *vif;
257
Lennert Buytenhekf57ca9c2010-01-12 13:50:14 +0100258 /* Firmware macid for this vif. */
259 int macid;
260
Lennert Buytenhekc2c2b122010-01-08 18:27:59 +0100261 /* Non AMPDU sequence number assigned by driver. */
Lennert Buytenheka6804002010-01-04 21:55:42 +0100262 u16 seqno;
Nishant Sarmukadame53d9b92010-12-30 11:23:32 -0800263
264 /* Saved WEP keys */
265 struct {
266 u8 enabled;
267 u8 key[sizeof(struct ieee80211_key_conf) + MAX_WEP_KEY_LEN];
268 } wep_key_conf[NUM_WEP_KEYS];
Nishant Sarmukadamd9a07d42010-12-30 11:23:33 -0800269
270 /* BSSID */
271 u8 bssid[ETH_ALEN];
272
273 /* A flag to indicate is HW crypto is enabled for this bssid */
274 bool is_hw_crypto_enabled;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100275};
Lennert Buytenheka94cc972009-08-03 21:58:57 +0200276#define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv))
Nishant Sarmukadamfcdc4032010-12-30 11:23:34 -0800277#define IEEE80211_KEY_CONF(_u8) ((struct ieee80211_key_conf *)(_u8))
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100278
Lennert Buytenheka6804002010-01-04 21:55:42 +0100279struct mwl8k_sta {
280 /* Index into station database. Returned by UPDATE_STADB. */
281 u8 peer_id;
282};
283#define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv))
284
Lennert Buytenhek777ad372010-01-12 13:48:04 +0100285static const struct ieee80211_channel mwl8k_channels_24[] = {
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100286 { .center_freq = 2412, .hw_value = 1, },
287 { .center_freq = 2417, .hw_value = 2, },
288 { .center_freq = 2422, .hw_value = 3, },
289 { .center_freq = 2427, .hw_value = 4, },
290 { .center_freq = 2432, .hw_value = 5, },
291 { .center_freq = 2437, .hw_value = 6, },
292 { .center_freq = 2442, .hw_value = 7, },
293 { .center_freq = 2447, .hw_value = 8, },
294 { .center_freq = 2452, .hw_value = 9, },
295 { .center_freq = 2457, .hw_value = 10, },
296 { .center_freq = 2462, .hw_value = 11, },
Lennert Buytenhek647ca6b2009-11-30 18:32:20 +0100297 { .center_freq = 2467, .hw_value = 12, },
298 { .center_freq = 2472, .hw_value = 13, },
299 { .center_freq = 2484, .hw_value = 14, },
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100300};
301
Lennert Buytenhek777ad372010-01-12 13:48:04 +0100302static const struct ieee80211_rate mwl8k_rates_24[] = {
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100303 { .bitrate = 10, .hw_value = 2, },
304 { .bitrate = 20, .hw_value = 4, },
305 { .bitrate = 55, .hw_value = 11, },
Lennert Buytenhek5dfd3e22009-10-22 20:20:29 +0200306 { .bitrate = 110, .hw_value = 22, },
307 { .bitrate = 220, .hw_value = 44, },
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100308 { .bitrate = 60, .hw_value = 12, },
309 { .bitrate = 90, .hw_value = 18, },
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100310 { .bitrate = 120, .hw_value = 24, },
311 { .bitrate = 180, .hw_value = 36, },
312 { .bitrate = 240, .hw_value = 48, },
313 { .bitrate = 360, .hw_value = 72, },
314 { .bitrate = 480, .hw_value = 96, },
315 { .bitrate = 540, .hw_value = 108, },
Lennert Buytenhek140eb5e2009-11-30 18:11:44 +0100316 { .bitrate = 720, .hw_value = 144, },
317};
318
Lennert Buytenhek4eae9ed2010-01-12 13:48:32 +0100319static const struct ieee80211_channel mwl8k_channels_50[] = {
320 { .center_freq = 5180, .hw_value = 36, },
321 { .center_freq = 5200, .hw_value = 40, },
322 { .center_freq = 5220, .hw_value = 44, },
323 { .center_freq = 5240, .hw_value = 48, },
324};
325
326static const struct ieee80211_rate mwl8k_rates_50[] = {
327 { .bitrate = 60, .hw_value = 12, },
328 { .bitrate = 90, .hw_value = 18, },
329 { .bitrate = 120, .hw_value = 24, },
330 { .bitrate = 180, .hw_value = 36, },
331 { .bitrate = 240, .hw_value = 48, },
332 { .bitrate = 360, .hw_value = 72, },
333 { .bitrate = 480, .hw_value = 96, },
334 { .bitrate = 540, .hw_value = 108, },
335 { .bitrate = 720, .hw_value = 144, },
336};
337
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100338/* Set or get info from Firmware */
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100339#define MWL8K_CMD_GET 0x0000
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -0800340#define MWL8K_CMD_SET 0x0001
341#define MWL8K_CMD_SET_LIST 0x0002
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100342
343/* Firmware command codes */
344#define MWL8K_CMD_CODE_DNLD 0x0001
345#define MWL8K_CMD_GET_HW_SPEC 0x0003
Lennert Buytenhek42fba212009-10-22 20:21:30 +0200346#define MWL8K_CMD_SET_HW_SPEC 0x0004
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100347#define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010
348#define MWL8K_CMD_GET_STAT 0x0014
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200349#define MWL8K_CMD_RADIO_CONTROL 0x001c
350#define MWL8K_CMD_RF_TX_POWER 0x001e
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -0800351#define MWL8K_CMD_TX_POWER 0x001f
Lennert Buytenhek08b06342009-10-22 20:21:33 +0200352#define MWL8K_CMD_RF_ANTENNA 0x0020
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +0100353#define MWL8K_CMD_SET_BEACON 0x0100 /* per-vif */
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100354#define MWL8K_CMD_SET_PRE_SCAN 0x0107
355#define MWL8K_CMD_SET_POST_SCAN 0x0108
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200356#define MWL8K_CMD_SET_RF_CHANNEL 0x010a
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100357#define MWL8K_CMD_SET_AID 0x010d
358#define MWL8K_CMD_SET_RATE 0x0110
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200359#define MWL8K_CMD_SET_FINALIZE_JOIN 0x0111
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100360#define MWL8K_CMD_RTS_THRESHOLD 0x0113
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200361#define MWL8K_CMD_SET_SLOT 0x0114
362#define MWL8K_CMD_SET_EDCA_PARAMS 0x0115
363#define MWL8K_CMD_SET_WMM_MODE 0x0123
364#define MWL8K_CMD_MIMO_CONFIG 0x0125
365#define MWL8K_CMD_USE_FIXED_RATE 0x0126
366#define MWL8K_CMD_ENABLE_SNIFFER 0x0150
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +0100367#define MWL8K_CMD_SET_MAC_ADDR 0x0202 /* per-vif */
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200368#define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +0100369#define MWL8K_CMD_BSS_START 0x1100 /* per-vif */
370#define MWL8K_CMD_SET_NEW_STN 0x1111 /* per-vif */
Nishant Sarmukadamfcdc4032010-12-30 11:23:34 -0800371#define MWL8K_CMD_UPDATE_ENCRYPTION 0x1122 /* per-vif */
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200372#define MWL8K_CMD_UPDATE_STADB 0x1123
Nishant Sarmukadam5faa1af2011-03-17 11:58:43 -0700373#define MWL8K_CMD_BASTREAM 0x1125
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100374
John W. Linvilleb6037422010-07-20 13:55:00 -0400375static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize)
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100376{
John W. Linvilleb6037422010-07-20 13:55:00 -0400377 u16 command = le16_to_cpu(cmd);
378
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100379#define MWL8K_CMDNAME(x) case MWL8K_CMD_##x: do {\
380 snprintf(buf, bufsize, "%s", #x);\
381 return buf;\
382 } while (0)
John W. Linvilleb6037422010-07-20 13:55:00 -0400383 switch (command & ~0x8000) {
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100384 MWL8K_CMDNAME(CODE_DNLD);
385 MWL8K_CMDNAME(GET_HW_SPEC);
Lennert Buytenhek42fba212009-10-22 20:21:30 +0200386 MWL8K_CMDNAME(SET_HW_SPEC);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100387 MWL8K_CMDNAME(MAC_MULTICAST_ADR);
388 MWL8K_CMDNAME(GET_STAT);
389 MWL8K_CMDNAME(RADIO_CONTROL);
390 MWL8K_CMDNAME(RF_TX_POWER);
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -0800391 MWL8K_CMDNAME(TX_POWER);
Lennert Buytenhek08b06342009-10-22 20:21:33 +0200392 MWL8K_CMDNAME(RF_ANTENNA);
Lennert Buytenhekb64fe612010-01-08 18:31:39 +0100393 MWL8K_CMDNAME(SET_BEACON);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100394 MWL8K_CMDNAME(SET_PRE_SCAN);
395 MWL8K_CMDNAME(SET_POST_SCAN);
396 MWL8K_CMDNAME(SET_RF_CHANNEL);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100397 MWL8K_CMDNAME(SET_AID);
398 MWL8K_CMDNAME(SET_RATE);
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200399 MWL8K_CMDNAME(SET_FINALIZE_JOIN);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100400 MWL8K_CMDNAME(RTS_THRESHOLD);
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200401 MWL8K_CMDNAME(SET_SLOT);
402 MWL8K_CMDNAME(SET_EDCA_PARAMS);
403 MWL8K_CMDNAME(SET_WMM_MODE);
404 MWL8K_CMDNAME(MIMO_CONFIG);
405 MWL8K_CMDNAME(USE_FIXED_RATE);
406 MWL8K_CMDNAME(ENABLE_SNIFFER);
Lennert Buytenhek32060e12009-10-22 20:20:04 +0200407 MWL8K_CMDNAME(SET_MAC_ADDR);
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200408 MWL8K_CMDNAME(SET_RATEADAPT_MODE);
Lennert Buytenhekb64fe612010-01-08 18:31:39 +0100409 MWL8K_CMDNAME(BSS_START);
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +0100410 MWL8K_CMDNAME(SET_NEW_STN);
Nishant Sarmukadamfcdc4032010-12-30 11:23:34 -0800411 MWL8K_CMDNAME(UPDATE_ENCRYPTION);
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200412 MWL8K_CMDNAME(UPDATE_STADB);
Nishant Sarmukadam5faa1af2011-03-17 11:58:43 -0700413 MWL8K_CMDNAME(BASTREAM);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100414 default:
415 snprintf(buf, bufsize, "0x%x", cmd);
416 }
417#undef MWL8K_CMDNAME
418
419 return buf;
420}
421
422/* Hardware and firmware reset */
423static void mwl8k_hw_reset(struct mwl8k_priv *priv)
424{
425 iowrite32(MWL8K_H2A_INT_RESET,
426 priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
427 iowrite32(MWL8K_H2A_INT_RESET,
428 priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
429 msleep(20);
430}
431
432/* Release fw image */
Brian Cavagnolod1f9e412010-11-12 17:23:53 -0800433static void mwl8k_release_fw(const struct firmware **fw)
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100434{
435 if (*fw == NULL)
436 return;
437 release_firmware(*fw);
438 *fw = NULL;
439}
440
441static void mwl8k_release_firmware(struct mwl8k_priv *priv)
442{
Lennert Buytenhek22be40d2009-11-30 18:32:38 +0100443 mwl8k_release_fw(&priv->fw_ucode);
444 mwl8k_release_fw(&priv->fw_helper);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100445}
446
Brian Cavagnolo99020472010-11-12 17:23:52 -0800447/* states for asynchronous f/w loading */
448static void mwl8k_fw_state_machine(const struct firmware *fw, void *context);
449enum {
450 FW_STATE_INIT = 0,
451 FW_STATE_LOADING_PREF,
452 FW_STATE_LOADING_ALT,
453 FW_STATE_ERROR,
454};
455
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100456/* Request fw image */
457static int mwl8k_request_fw(struct mwl8k_priv *priv,
Brian Cavagnolod1f9e412010-11-12 17:23:53 -0800458 const char *fname, const struct firmware **fw,
Brian Cavagnolo99020472010-11-12 17:23:52 -0800459 bool nowait)
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100460{
461 /* release current image */
462 if (*fw != NULL)
463 mwl8k_release_fw(fw);
464
Brian Cavagnolo99020472010-11-12 17:23:52 -0800465 if (nowait)
466 return request_firmware_nowait(THIS_MODULE, 1, fname,
467 &priv->pdev->dev, GFP_KERNEL,
468 priv, mwl8k_fw_state_machine);
469 else
Brian Cavagnolod1f9e412010-11-12 17:23:53 -0800470 return request_firmware(fw, fname, &priv->pdev->dev);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100471}
472
Brian Cavagnolo99020472010-11-12 17:23:52 -0800473static int mwl8k_request_firmware(struct mwl8k_priv *priv, char *fw_image,
474 bool nowait)
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100475{
Lennert Buytenheka74b2952009-10-22 20:20:50 +0200476 struct mwl8k_device_info *di = priv->device_info;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100477 int rc;
478
Lennert Buytenheka74b2952009-10-22 20:20:50 +0200479 if (di->helper_image != NULL) {
Brian Cavagnolo99020472010-11-12 17:23:52 -0800480 if (nowait)
481 rc = mwl8k_request_fw(priv, di->helper_image,
482 &priv->fw_helper, true);
483 else
484 rc = mwl8k_request_fw(priv, di->helper_image,
485 &priv->fw_helper, false);
486 if (rc)
487 printk(KERN_ERR "%s: Error requesting helper fw %s\n",
488 pci_name(priv->pdev), di->helper_image);
489
490 if (rc || nowait)
Lennert Buytenheka74b2952009-10-22 20:20:50 +0200491 return rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100492 }
493
Brian Cavagnolo99020472010-11-12 17:23:52 -0800494 if (nowait) {
495 /*
496 * if we get here, no helper image is needed. Skip the
497 * FW_STATE_INIT state.
498 */
499 priv->fw_state = FW_STATE_LOADING_PREF;
500 rc = mwl8k_request_fw(priv, fw_image,
501 &priv->fw_ucode,
502 true);
503 } else
504 rc = mwl8k_request_fw(priv, fw_image,
505 &priv->fw_ucode, false);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100506 if (rc) {
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200507 printk(KERN_ERR "%s: Error requesting firmware file %s\n",
Brian Cavagnolo0863ade2010-11-12 17:23:50 -0800508 pci_name(priv->pdev), fw_image);
Lennert Buytenhek22be40d2009-11-30 18:32:38 +0100509 mwl8k_release_fw(&priv->fw_helper);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100510 return rc;
511 }
512
513 return 0;
514}
515
516struct mwl8k_cmd_pkt {
517 __le16 code;
518 __le16 length;
Lennert Buytenhekf57ca9c2010-01-12 13:50:14 +0100519 __u8 seq_num;
520 __u8 macid;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100521 __le16 result;
522 char payload[0];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000523} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100524
525/*
526 * Firmware loading.
527 */
528static int
529mwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length)
530{
531 void __iomem *regs = priv->regs;
532 dma_addr_t dma_addr;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100533 int loops;
534
535 dma_addr = pci_map_single(priv->pdev, data, length, PCI_DMA_TODEVICE);
536 if (pci_dma_mapping_error(priv->pdev, dma_addr))
537 return -ENOMEM;
538
539 iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR);
540 iowrite32(0, regs + MWL8K_HIU_INT_CODE);
541 iowrite32(MWL8K_H2A_INT_DOORBELL,
542 regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
543 iowrite32(MWL8K_H2A_INT_DUMMY,
544 regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
545
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100546 loops = 1000;
547 do {
548 u32 int_code;
549
550 int_code = ioread32(regs + MWL8K_HIU_INT_CODE);
551 if (int_code == MWL8K_INT_CODE_CMD_FINISHED) {
552 iowrite32(0, regs + MWL8K_HIU_INT_CODE);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100553 break;
554 }
555
Lennert Buytenhek3d76e822009-10-22 20:20:16 +0200556 cond_resched();
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100557 udelay(1);
558 } while (--loops);
559
560 pci_unmap_single(priv->pdev, dma_addr, length, PCI_DMA_TODEVICE);
561
Lennert Buytenhekd4b705702009-07-16 12:44:45 +0200562 return loops ? 0 : -ETIMEDOUT;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100563}
564
565static int mwl8k_load_fw_image(struct mwl8k_priv *priv,
566 const u8 *data, size_t length)
567{
568 struct mwl8k_cmd_pkt *cmd;
569 int done;
570 int rc = 0;
571
572 cmd = kmalloc(sizeof(*cmd) + 256, GFP_KERNEL);
573 if (cmd == NULL)
574 return -ENOMEM;
575
576 cmd->code = cpu_to_le16(MWL8K_CMD_CODE_DNLD);
577 cmd->seq_num = 0;
Lennert Buytenhekf57ca9c2010-01-12 13:50:14 +0100578 cmd->macid = 0;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100579 cmd->result = 0;
580
581 done = 0;
582 while (length) {
583 int block_size = length > 256 ? 256 : length;
584
585 memcpy(cmd->payload, data + done, block_size);
586 cmd->length = cpu_to_le16(block_size);
587
588 rc = mwl8k_send_fw_load_cmd(priv, cmd,
589 sizeof(*cmd) + block_size);
590 if (rc)
591 break;
592
593 done += block_size;
594 length -= block_size;
595 }
596
597 if (!rc) {
598 cmd->length = 0;
599 rc = mwl8k_send_fw_load_cmd(priv, cmd, sizeof(*cmd));
600 }
601
602 kfree(cmd);
603
604 return rc;
605}
606
607static int mwl8k_feed_fw_image(struct mwl8k_priv *priv,
608 const u8 *data, size_t length)
609{
610 unsigned char *buffer;
611 int may_continue, rc = 0;
612 u32 done, prev_block_size;
613
614 buffer = kmalloc(1024, GFP_KERNEL);
615 if (buffer == NULL)
616 return -ENOMEM;
617
618 done = 0;
619 prev_block_size = 0;
620 may_continue = 1000;
621 while (may_continue > 0) {
622 u32 block_size;
623
624 block_size = ioread32(priv->regs + MWL8K_HIU_SCRATCH);
625 if (block_size & 1) {
626 block_size &= ~1;
627 may_continue--;
628 } else {
629 done += prev_block_size;
630 length -= prev_block_size;
631 }
632
633 if (block_size > 1024 || block_size > length) {
634 rc = -EOVERFLOW;
635 break;
636 }
637
638 if (length == 0) {
639 rc = 0;
640 break;
641 }
642
643 if (block_size == 0) {
644 rc = -EPROTO;
645 may_continue--;
646 udelay(1);
647 continue;
648 }
649
650 prev_block_size = block_size;
651 memcpy(buffer, data + done, block_size);
652
653 rc = mwl8k_send_fw_load_cmd(priv, buffer, block_size);
654 if (rc)
655 break;
656 }
657
658 if (!rc && length != 0)
659 rc = -EREMOTEIO;
660
661 kfree(buffer);
662
663 return rc;
664}
665
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200666static int mwl8k_load_firmware(struct ieee80211_hw *hw)
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100667{
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200668 struct mwl8k_priv *priv = hw->priv;
Brian Cavagnolod1f9e412010-11-12 17:23:53 -0800669 const struct firmware *fw = priv->fw_ucode;
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200670 int rc;
671 int loops;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100672
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200673 if (!memcmp(fw->data, "\x01\x00\x00\x00", 4)) {
Brian Cavagnolod1f9e412010-11-12 17:23:53 -0800674 const struct firmware *helper = priv->fw_helper;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100675
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200676 if (helper == NULL) {
677 printk(KERN_ERR "%s: helper image needed but none "
678 "given\n", pci_name(priv->pdev));
679 return -EINVAL;
680 }
681
682 rc = mwl8k_load_fw_image(priv, helper->data, helper->size);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100683 if (rc) {
684 printk(KERN_ERR "%s: unable to load firmware "
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200685 "helper image\n", pci_name(priv->pdev));
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100686 return rc;
687 }
Lennert Buytenhek89b872e2009-11-30 18:13:20 +0100688 msleep(5);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100689
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200690 rc = mwl8k_feed_fw_image(priv, fw->data, fw->size);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100691 } else {
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200692 rc = mwl8k_load_fw_image(priv, fw->data, fw->size);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100693 }
694
695 if (rc) {
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200696 printk(KERN_ERR "%s: unable to load firmware image\n",
697 pci_name(priv->pdev));
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100698 return rc;
699 }
700
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100701 iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100702
Lennert Buytenhek89b872e2009-11-30 18:13:20 +0100703 loops = 500000;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100704 do {
Lennert Buytenhekeae74e62009-10-22 20:20:53 +0200705 u32 ready_code;
706
707 ready_code = ioread32(priv->regs + MWL8K_HIU_INT_CODE);
708 if (ready_code == MWL8K_FWAP_READY) {
709 priv->ap_fw = 1;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100710 break;
Lennert Buytenhekeae74e62009-10-22 20:20:53 +0200711 } else if (ready_code == MWL8K_FWSTA_READY) {
712 priv->ap_fw = 0;
713 break;
714 }
715
716 cond_resched();
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100717 udelay(1);
718 } while (--loops);
719
720 return loops ? 0 : -ETIMEDOUT;
721}
722
723
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100724/* DMA header used by firmware and hardware. */
725struct mwl8k_dma_data {
726 __le16 fwlen;
727 struct ieee80211_hdr wh;
Lennert Buytenhek20f09c32009-11-30 18:12:08 +0100728 char data[0];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000729} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100730
731/* Routines to add/remove DMA header from skb. */
Lennert Buytenhek20f09c32009-11-30 18:12:08 +0100732static inline void mwl8k_remove_dma_header(struct sk_buff *skb, __le16 qos)
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100733{
Lennert Buytenhek20f09c32009-11-30 18:12:08 +0100734 struct mwl8k_dma_data *tr;
735 int hdrlen;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100736
Lennert Buytenhek20f09c32009-11-30 18:12:08 +0100737 tr = (struct mwl8k_dma_data *)skb->data;
738 hdrlen = ieee80211_hdrlen(tr->wh.frame_control);
739
740 if (hdrlen != sizeof(tr->wh)) {
741 if (ieee80211_is_data_qos(tr->wh.frame_control)) {
742 memmove(tr->data - hdrlen, &tr->wh, hdrlen - 2);
743 *((__le16 *)(tr->data - 2)) = qos;
744 } else {
745 memmove(tr->data - hdrlen, &tr->wh, hdrlen);
746 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100747 }
Lennert Buytenhek20f09c32009-11-30 18:12:08 +0100748
749 if (hdrlen != sizeof(*tr))
750 skb_pull(skb, sizeof(*tr) - hdrlen);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100751}
752
Nishant Sarmukadam252486a2010-12-30 11:23:31 -0800753static void
754mwl8k_add_dma_header(struct sk_buff *skb, int tail_pad)
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100755{
756 struct ieee80211_hdr *wh;
Lennert Buytenhekca009302009-11-30 18:12:20 +0100757 int hdrlen;
Nishant Sarmukadam252486a2010-12-30 11:23:31 -0800758 int reqd_hdrlen;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100759 struct mwl8k_dma_data *tr;
760
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100761 /*
Lennert Buytenhekca009302009-11-30 18:12:20 +0100762 * Add a firmware DMA header; the firmware requires that we
763 * present a 2-byte payload length followed by a 4-address
764 * header (without QoS field), followed (optionally) by any
765 * WEP/ExtIV header (but only filled in for CCMP).
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100766 */
Lennert Buytenhekca009302009-11-30 18:12:20 +0100767 wh = (struct ieee80211_hdr *)skb->data;
768
769 hdrlen = ieee80211_hdrlen(wh->frame_control);
Nishant Sarmukadam252486a2010-12-30 11:23:31 -0800770 reqd_hdrlen = sizeof(*tr);
771
772 if (hdrlen != reqd_hdrlen)
773 skb_push(skb, reqd_hdrlen - hdrlen);
Lennert Buytenhekca009302009-11-30 18:12:20 +0100774
775 if (ieee80211_is_data_qos(wh->frame_control))
Nishant Sarmukadam252486a2010-12-30 11:23:31 -0800776 hdrlen -= IEEE80211_QOS_CTL_LEN;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100777
778 tr = (struct mwl8k_dma_data *)skb->data;
779 if (wh != &tr->wh)
780 memmove(&tr->wh, wh, hdrlen);
Lennert Buytenhekca009302009-11-30 18:12:20 +0100781 if (hdrlen != sizeof(tr->wh))
782 memset(((void *)&tr->wh) + hdrlen, 0, sizeof(tr->wh) - hdrlen);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100783
784 /*
785 * Firmware length is the length of the fully formed "802.11
786 * payload". That is, everything except for the 802.11 header.
787 * This includes all crypto material including the MIC.
788 */
Nishant Sarmukadam252486a2010-12-30 11:23:31 -0800789 tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr) + tail_pad);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100790}
791
Nishant Sarmukadame53d9b92010-12-30 11:23:32 -0800792static void mwl8k_encapsulate_tx_frame(struct sk_buff *skb)
793{
794 struct ieee80211_hdr *wh;
795 struct ieee80211_tx_info *tx_info;
796 struct ieee80211_key_conf *key_conf;
797 int data_pad;
798
799 wh = (struct ieee80211_hdr *)skb->data;
800
801 tx_info = IEEE80211_SKB_CB(skb);
802
803 key_conf = NULL;
804 if (ieee80211_is_data(wh->frame_control))
805 key_conf = tx_info->control.hw_key;
806
807 /*
808 * Make sure the packet header is in the DMA header format (4-address
809 * without QoS), the necessary crypto padding between the header and the
810 * payload has already been provided by mac80211, but it doesn't add tail
811 * padding when HW crypto is enabled.
812 *
813 * We have the following trailer padding requirements:
814 * - WEP: 4 trailer bytes (ICV)
815 * - TKIP: 12 trailer bytes (8 MIC + 4 ICV)
816 * - CCMP: 8 trailer bytes (MIC)
817 */
818 data_pad = 0;
819 if (key_conf != NULL) {
820 switch (key_conf->cipher) {
821 case WLAN_CIPHER_SUITE_WEP40:
822 case WLAN_CIPHER_SUITE_WEP104:
823 data_pad = 4;
824 break;
825 case WLAN_CIPHER_SUITE_TKIP:
826 data_pad = 12;
827 break;
828 case WLAN_CIPHER_SUITE_CCMP:
829 data_pad = 8;
830 break;
831 }
832 }
833 mwl8k_add_dma_header(skb, data_pad);
834}
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100835
836/*
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100837 * Packet reception for 88w8366 AP firmware.
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200838 */
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100839struct mwl8k_rxd_8366_ap {
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200840 __le16 pkt_len;
841 __u8 sq2;
842 __u8 rate;
843 __le32 pkt_phys_addr;
844 __le32 next_rxd_phys_addr;
845 __le16 qos_control;
846 __le16 htsig2;
847 __le32 hw_rssi_info;
848 __le32 hw_noise_floor_info;
849 __u8 noise_floor;
850 __u8 pad0[3];
851 __u8 rssi;
852 __u8 rx_status;
853 __u8 channel;
854 __u8 rx_ctrl;
Eric Dumazetba2d3582010-06-02 18:10:09 +0000855} __packed;
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200856
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100857#define MWL8K_8366_AP_RATE_INFO_MCS_FORMAT 0x80
858#define MWL8K_8366_AP_RATE_INFO_40MHZ 0x40
859#define MWL8K_8366_AP_RATE_INFO_RATEID(x) ((x) & 0x3f)
Lennert Buytenhek8e9f33f2009-11-30 18:12:35 +0100860
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100861#define MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST 0x80
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200862
Nishant Sarmukadamd9a07d42010-12-30 11:23:33 -0800863/* 8366 AP rx_status bits */
864#define MWL8K_8366_AP_RXSTAT_DECRYPT_ERR_MASK 0x80
865#define MWL8K_8366_AP_RXSTAT_GENERAL_DECRYPT_ERR 0xFF
866#define MWL8K_8366_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR 0x02
867#define MWL8K_8366_AP_RXSTAT_WEP_DECRYPT_ICV_ERR 0x04
868#define MWL8K_8366_AP_RXSTAT_TKIP_DECRYPT_ICV_ERR 0x08
869
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100870static void mwl8k_rxd_8366_ap_init(void *_rxd, dma_addr_t next_dma_addr)
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200871{
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100872 struct mwl8k_rxd_8366_ap *rxd = _rxd;
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200873
874 rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr);
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100875 rxd->rx_ctrl = MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST;
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200876}
877
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100878static void mwl8k_rxd_8366_ap_refill(void *_rxd, dma_addr_t addr, int len)
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200879{
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100880 struct mwl8k_rxd_8366_ap *rxd = _rxd;
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200881
882 rxd->pkt_len = cpu_to_le16(len);
883 rxd->pkt_phys_addr = cpu_to_le32(addr);
884 wmb();
885 rxd->rx_ctrl = 0;
886}
887
888static int
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100889mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status,
John W. Linville0d462bb2010-07-28 14:04:24 -0400890 __le16 *qos, s8 *noise)
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200891{
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100892 struct mwl8k_rxd_8366_ap *rxd = _rxd;
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200893
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100894 if (!(rxd->rx_ctrl & MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST))
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200895 return -1;
896 rmb();
897
898 memset(status, 0, sizeof(*status));
899
900 status->signal = -rxd->rssi;
John W. Linville0d462bb2010-07-28 14:04:24 -0400901 *noise = -rxd->noise_floor;
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200902
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100903 if (rxd->rate & MWL8K_8366_AP_RATE_INFO_MCS_FORMAT) {
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200904 status->flag |= RX_FLAG_HT;
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100905 if (rxd->rate & MWL8K_8366_AP_RATE_INFO_40MHZ)
Lennert Buytenhek8e9f33f2009-11-30 18:12:35 +0100906 status->flag |= RX_FLAG_40MHZ;
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100907 status->rate_idx = MWL8K_8366_AP_RATE_INFO_RATEID(rxd->rate);
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200908 } else {
909 int i;
910
Lennert Buytenhek777ad372010-01-12 13:48:04 +0100911 for (i = 0; i < ARRAY_SIZE(mwl8k_rates_24); i++) {
912 if (mwl8k_rates_24[i].hw_value == rxd->rate) {
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200913 status->rate_idx = i;
914 break;
915 }
916 }
917 }
918
Lennert Buytenhek85478342010-01-12 13:48:56 +0100919 if (rxd->channel > 14) {
920 status->band = IEEE80211_BAND_5GHZ;
921 if (!(status->flag & RX_FLAG_HT))
922 status->rate_idx -= 5;
923 } else {
924 status->band = IEEE80211_BAND_2GHZ;
925 }
Bruno Randolf59eb21a2011-01-17 13:37:28 +0900926 status->freq = ieee80211_channel_to_frequency(rxd->channel,
927 status->band);
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200928
Lennert Buytenhek20f09c32009-11-30 18:12:08 +0100929 *qos = rxd->qos_control;
930
Nishant Sarmukadamd9a07d42010-12-30 11:23:33 -0800931 if ((rxd->rx_status != MWL8K_8366_AP_RXSTAT_GENERAL_DECRYPT_ERR) &&
932 (rxd->rx_status & MWL8K_8366_AP_RXSTAT_DECRYPT_ERR_MASK) &&
933 (rxd->rx_status & MWL8K_8366_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR))
934 status->flag |= RX_FLAG_MMIC_ERROR;
935
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200936 return le16_to_cpu(rxd->pkt_len);
937}
938
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100939static struct rxd_ops rxd_8366_ap_ops = {
940 .rxd_size = sizeof(struct mwl8k_rxd_8366_ap),
941 .rxd_init = mwl8k_rxd_8366_ap_init,
942 .rxd_refill = mwl8k_rxd_8366_ap_refill,
943 .rxd_process = mwl8k_rxd_8366_ap_process,
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200944};
945
946/*
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100947 * Packet reception for STA firmware.
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100948 */
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100949struct mwl8k_rxd_sta {
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100950 __le16 pkt_len;
951 __u8 link_quality;
952 __u8 noise_level;
953 __le32 pkt_phys_addr;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200954 __le32 next_rxd_phys_addr;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100955 __le16 qos_control;
956 __le16 rate_info;
957 __le32 pad0[4];
958 __u8 rssi;
959 __u8 channel;
960 __le16 pad1;
961 __u8 rx_ctrl;
962 __u8 rx_status;
963 __u8 pad2[2];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000964} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100965
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100966#define MWL8K_STA_RATE_INFO_SHORTPRE 0x8000
967#define MWL8K_STA_RATE_INFO_ANTSELECT(x) (((x) >> 11) & 0x3)
968#define MWL8K_STA_RATE_INFO_RATEID(x) (((x) >> 3) & 0x3f)
969#define MWL8K_STA_RATE_INFO_40MHZ 0x0004
970#define MWL8K_STA_RATE_INFO_SHORTGI 0x0002
971#define MWL8K_STA_RATE_INFO_MCS_FORMAT 0x0001
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200972
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100973#define MWL8K_STA_RX_CTRL_OWNED_BY_HOST 0x02
Nishant Sarmukadamd9a07d42010-12-30 11:23:33 -0800974#define MWL8K_STA_RX_CTRL_DECRYPT_ERROR 0x04
975/* ICV=0 or MIC=1 */
976#define MWL8K_STA_RX_CTRL_DEC_ERR_TYPE 0x08
977/* Key is uploaded only in failure case */
978#define MWL8K_STA_RX_CTRL_KEY_INDEX 0x30
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200979
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100980static void mwl8k_rxd_sta_init(void *_rxd, dma_addr_t next_dma_addr)
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200981{
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100982 struct mwl8k_rxd_sta *rxd = _rxd;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200983
984 rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr);
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100985 rxd->rx_ctrl = MWL8K_STA_RX_CTRL_OWNED_BY_HOST;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200986}
987
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100988static void mwl8k_rxd_sta_refill(void *_rxd, dma_addr_t addr, int len)
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200989{
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100990 struct mwl8k_rxd_sta *rxd = _rxd;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200991
992 rxd->pkt_len = cpu_to_le16(len);
993 rxd->pkt_phys_addr = cpu_to_le32(addr);
994 wmb();
995 rxd->rx_ctrl = 0;
996}
997
998static int
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100999mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status,
John W. Linville0d462bb2010-07-28 14:04:24 -04001000 __le16 *qos, s8 *noise)
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001001{
Lennert Buytenhek89a91f42009-11-30 18:32:54 +01001002 struct mwl8k_rxd_sta *rxd = _rxd;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001003 u16 rate_info;
1004
Lennert Buytenhek89a91f42009-11-30 18:32:54 +01001005 if (!(rxd->rx_ctrl & MWL8K_STA_RX_CTRL_OWNED_BY_HOST))
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001006 return -1;
1007 rmb();
1008
1009 rate_info = le16_to_cpu(rxd->rate_info);
1010
1011 memset(status, 0, sizeof(*status));
1012
1013 status->signal = -rxd->rssi;
John W. Linville0d462bb2010-07-28 14:04:24 -04001014 *noise = -rxd->noise_level;
Lennert Buytenhek89a91f42009-11-30 18:32:54 +01001015 status->antenna = MWL8K_STA_RATE_INFO_ANTSELECT(rate_info);
1016 status->rate_idx = MWL8K_STA_RATE_INFO_RATEID(rate_info);
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001017
Lennert Buytenhek89a91f42009-11-30 18:32:54 +01001018 if (rate_info & MWL8K_STA_RATE_INFO_SHORTPRE)
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001019 status->flag |= RX_FLAG_SHORTPRE;
Lennert Buytenhek89a91f42009-11-30 18:32:54 +01001020 if (rate_info & MWL8K_STA_RATE_INFO_40MHZ)
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001021 status->flag |= RX_FLAG_40MHZ;
Lennert Buytenhek89a91f42009-11-30 18:32:54 +01001022 if (rate_info & MWL8K_STA_RATE_INFO_SHORTGI)
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001023 status->flag |= RX_FLAG_SHORT_GI;
Lennert Buytenhek89a91f42009-11-30 18:32:54 +01001024 if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT)
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001025 status->flag |= RX_FLAG_HT;
1026
Lennert Buytenhek85478342010-01-12 13:48:56 +01001027 if (rxd->channel > 14) {
1028 status->band = IEEE80211_BAND_5GHZ;
1029 if (!(status->flag & RX_FLAG_HT))
1030 status->rate_idx -= 5;
1031 } else {
1032 status->band = IEEE80211_BAND_2GHZ;
1033 }
Bruno Randolf59eb21a2011-01-17 13:37:28 +09001034 status->freq = ieee80211_channel_to_frequency(rxd->channel,
1035 status->band);
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001036
Lennert Buytenhek20f09c32009-11-30 18:12:08 +01001037 *qos = rxd->qos_control;
Nishant Sarmukadamd9a07d42010-12-30 11:23:33 -08001038 if ((rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DECRYPT_ERROR) &&
1039 (rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DEC_ERR_TYPE))
1040 status->flag |= RX_FLAG_MMIC_ERROR;
Lennert Buytenhek20f09c32009-11-30 18:12:08 +01001041
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001042 return le16_to_cpu(rxd->pkt_len);
1043}
1044
Lennert Buytenhek89a91f42009-11-30 18:32:54 +01001045static struct rxd_ops rxd_sta_ops = {
1046 .rxd_size = sizeof(struct mwl8k_rxd_sta),
1047 .rxd_init = mwl8k_rxd_sta_init,
1048 .rxd_refill = mwl8k_rxd_sta_refill,
1049 .rxd_process = mwl8k_rxd_sta_process,
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001050};
1051
1052
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001053#define MWL8K_RX_DESCS 256
1054#define MWL8K_RX_MAXSZ 3800
1055
1056static int mwl8k_rxq_init(struct ieee80211_hw *hw, int index)
1057{
1058 struct mwl8k_priv *priv = hw->priv;
1059 struct mwl8k_rx_queue *rxq = priv->rxq + index;
1060 int size;
1061 int i;
1062
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001063 rxq->rxd_count = 0;
1064 rxq->head = 0;
1065 rxq->tail = 0;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001066
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001067 size = MWL8K_RX_DESCS * priv->rxd_ops->rxd_size;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001068
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001069 rxq->rxd = pci_alloc_consistent(priv->pdev, size, &rxq->rxd_dma);
1070 if (rxq->rxd == NULL) {
Joe Perches5db55842010-08-11 19:11:19 -07001071 wiphy_err(hw->wiphy, "failed to alloc RX descriptors\n");
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001072 return -ENOMEM;
1073 }
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001074 memset(rxq->rxd, 0, size);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001075
Shan Weib9ede5f2011-03-08 11:02:03 +08001076 rxq->buf = kcalloc(MWL8K_RX_DESCS, sizeof(*rxq->buf), GFP_KERNEL);
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001077 if (rxq->buf == NULL) {
Joe Perches5db55842010-08-11 19:11:19 -07001078 wiphy_err(hw->wiphy, "failed to alloc RX skbuff list\n");
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001079 pci_free_consistent(priv->pdev, size, rxq->rxd, rxq->rxd_dma);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001080 return -ENOMEM;
1081 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001082
1083 for (i = 0; i < MWL8K_RX_DESCS; i++) {
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001084 int desc_size;
1085 void *rxd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001086 int nexti;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001087 dma_addr_t next_dma_addr;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001088
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001089 desc_size = priv->rxd_ops->rxd_size;
1090 rxd = rxq->rxd + (i * priv->rxd_ops->rxd_size);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001091
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001092 nexti = i + 1;
1093 if (nexti == MWL8K_RX_DESCS)
1094 nexti = 0;
1095 next_dma_addr = rxq->rxd_dma + (nexti * desc_size);
1096
1097 priv->rxd_ops->rxd_init(rxd, next_dma_addr);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001098 }
1099
1100 return 0;
1101}
1102
1103static int rxq_refill(struct ieee80211_hw *hw, int index, int limit)
1104{
1105 struct mwl8k_priv *priv = hw->priv;
1106 struct mwl8k_rx_queue *rxq = priv->rxq + index;
1107 int refilled;
1108
1109 refilled = 0;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001110 while (rxq->rxd_count < MWL8K_RX_DESCS && limit--) {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001111 struct sk_buff *skb;
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001112 dma_addr_t addr;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001113 int rx;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001114 void *rxd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001115
1116 skb = dev_alloc_skb(MWL8K_RX_MAXSZ);
1117 if (skb == NULL)
1118 break;
1119
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001120 addr = pci_map_single(priv->pdev, skb->data,
1121 MWL8K_RX_MAXSZ, DMA_FROM_DEVICE);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001122
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001123 rxq->rxd_count++;
1124 rx = rxq->tail++;
1125 if (rxq->tail == MWL8K_RX_DESCS)
1126 rxq->tail = 0;
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001127 rxq->buf[rx].skb = skb;
FUJITA Tomonori53b1b3e2010-04-02 13:10:38 +09001128 dma_unmap_addr_set(&rxq->buf[rx], dma, addr);
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001129
1130 rxd = rxq->rxd + (rx * priv->rxd_ops->rxd_size);
1131 priv->rxd_ops->rxd_refill(rxd, addr, MWL8K_RX_MAXSZ);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001132
1133 refilled++;
1134 }
1135
1136 return refilled;
1137}
1138
1139/* Must be called only when the card's reception is completely halted */
1140static void mwl8k_rxq_deinit(struct ieee80211_hw *hw, int index)
1141{
1142 struct mwl8k_priv *priv = hw->priv;
1143 struct mwl8k_rx_queue *rxq = priv->rxq + index;
1144 int i;
1145
Brian Cavagnolo73b46322011-03-17 11:58:41 -07001146 if (rxq->rxd == NULL)
1147 return;
1148
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001149 for (i = 0; i < MWL8K_RX_DESCS; i++) {
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001150 if (rxq->buf[i].skb != NULL) {
1151 pci_unmap_single(priv->pdev,
FUJITA Tomonori53b1b3e2010-04-02 13:10:38 +09001152 dma_unmap_addr(&rxq->buf[i], dma),
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001153 MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE);
FUJITA Tomonori53b1b3e2010-04-02 13:10:38 +09001154 dma_unmap_addr_set(&rxq->buf[i], dma, 0);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001155
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001156 kfree_skb(rxq->buf[i].skb);
1157 rxq->buf[i].skb = NULL;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001158 }
1159 }
1160
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001161 kfree(rxq->buf);
1162 rxq->buf = NULL;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001163
1164 pci_free_consistent(priv->pdev,
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001165 MWL8K_RX_DESCS * priv->rxd_ops->rxd_size,
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001166 rxq->rxd, rxq->rxd_dma);
1167 rxq->rxd = NULL;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001168}
1169
1170
1171/*
1172 * Scan a list of BSSIDs to process for finalize join.
1173 * Allows for extension to process multiple BSSIDs.
1174 */
1175static inline int
1176mwl8k_capture_bssid(struct mwl8k_priv *priv, struct ieee80211_hdr *wh)
1177{
1178 return priv->capture_beacon &&
1179 ieee80211_is_beacon(wh->frame_control) &&
1180 !compare_ether_addr(wh->addr3, priv->capture_bssid);
1181}
1182
Lennert Buytenhek37797522009-10-22 20:19:45 +02001183static inline void mwl8k_save_beacon(struct ieee80211_hw *hw,
1184 struct sk_buff *skb)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001185{
Lennert Buytenhek37797522009-10-22 20:19:45 +02001186 struct mwl8k_priv *priv = hw->priv;
1187
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001188 priv->capture_beacon = false;
Lennert Buytenhekd89173f2009-07-16 09:54:27 +02001189 memset(priv->capture_bssid, 0, ETH_ALEN);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001190
1191 /*
1192 * Use GFP_ATOMIC as rxq_process is called from
1193 * the primary interrupt handler, memory allocation call
1194 * must not sleep.
1195 */
1196 priv->beacon_skb = skb_copy(skb, GFP_ATOMIC);
1197 if (priv->beacon_skb != NULL)
Lennert Buytenhek37797522009-10-22 20:19:45 +02001198 ieee80211_queue_work(hw, &priv->finalize_join_worker);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001199}
1200
Nishant Sarmukadamd9a07d42010-12-30 11:23:33 -08001201static inline struct mwl8k_vif *mwl8k_find_vif_bss(struct list_head *vif_list,
1202 u8 *bssid)
1203{
1204 struct mwl8k_vif *mwl8k_vif;
1205
1206 list_for_each_entry(mwl8k_vif,
1207 vif_list, list) {
1208 if (memcmp(bssid, mwl8k_vif->bssid,
1209 ETH_ALEN) == 0)
1210 return mwl8k_vif;
1211 }
1212
1213 return NULL;
1214}
1215
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001216static int rxq_process(struct ieee80211_hw *hw, int index, int limit)
1217{
1218 struct mwl8k_priv *priv = hw->priv;
Nishant Sarmukadamd9a07d42010-12-30 11:23:33 -08001219 struct mwl8k_vif *mwl8k_vif = NULL;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001220 struct mwl8k_rx_queue *rxq = priv->rxq + index;
1221 int processed;
1222
1223 processed = 0;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001224 while (rxq->rxd_count && limit--) {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001225 struct sk_buff *skb;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001226 void *rxd;
1227 int pkt_len;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001228 struct ieee80211_rx_status status;
Nishant Sarmukadamd9a07d42010-12-30 11:23:33 -08001229 struct ieee80211_hdr *wh;
Lennert Buytenhek20f09c32009-11-30 18:12:08 +01001230 __le16 qos;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001231
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001232 skb = rxq->buf[rxq->head].skb;
Lennert Buytenhekd25f9f12009-08-03 21:58:26 +02001233 if (skb == NULL)
1234 break;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001235
1236 rxd = rxq->rxd + (rxq->head * priv->rxd_ops->rxd_size);
1237
John W. Linville0d462bb2010-07-28 14:04:24 -04001238 pkt_len = priv->rxd_ops->rxd_process(rxd, &status, &qos,
1239 &priv->noise);
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001240 if (pkt_len < 0)
1241 break;
1242
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001243 rxq->buf[rxq->head].skb = NULL;
1244
1245 pci_unmap_single(priv->pdev,
FUJITA Tomonori53b1b3e2010-04-02 13:10:38 +09001246 dma_unmap_addr(&rxq->buf[rxq->head], dma),
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001247 MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE);
FUJITA Tomonori53b1b3e2010-04-02 13:10:38 +09001248 dma_unmap_addr_set(&rxq->buf[rxq->head], dma, 0);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001249
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001250 rxq->head++;
1251 if (rxq->head == MWL8K_RX_DESCS)
1252 rxq->head = 0;
1253
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001254 rxq->rxd_count--;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001255
Nishant Sarmukadamd9a07d42010-12-30 11:23:33 -08001256 wh = &((struct mwl8k_dma_data *)skb->data)->wh;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001257
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001258 /*
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +02001259 * Check for a pending join operation. Save a
1260 * copy of the beacon and schedule a tasklet to
1261 * send a FINALIZE_JOIN command to the firmware.
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001262 */
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001263 if (mwl8k_capture_bssid(priv, (void *)skb->data))
Lennert Buytenhek37797522009-10-22 20:19:45 +02001264 mwl8k_save_beacon(hw, skb);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001265
Nishant Sarmukadamd9a07d42010-12-30 11:23:33 -08001266 if (ieee80211_has_protected(wh->frame_control)) {
1267
1268 /* Check if hw crypto has been enabled for
1269 * this bss. If yes, set the status flags
1270 * accordingly
1271 */
1272 mwl8k_vif = mwl8k_find_vif_bss(&priv->vif_list,
1273 wh->addr1);
1274
1275 if (mwl8k_vif != NULL &&
1276 mwl8k_vif->is_hw_crypto_enabled == true) {
1277 /*
1278 * When MMIC ERROR is encountered
1279 * by the firmware, payload is
1280 * dropped and only 32 bytes of
1281 * mwl8k Firmware header is sent
1282 * to the host.
1283 *
1284 * We need to add four bytes of
1285 * key information. In it
1286 * MAC80211 expects keyidx set to
1287 * 0 for triggering Counter
1288 * Measure of MMIC failure.
1289 */
1290 if (status.flag & RX_FLAG_MMIC_ERROR) {
1291 struct mwl8k_dma_data *tr;
1292 tr = (struct mwl8k_dma_data *)skb->data;
1293 memset((void *)&(tr->data), 0, 4);
1294 pkt_len += 4;
1295 }
1296
1297 if (!ieee80211_is_auth(wh->frame_control))
1298 status.flag |= RX_FLAG_IV_STRIPPED |
1299 RX_FLAG_DECRYPTED |
1300 RX_FLAG_MMIC_STRIPPED;
1301 }
1302 }
1303
1304 skb_put(skb, pkt_len);
1305 mwl8k_remove_dma_header(skb, qos);
Johannes Bergf1d58c22009-06-17 13:13:00 +02001306 memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
1307 ieee80211_rx_irqsafe(hw, skb);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001308
1309 processed++;
1310 }
1311
1312 return processed;
1313}
1314
1315
1316/*
1317 * Packet transmission.
1318 */
1319
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001320#define MWL8K_TXD_STATUS_OK 0x00000001
1321#define MWL8K_TXD_STATUS_OK_RETRY 0x00000002
1322#define MWL8K_TXD_STATUS_OK_MORE_RETRY 0x00000004
1323#define MWL8K_TXD_STATUS_MULTICAST_TX 0x00000008
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001324#define MWL8K_TXD_STATUS_FW_OWNED 0x80000000
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001325
Lennert Buytenheke0493a82009-11-30 18:32:00 +01001326#define MWL8K_QOS_QLEN_UNSPEC 0xff00
1327#define MWL8K_QOS_ACK_POLICY_MASK 0x0060
1328#define MWL8K_QOS_ACK_POLICY_NORMAL 0x0000
1329#define MWL8K_QOS_ACK_POLICY_BLOCKACK 0x0060
1330#define MWL8K_QOS_EOSP 0x0010
1331
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001332struct mwl8k_tx_desc {
1333 __le32 status;
1334 __u8 data_rate;
1335 __u8 tx_priority;
1336 __le16 qos_control;
1337 __le32 pkt_phys_addr;
1338 __le16 pkt_len;
Lennert Buytenhekd89173f2009-07-16 09:54:27 +02001339 __u8 dest_MAC_addr[ETH_ALEN];
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001340 __le32 next_txd_phys_addr;
Brian Cavagnolo8a7a5782011-03-17 11:58:42 -07001341 __le32 timestamp;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001342 __le16 rate_info;
1343 __u8 peer_id;
Brian Cavagnoloa1fe24b2010-11-12 17:23:47 -08001344 __u8 tx_frag_cnt;
Eric Dumazetba2d3582010-06-02 18:10:09 +00001345} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001346
1347#define MWL8K_TX_DESCS 128
1348
1349static int mwl8k_txq_init(struct ieee80211_hw *hw, int index)
1350{
1351 struct mwl8k_priv *priv = hw->priv;
1352 struct mwl8k_tx_queue *txq = priv->txq + index;
1353 int size;
1354 int i;
1355
Kalle Valo8ccbc3b2010-02-07 10:20:52 +02001356 txq->len = 0;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001357 txq->head = 0;
1358 txq->tail = 0;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001359
1360 size = MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc);
1361
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001362 txq->txd = pci_alloc_consistent(priv->pdev, size, &txq->txd_dma);
1363 if (txq->txd == NULL) {
Joe Perches5db55842010-08-11 19:11:19 -07001364 wiphy_err(hw->wiphy, "failed to alloc TX descriptors\n");
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001365 return -ENOMEM;
1366 }
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001367 memset(txq->txd, 0, size);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001368
Shan Weib9ede5f2011-03-08 11:02:03 +08001369 txq->skb = kcalloc(MWL8K_TX_DESCS, sizeof(*txq->skb), GFP_KERNEL);
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001370 if (txq->skb == NULL) {
Joe Perches5db55842010-08-11 19:11:19 -07001371 wiphy_err(hw->wiphy, "failed to alloc TX skbuff list\n");
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001372 pci_free_consistent(priv->pdev, size, txq->txd, txq->txd_dma);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001373 return -ENOMEM;
1374 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001375
1376 for (i = 0; i < MWL8K_TX_DESCS; i++) {
1377 struct mwl8k_tx_desc *tx_desc;
1378 int nexti;
1379
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001380 tx_desc = txq->txd + i;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001381 nexti = (i + 1) % MWL8K_TX_DESCS;
1382
1383 tx_desc->status = 0;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001384 tx_desc->next_txd_phys_addr =
1385 cpu_to_le32(txq->txd_dma + nexti * sizeof(*tx_desc));
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001386 }
1387
1388 return 0;
1389}
1390
1391static inline void mwl8k_tx_start(struct mwl8k_priv *priv)
1392{
1393 iowrite32(MWL8K_H2A_INT_PPA_READY,
1394 priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
1395 iowrite32(MWL8K_H2A_INT_DUMMY,
1396 priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
1397 ioread32(priv->regs + MWL8K_HIU_INT_CODE);
1398}
1399
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001400static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001401{
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001402 struct mwl8k_priv *priv = hw->priv;
1403 int i;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001404
Brian Cavagnoloe6007072011-03-17 11:58:44 -07001405 for (i = 0; i < mwl8k_tx_queues(priv); i++) {
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001406 struct mwl8k_tx_queue *txq = priv->txq + i;
1407 int fw_owned = 0;
1408 int drv_owned = 0;
1409 int unused = 0;
1410 int desc;
Lennert Buytenhekc3f967d2009-08-18 04:15:22 +02001411
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001412 for (desc = 0; desc < MWL8K_TX_DESCS; desc++) {
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001413 struct mwl8k_tx_desc *tx_desc = txq->txd + desc;
1414 u32 status;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001415
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001416 status = le32_to_cpu(tx_desc->status);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001417 if (status & MWL8K_TXD_STATUS_FW_OWNED)
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001418 fw_owned++;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001419 else
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001420 drv_owned++;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001421
1422 if (tx_desc->pkt_len == 0)
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001423 unused++;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001424 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001425
Joe Perchesc96c31e2010-07-26 14:39:58 -07001426 wiphy_err(hw->wiphy,
1427 "txq[%d] len=%d head=%d tail=%d "
1428 "fw_owned=%d drv_owned=%d unused=%d\n",
1429 i,
1430 txq->len, txq->head, txq->tail,
1431 fw_owned, drv_owned, unused);
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001432 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001433}
1434
Lennert Buytenhek618952a2009-08-18 03:18:01 +02001435/*
Lennert Buytenhek88de754a2009-10-22 20:19:37 +02001436 * Must be called with priv->fw_mutex held and tx queues stopped.
Lennert Buytenhek618952a2009-08-18 03:18:01 +02001437 */
Lennert Buytenhek62abd3c2010-01-08 18:28:34 +01001438#define MWL8K_TX_WAIT_TIMEOUT_MS 5000
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001439
Lennert Buytenhek950d5b02009-07-17 01:48:41 +02001440static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001441{
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001442 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenhek88de754a2009-10-22 20:19:37 +02001443 DECLARE_COMPLETION_ONSTACK(tx_wait);
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001444 int retry;
1445 int rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001446
1447 might_sleep();
1448
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001449 /*
1450 * The TX queues are stopped at this point, so this test
1451 * doesn't need to take ->tx_lock.
1452 */
1453 if (!priv->pending_tx_pkts)
1454 return 0;
1455
1456 retry = 0;
1457 rc = 0;
1458
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001459 spin_lock_bh(&priv->tx_lock);
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001460 priv->tx_wait = &tx_wait;
1461 while (!rc) {
1462 int oldcount;
1463 unsigned long timeout;
1464
1465 oldcount = priv->pending_tx_pkts;
1466
1467 spin_unlock_bh(&priv->tx_lock);
1468 timeout = wait_for_completion_timeout(&tx_wait,
1469 msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS));
1470 spin_lock_bh(&priv->tx_lock);
1471
1472 if (timeout) {
1473 WARN_ON(priv->pending_tx_pkts);
1474 if (retry) {
Joe Perchesc96c31e2010-07-26 14:39:58 -07001475 wiphy_notice(hw->wiphy, "tx rings drained\n");
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001476 }
1477 break;
1478 }
1479
1480 if (priv->pending_tx_pkts < oldcount) {
Joe Perchesc96c31e2010-07-26 14:39:58 -07001481 wiphy_notice(hw->wiphy,
1482 "waiting for tx rings to drain (%d -> %d pkts)\n",
1483 oldcount, priv->pending_tx_pkts);
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001484 retry = 1;
1485 continue;
1486 }
1487
1488 priv->tx_wait = NULL;
1489
Joe Perchesc96c31e2010-07-26 14:39:58 -07001490 wiphy_err(hw->wiphy, "tx rings stuck for %d ms\n",
1491 MWL8K_TX_WAIT_TIMEOUT_MS);
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001492 mwl8k_dump_tx_rings(hw);
1493
1494 rc = -ETIMEDOUT;
1495 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001496 spin_unlock_bh(&priv->tx_lock);
1497
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001498 return rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001499}
1500
Lennert Buytenhekc23b5a62009-07-16 13:49:55 +02001501#define MWL8K_TXD_SUCCESS(status) \
1502 ((status) & (MWL8K_TXD_STATUS_OK | \
1503 MWL8K_TXD_STATUS_OK_RETRY | \
1504 MWL8K_TXD_STATUS_OK_MORE_RETRY))
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001505
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01001506static int
1507mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001508{
1509 struct mwl8k_priv *priv = hw->priv;
1510 struct mwl8k_tx_queue *txq = priv->txq + index;
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01001511 int processed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001512
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01001513 processed = 0;
Kalle Valo8ccbc3b2010-02-07 10:20:52 +02001514 while (txq->len > 0 && limit--) {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001515 int tx;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001516 struct mwl8k_tx_desc *tx_desc;
1517 unsigned long addr;
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02001518 int size;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001519 struct sk_buff *skb;
1520 struct ieee80211_tx_info *info;
1521 u32 status;
1522
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001523 tx = txq->head;
1524 tx_desc = txq->txd + tx;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001525
1526 status = le32_to_cpu(tx_desc->status);
1527
1528 if (status & MWL8K_TXD_STATUS_FW_OWNED) {
1529 if (!force)
1530 break;
1531 tx_desc->status &=
1532 ~cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED);
1533 }
1534
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001535 txq->head = (tx + 1) % MWL8K_TX_DESCS;
Kalle Valo8ccbc3b2010-02-07 10:20:52 +02001536 BUG_ON(txq->len == 0);
1537 txq->len--;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001538 priv->pending_tx_pkts--;
1539
1540 addr = le32_to_cpu(tx_desc->pkt_phys_addr);
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02001541 size = le16_to_cpu(tx_desc->pkt_len);
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001542 skb = txq->skb[tx];
1543 txq->skb[tx] = NULL;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001544
1545 BUG_ON(skb == NULL);
1546 pci_unmap_single(priv->pdev, addr, size, PCI_DMA_TODEVICE);
1547
Lennert Buytenhek20f09c32009-11-30 18:12:08 +01001548 mwl8k_remove_dma_header(skb, tx_desc->qos_control);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001549
1550 /* Mark descriptor as unused */
1551 tx_desc->pkt_phys_addr = 0;
1552 tx_desc->pkt_len = 0;
1553
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001554 info = IEEE80211_SKB_CB(skb);
1555 ieee80211_tx_info_clear_status(info);
Nishant Sarmukadam0bf22c32011-02-17 14:45:17 -08001556
1557 /* Rate control is happening in the firmware.
1558 * Ensure no tx rate is being reported.
1559 */
1560 info->status.rates[0].idx = -1;
1561 info->status.rates[0].count = 1;
1562
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02001563 if (MWL8K_TXD_SUCCESS(status))
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001564 info->flags |= IEEE80211_TX_STAT_ACK;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001565
1566 ieee80211_tx_status_irqsafe(hw, skb);
1567
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01001568 processed++;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001569 }
1570
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01001571 if (processed && priv->radio_on && !mutex_is_locked(&priv->fw_mutex))
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001572 ieee80211_wake_queue(hw, index);
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01001573
1574 return processed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001575}
1576
1577/* must be called only when the card's transmit is completely halted */
1578static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index)
1579{
1580 struct mwl8k_priv *priv = hw->priv;
1581 struct mwl8k_tx_queue *txq = priv->txq + index;
1582
Brian Cavagnolo73b46322011-03-17 11:58:41 -07001583 if (txq->txd == NULL)
1584 return;
1585
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01001586 mwl8k_txq_reclaim(hw, index, INT_MAX, 1);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001587
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001588 kfree(txq->skb);
1589 txq->skb = NULL;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001590
1591 pci_free_consistent(priv->pdev,
1592 MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc),
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001593 txq->txd, txq->txd_dma);
1594 txq->txd = NULL;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001595}
1596
Johannes Berg7bb45682011-02-24 14:42:06 +01001597static void
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001598mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
1599{
1600 struct mwl8k_priv *priv = hw->priv;
1601 struct ieee80211_tx_info *tx_info;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001602 struct mwl8k_vif *mwl8k_vif;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001603 struct ieee80211_hdr *wh;
1604 struct mwl8k_tx_queue *txq;
1605 struct mwl8k_tx_desc *tx;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001606 dma_addr_t dma;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001607 u32 txstatus;
1608 u8 txdatarate;
1609 u16 qos;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001610
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001611 wh = (struct ieee80211_hdr *)skb->data;
1612 if (ieee80211_is_data_qos(wh->frame_control))
1613 qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh)));
1614 else
1615 qos = 0;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001616
Nishant Sarmukadamd9a07d42010-12-30 11:23:33 -08001617 if (priv->ap_fw)
1618 mwl8k_encapsulate_tx_frame(skb);
1619 else
1620 mwl8k_add_dma_header(skb, 0);
1621
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001622 wh = &((struct mwl8k_dma_data *)skb->data)->wh;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001623
1624 tx_info = IEEE80211_SKB_CB(skb);
1625 mwl8k_vif = MWL8K_VIF(tx_info->control.vif);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001626
1627 if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001628 wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
Lennert Buytenhek657232b2010-01-12 13:47:47 +01001629 wh->seq_ctrl |= cpu_to_le16(mwl8k_vif->seqno);
1630 mwl8k_vif->seqno += 0x10;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001631 }
1632
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001633 /* Setup firmware control bit fields for each frame type. */
1634 txstatus = 0;
1635 txdatarate = 0;
1636 if (ieee80211_is_mgmt(wh->frame_control) ||
1637 ieee80211_is_ctl(wh->frame_control)) {
1638 txdatarate = 0;
Lennert Buytenheke0493a82009-11-30 18:32:00 +01001639 qos |= MWL8K_QOS_QLEN_UNSPEC | MWL8K_QOS_EOSP;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001640 } else if (ieee80211_is_data(wh->frame_control)) {
1641 txdatarate = 1;
1642 if (is_multicast_ether_addr(wh->addr1))
1643 txstatus |= MWL8K_TXD_STATUS_MULTICAST_TX;
1644
Lennert Buytenheke0493a82009-11-30 18:32:00 +01001645 qos &= ~MWL8K_QOS_ACK_POLICY_MASK;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001646 if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
Lennert Buytenheke0493a82009-11-30 18:32:00 +01001647 qos |= MWL8K_QOS_ACK_POLICY_BLOCKACK;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001648 else
Lennert Buytenheke0493a82009-11-30 18:32:00 +01001649 qos |= MWL8K_QOS_ACK_POLICY_NORMAL;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001650 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001651
1652 dma = pci_map_single(priv->pdev, skb->data,
1653 skb->len, PCI_DMA_TODEVICE);
1654
1655 if (pci_dma_mapping_error(priv->pdev, dma)) {
Joe Perchesc96c31e2010-07-26 14:39:58 -07001656 wiphy_debug(hw->wiphy,
1657 "failed to dma map skb, dropping TX frame.\n");
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001658 dev_kfree_skb(skb);
Johannes Berg7bb45682011-02-24 14:42:06 +01001659 return;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001660 }
1661
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001662 spin_lock_bh(&priv->tx_lock);
1663
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001664 txq = priv->txq + index;
1665
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001666 BUG_ON(txq->skb[txq->tail] != NULL);
1667 txq->skb[txq->tail] = skb;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001668
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001669 tx = txq->txd + txq->tail;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001670 tx->data_rate = txdatarate;
1671 tx->tx_priority = index;
1672 tx->qos_control = cpu_to_le16(qos);
1673 tx->pkt_phys_addr = cpu_to_le32(dma);
1674 tx->pkt_len = cpu_to_le16(skb->len);
1675 tx->rate_info = 0;
Lennert Buytenheka6804002010-01-04 21:55:42 +01001676 if (!priv->ap_fw && tx_info->control.sta != NULL)
1677 tx->peer_id = MWL8K_STA(tx_info->control.sta)->peer_id;
1678 else
1679 tx->peer_id = 0;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001680 wmb();
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001681 tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus);
1682
Kalle Valo8ccbc3b2010-02-07 10:20:52 +02001683 txq->len++;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001684 priv->pending_tx_pkts++;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001685
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001686 txq->tail++;
1687 if (txq->tail == MWL8K_TX_DESCS)
1688 txq->tail = 0;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001689
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001690 if (txq->head == txq->tail)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001691 ieee80211_stop_queue(hw, index);
1692
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001693 mwl8k_tx_start(priv);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001694
1695 spin_unlock_bh(&priv->tx_lock);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001696}
1697
1698
1699/*
Lennert Buytenhek618952a2009-08-18 03:18:01 +02001700 * Firmware access.
1701 *
1702 * We have the following requirements for issuing firmware commands:
1703 * - Some commands require that the packet transmit path is idle when
1704 * the command is issued. (For simplicity, we'll just quiesce the
1705 * transmit path for every command.)
1706 * - There are certain sequences of commands that need to be issued to
1707 * the hardware sequentially, with no other intervening commands.
1708 *
1709 * This leads to an implementation of a "firmware lock" as a mutex that
1710 * can be taken recursively, and which is taken by both the low-level
1711 * command submission function (mwl8k_post_cmd) as well as any users of
1712 * that function that require issuing of an atomic sequence of commands,
1713 * and quiesces the transmit path whenever it's taken.
1714 */
1715static int mwl8k_fw_lock(struct ieee80211_hw *hw)
1716{
1717 struct mwl8k_priv *priv = hw->priv;
1718
1719 if (priv->fw_mutex_owner != current) {
1720 int rc;
1721
1722 mutex_lock(&priv->fw_mutex);
1723 ieee80211_stop_queues(hw);
1724
1725 rc = mwl8k_tx_wait_empty(hw);
1726 if (rc) {
1727 ieee80211_wake_queues(hw);
1728 mutex_unlock(&priv->fw_mutex);
1729
1730 return rc;
1731 }
1732
1733 priv->fw_mutex_owner = current;
1734 }
1735
1736 priv->fw_mutex_depth++;
1737
1738 return 0;
1739}
1740
1741static void mwl8k_fw_unlock(struct ieee80211_hw *hw)
1742{
1743 struct mwl8k_priv *priv = hw->priv;
1744
1745 if (!--priv->fw_mutex_depth) {
1746 ieee80211_wake_queues(hw);
1747 priv->fw_mutex_owner = NULL;
1748 mutex_unlock(&priv->fw_mutex);
1749 }
1750}
1751
1752
1753/*
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001754 * Command processing.
1755 */
1756
Lennert Buytenhek0c9cc642009-11-30 18:12:49 +01001757/* Timeout firmware commands after 10s */
1758#define MWL8K_CMD_TIMEOUT_MS 10000
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001759
1760static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
1761{
1762 DECLARE_COMPLETION_ONSTACK(cmd_wait);
1763 struct mwl8k_priv *priv = hw->priv;
1764 void __iomem *regs = priv->regs;
1765 dma_addr_t dma_addr;
1766 unsigned int dma_size;
1767 int rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001768 unsigned long timeout = 0;
1769 u8 buf[32];
1770
John W. Linvilleb6037422010-07-20 13:55:00 -04001771 cmd->result = (__force __le16) 0xffff;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001772 dma_size = le16_to_cpu(cmd->length);
1773 dma_addr = pci_map_single(priv->pdev, cmd, dma_size,
1774 PCI_DMA_BIDIRECTIONAL);
1775 if (pci_dma_mapping_error(priv->pdev, dma_addr))
1776 return -ENOMEM;
1777
Lennert Buytenhek618952a2009-08-18 03:18:01 +02001778 rc = mwl8k_fw_lock(hw);
Lennert Buytenhek39a1e422009-08-24 15:42:46 +02001779 if (rc) {
1780 pci_unmap_single(priv->pdev, dma_addr, dma_size,
1781 PCI_DMA_BIDIRECTIONAL);
Lennert Buytenhek618952a2009-08-18 03:18:01 +02001782 return rc;
Lennert Buytenhek39a1e422009-08-24 15:42:46 +02001783 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001784
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001785 priv->hostcmd_wait = &cmd_wait;
1786 iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR);
1787 iowrite32(MWL8K_H2A_INT_DOORBELL,
1788 regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
1789 iowrite32(MWL8K_H2A_INT_DUMMY,
1790 regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001791
1792 timeout = wait_for_completion_timeout(&cmd_wait,
1793 msecs_to_jiffies(MWL8K_CMD_TIMEOUT_MS));
1794
Lennert Buytenhek618952a2009-08-18 03:18:01 +02001795 priv->hostcmd_wait = NULL;
1796
1797 mwl8k_fw_unlock(hw);
1798
Lennert Buytenhek37055bd2009-08-03 21:58:47 +02001799 pci_unmap_single(priv->pdev, dma_addr, dma_size,
1800 PCI_DMA_BIDIRECTIONAL);
1801
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001802 if (!timeout) {
Joe Perches5db55842010-08-11 19:11:19 -07001803 wiphy_err(hw->wiphy, "Command %s timeout after %u ms\n",
Joe Perchesc96c31e2010-07-26 14:39:58 -07001804 mwl8k_cmd_name(cmd->code, buf, sizeof(buf)),
1805 MWL8K_CMD_TIMEOUT_MS);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001806 rc = -ETIMEDOUT;
1807 } else {
Lennert Buytenhek0c9cc642009-11-30 18:12:49 +01001808 int ms;
1809
1810 ms = MWL8K_CMD_TIMEOUT_MS - jiffies_to_msecs(timeout);
1811
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02001812 rc = cmd->result ? -EINVAL : 0;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001813 if (rc)
Joe Perches5db55842010-08-11 19:11:19 -07001814 wiphy_err(hw->wiphy, "Command %s error 0x%x\n",
Joe Perchesc96c31e2010-07-26 14:39:58 -07001815 mwl8k_cmd_name(cmd->code, buf, sizeof(buf)),
1816 le16_to_cpu(cmd->result));
Lennert Buytenhek0c9cc642009-11-30 18:12:49 +01001817 else if (ms > 2000)
Joe Perches5db55842010-08-11 19:11:19 -07001818 wiphy_notice(hw->wiphy, "Command %s took %d ms\n",
Joe Perchesc96c31e2010-07-26 14:39:58 -07001819 mwl8k_cmd_name(cmd->code,
1820 buf, sizeof(buf)),
1821 ms);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001822 }
1823
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001824 return rc;
1825}
1826
Lennert Buytenhekf57ca9c2010-01-12 13:50:14 +01001827static int mwl8k_post_pervif_cmd(struct ieee80211_hw *hw,
1828 struct ieee80211_vif *vif,
1829 struct mwl8k_cmd_pkt *cmd)
1830{
1831 if (vif != NULL)
1832 cmd->macid = MWL8K_VIF(vif)->macid;
1833 return mwl8k_post_cmd(hw, cmd);
1834}
1835
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001836/*
Lennert Buytenhek1349ad22010-01-12 13:48:17 +01001837 * Setup code shared between STA and AP firmware images.
1838 */
1839static void mwl8k_setup_2ghz_band(struct ieee80211_hw *hw)
1840{
1841 struct mwl8k_priv *priv = hw->priv;
1842
1843 BUILD_BUG_ON(sizeof(priv->channels_24) != sizeof(mwl8k_channels_24));
1844 memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24));
1845
1846 BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24));
1847 memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24));
1848
1849 priv->band_24.band = IEEE80211_BAND_2GHZ;
1850 priv->band_24.channels = priv->channels_24;
1851 priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24);
1852 priv->band_24.bitrates = priv->rates_24;
1853 priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24);
1854
1855 hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band_24;
1856}
1857
Lennert Buytenhek4eae9ed2010-01-12 13:48:32 +01001858static void mwl8k_setup_5ghz_band(struct ieee80211_hw *hw)
1859{
1860 struct mwl8k_priv *priv = hw->priv;
1861
1862 BUILD_BUG_ON(sizeof(priv->channels_50) != sizeof(mwl8k_channels_50));
1863 memcpy(priv->channels_50, mwl8k_channels_50, sizeof(mwl8k_channels_50));
1864
1865 BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl8k_rates_50));
1866 memcpy(priv->rates_50, mwl8k_rates_50, sizeof(mwl8k_rates_50));
1867
1868 priv->band_50.band = IEEE80211_BAND_5GHZ;
1869 priv->band_50.channels = priv->channels_50;
1870 priv->band_50.n_channels = ARRAY_SIZE(mwl8k_channels_50);
1871 priv->band_50.bitrates = priv->rates_50;
1872 priv->band_50.n_bitrates = ARRAY_SIZE(mwl8k_rates_50);
1873
1874 hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &priv->band_50;
1875}
1876
Lennert Buytenhek1349ad22010-01-12 13:48:17 +01001877/*
Lennert Buytenhek04b147b2009-10-22 20:21:05 +02001878 * CMD_GET_HW_SPEC (STA version).
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001879 */
Lennert Buytenhek04b147b2009-10-22 20:21:05 +02001880struct mwl8k_cmd_get_hw_spec_sta {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001881 struct mwl8k_cmd_pkt header;
1882 __u8 hw_rev;
1883 __u8 host_interface;
1884 __le16 num_mcaddrs;
Lennert Buytenhekd89173f2009-07-16 09:54:27 +02001885 __u8 perm_addr[ETH_ALEN];
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001886 __le16 region_code;
1887 __le32 fw_rev;
1888 __le32 ps_cookie;
1889 __le32 caps;
1890 __u8 mcs_bitmap[16];
1891 __le32 rx_queue_ptr;
1892 __le32 num_tx_queues;
Brian Cavagnoloe6007072011-03-17 11:58:44 -07001893 __le32 tx_queue_ptrs[MWL8K_TX_WMM_QUEUES];
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001894 __le32 caps2;
1895 __le32 num_tx_desc_per_queue;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001896 __le32 total_rxd;
Eric Dumazetba2d3582010-06-02 18:10:09 +00001897} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001898
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001899#define MWL8K_CAP_MAX_AMSDU 0x20000000
1900#define MWL8K_CAP_GREENFIELD 0x08000000
1901#define MWL8K_CAP_AMPDU 0x04000000
1902#define MWL8K_CAP_RX_STBC 0x01000000
1903#define MWL8K_CAP_TX_STBC 0x00800000
1904#define MWL8K_CAP_SHORTGI_40MHZ 0x00400000
1905#define MWL8K_CAP_SHORTGI_20MHZ 0x00200000
1906#define MWL8K_CAP_RX_ANTENNA_MASK 0x000e0000
1907#define MWL8K_CAP_TX_ANTENNA_MASK 0x0001c000
1908#define MWL8K_CAP_DELAY_BA 0x00003000
1909#define MWL8K_CAP_MIMO 0x00000200
1910#define MWL8K_CAP_40MHZ 0x00000100
Lennert Buytenhek06953232010-01-12 13:49:41 +01001911#define MWL8K_CAP_BAND_MASK 0x00000007
1912#define MWL8K_CAP_5GHZ 0x00000004
1913#define MWL8K_CAP_2GHZ4 0x00000001
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001914
Lennert Buytenhek06953232010-01-12 13:49:41 +01001915static void
1916mwl8k_set_ht_caps(struct ieee80211_hw *hw,
1917 struct ieee80211_supported_band *band, u32 cap)
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001918{
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001919 int rx_streams;
1920 int tx_streams;
1921
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001922 band->ht_cap.ht_supported = 1;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001923
1924 if (cap & MWL8K_CAP_MAX_AMSDU)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001925 band->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001926 if (cap & MWL8K_CAP_GREENFIELD)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001927 band->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001928 if (cap & MWL8K_CAP_AMPDU) {
1929 hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001930 band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
1931 band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001932 }
1933 if (cap & MWL8K_CAP_RX_STBC)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001934 band->ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001935 if (cap & MWL8K_CAP_TX_STBC)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001936 band->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001937 if (cap & MWL8K_CAP_SHORTGI_40MHZ)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001938 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001939 if (cap & MWL8K_CAP_SHORTGI_20MHZ)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001940 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001941 if (cap & MWL8K_CAP_DELAY_BA)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001942 band->ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001943 if (cap & MWL8K_CAP_40MHZ)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001944 band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001945
1946 rx_streams = hweight32(cap & MWL8K_CAP_RX_ANTENNA_MASK);
1947 tx_streams = hweight32(cap & MWL8K_CAP_TX_ANTENNA_MASK);
1948
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001949 band->ht_cap.mcs.rx_mask[0] = 0xff;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001950 if (rx_streams >= 2)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001951 band->ht_cap.mcs.rx_mask[1] = 0xff;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001952 if (rx_streams >= 3)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001953 band->ht_cap.mcs.rx_mask[2] = 0xff;
1954 band->ht_cap.mcs.rx_mask[4] = 0x01;
1955 band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001956
1957 if (rx_streams != tx_streams) {
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001958 band->ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
1959 band->ht_cap.mcs.tx_params |= (tx_streams - 1) <<
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001960 IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
1961 }
1962}
1963
Lennert Buytenhek06953232010-01-12 13:49:41 +01001964static void
1965mwl8k_set_caps(struct ieee80211_hw *hw, u32 caps)
1966{
1967 struct mwl8k_priv *priv = hw->priv;
1968
1969 if ((caps & MWL8K_CAP_2GHZ4) || !(caps & MWL8K_CAP_BAND_MASK)) {
1970 mwl8k_setup_2ghz_band(hw);
1971 if (caps & MWL8K_CAP_MIMO)
1972 mwl8k_set_ht_caps(hw, &priv->band_24, caps);
1973 }
1974
1975 if (caps & MWL8K_CAP_5GHZ) {
1976 mwl8k_setup_5ghz_band(hw);
1977 if (caps & MWL8K_CAP_MIMO)
1978 mwl8k_set_ht_caps(hw, &priv->band_50, caps);
1979 }
1980}
1981
Lennert Buytenhek04b147b2009-10-22 20:21:05 +02001982static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001983{
1984 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenhek04b147b2009-10-22 20:21:05 +02001985 struct mwl8k_cmd_get_hw_spec_sta *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001986 int rc;
1987 int i;
1988
1989 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
1990 if (cmd == NULL)
1991 return -ENOMEM;
1992
1993 cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC);
1994 cmd->header.length = cpu_to_le16(sizeof(*cmd));
1995
1996 memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr));
1997 cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001998 cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma);
Brian Cavagnoloe6007072011-03-17 11:58:44 -07001999 cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv));
2000 for (i = 0; i < mwl8k_tx_queues(priv); i++)
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02002001 cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma);
Lennert Buytenhek4ff64322009-08-03 21:58:39 +02002002 cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02002003 cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002004
2005 rc = mwl8k_post_cmd(hw, &cmd->header);
2006
2007 if (!rc) {
2008 SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr);
2009 priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
Lennert Buytenhek4ff64322009-08-03 21:58:39 +02002010 priv->fw_rev = le32_to_cpu(cmd->fw_rev);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002011 priv->hw_rev = cmd->hw_rev;
Lennert Buytenhek06953232010-01-12 13:49:41 +01002012 mwl8k_set_caps(hw, le32_to_cpu(cmd->caps));
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01002013 priv->ap_macids_supported = 0x00000000;
2014 priv->sta_macids_supported = 0x00000001;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002015 }
2016
2017 kfree(cmd);
2018 return rc;
2019}
2020
2021/*
Lennert Buytenhek42fba212009-10-22 20:21:30 +02002022 * CMD_GET_HW_SPEC (AP version).
2023 */
2024struct mwl8k_cmd_get_hw_spec_ap {
2025 struct mwl8k_cmd_pkt header;
2026 __u8 hw_rev;
2027 __u8 host_interface;
2028 __le16 num_wcb;
2029 __le16 num_mcaddrs;
2030 __u8 perm_addr[ETH_ALEN];
2031 __le16 region_code;
2032 __le16 num_antenna;
2033 __le32 fw_rev;
2034 __le32 wcbbase0;
2035 __le32 rxwrptr;
2036 __le32 rxrdptr;
2037 __le32 ps_cookie;
2038 __le32 wcbbase1;
2039 __le32 wcbbase2;
2040 __le32 wcbbase3;
Brian Cavagnolo952a0e92010-11-12 17:23:51 -08002041 __le32 fw_api_version;
Brian Cavagnolo8a7a5782011-03-17 11:58:42 -07002042 __le32 caps;
2043 __le32 num_of_ampdu_queues;
2044 __le32 wcbbase_ampdu[MWL8K_MAX_AMPDU_QUEUES];
Eric Dumazetba2d3582010-06-02 18:10:09 +00002045} __packed;
Lennert Buytenhek42fba212009-10-22 20:21:30 +02002046
2047static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw)
2048{
2049 struct mwl8k_priv *priv = hw->priv;
2050 struct mwl8k_cmd_get_hw_spec_ap *cmd;
Brian Cavagnolo8a7a5782011-03-17 11:58:42 -07002051 int rc, i;
Brian Cavagnolo952a0e92010-11-12 17:23:51 -08002052 u32 api_version;
Lennert Buytenhek42fba212009-10-22 20:21:30 +02002053
2054 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2055 if (cmd == NULL)
2056 return -ENOMEM;
2057
2058 cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC);
2059 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2060
2061 memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr));
2062 cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
2063
2064 rc = mwl8k_post_cmd(hw, &cmd->header);
2065
2066 if (!rc) {
2067 int off;
2068
Brian Cavagnolo952a0e92010-11-12 17:23:51 -08002069 api_version = le32_to_cpu(cmd->fw_api_version);
2070 if (priv->device_info->fw_api_ap != api_version) {
2071 printk(KERN_ERR "%s: Unsupported fw API version for %s."
2072 " Expected %d got %d.\n", MWL8K_NAME,
2073 priv->device_info->part_name,
2074 priv->device_info->fw_api_ap,
2075 api_version);
2076 rc = -EINVAL;
2077 goto done;
2078 }
Lennert Buytenhek42fba212009-10-22 20:21:30 +02002079 SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr);
2080 priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
2081 priv->fw_rev = le32_to_cpu(cmd->fw_rev);
2082 priv->hw_rev = cmd->hw_rev;
Brian Cavagnolo8a7a5782011-03-17 11:58:42 -07002083 mwl8k_set_caps(hw, le32_to_cpu(cmd->caps));
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01002084 priv->ap_macids_supported = 0x000000ff;
2085 priv->sta_macids_supported = 0x00000000;
Brian Cavagnolo8a7a5782011-03-17 11:58:42 -07002086 priv->num_ampdu_queues = le32_to_cpu(cmd->num_of_ampdu_queues);
2087 if (priv->num_ampdu_queues > MWL8K_MAX_AMPDU_QUEUES) {
2088 wiphy_warn(hw->wiphy, "fw reported %d ampdu queues"
2089 " but we only support %d.\n",
2090 priv->num_ampdu_queues,
2091 MWL8K_MAX_AMPDU_QUEUES);
2092 priv->num_ampdu_queues = MWL8K_MAX_AMPDU_QUEUES;
2093 }
Lennert Buytenhek42fba212009-10-22 20:21:30 +02002094 off = le32_to_cpu(cmd->rxwrptr) & 0xffff;
John W. Linvilleb6037422010-07-20 13:55:00 -04002095 iowrite32(priv->rxq[0].rxd_dma, priv->sram + off);
Lennert Buytenhek42fba212009-10-22 20:21:30 +02002096
2097 off = le32_to_cpu(cmd->rxrdptr) & 0xffff;
John W. Linvilleb6037422010-07-20 13:55:00 -04002098 iowrite32(priv->rxq[0].rxd_dma, priv->sram + off);
Lennert Buytenhek42fba212009-10-22 20:21:30 +02002099
Brian Cavagnolo73b46322011-03-17 11:58:41 -07002100 priv->txq_offset[0] = le32_to_cpu(cmd->wcbbase0) & 0xffff;
2101 priv->txq_offset[1] = le32_to_cpu(cmd->wcbbase1) & 0xffff;
2102 priv->txq_offset[2] = le32_to_cpu(cmd->wcbbase2) & 0xffff;
2103 priv->txq_offset[3] = le32_to_cpu(cmd->wcbbase3) & 0xffff;
Brian Cavagnolo8a7a5782011-03-17 11:58:42 -07002104
2105 for (i = 0; i < priv->num_ampdu_queues; i++)
Brian Cavagnoloe6007072011-03-17 11:58:44 -07002106 priv->txq_offset[i + MWL8K_TX_WMM_QUEUES] =
Brian Cavagnolo8a7a5782011-03-17 11:58:42 -07002107 le32_to_cpu(cmd->wcbbase_ampdu[i]) & 0xffff;
Lennert Buytenhek42fba212009-10-22 20:21:30 +02002108 }
2109
Brian Cavagnolo952a0e92010-11-12 17:23:51 -08002110done:
Lennert Buytenhek42fba212009-10-22 20:21:30 +02002111 kfree(cmd);
2112 return rc;
2113}
2114
2115/*
2116 * CMD_SET_HW_SPEC.
2117 */
2118struct mwl8k_cmd_set_hw_spec {
2119 struct mwl8k_cmd_pkt header;
2120 __u8 hw_rev;
2121 __u8 host_interface;
2122 __le16 num_mcaddrs;
2123 __u8 perm_addr[ETH_ALEN];
2124 __le16 region_code;
2125 __le32 fw_rev;
2126 __le32 ps_cookie;
2127 __le32 caps;
2128 __le32 rx_queue_ptr;
2129 __le32 num_tx_queues;
Brian Cavagnoloe6007072011-03-17 11:58:44 -07002130 __le32 tx_queue_ptrs[MWL8K_MAX_TX_QUEUES];
Lennert Buytenhek42fba212009-10-22 20:21:30 +02002131 __le32 flags;
2132 __le32 num_tx_desc_per_queue;
2133 __le32 total_rxd;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002134} __packed;
Lennert Buytenhek42fba212009-10-22 20:21:30 +02002135
Brian Cavagnolo8a7a5782011-03-17 11:58:42 -07002136/* If enabled, MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY will cause
2137 * packets to expire 500 ms after the timestamp in the tx descriptor. That is,
2138 * the packets that are queued for more than 500ms, will be dropped in the
2139 * hardware. This helps minimizing the issues caused due to head-of-line
2140 * blocking where a slow client can hog the bandwidth and affect traffic to a
2141 * faster client.
2142 */
2143#define MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY 0x00000400
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01002144#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080
2145#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP 0x00000020
2146#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON 0x00000010
Lennert Buytenhek42fba212009-10-22 20:21:30 +02002147
2148static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw)
2149{
2150 struct mwl8k_priv *priv = hw->priv;
2151 struct mwl8k_cmd_set_hw_spec *cmd;
2152 int rc;
2153 int i;
2154
2155 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2156 if (cmd == NULL)
2157 return -ENOMEM;
2158
2159 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_HW_SPEC);
2160 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2161
2162 cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
2163 cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma);
Brian Cavagnoloe6007072011-03-17 11:58:44 -07002164 cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv));
Nishant Sarmukadam85c92052011-02-17 14:45:18 -08002165
2166 /*
2167 * Mac80211 stack has Q0 as highest priority and Q3 as lowest in
2168 * that order. Firmware has Q3 as highest priority and Q0 as lowest
2169 * in that order. Map Q3 of mac80211 to Q0 of firmware so that the
2170 * priority is interpreted the right way in firmware.
2171 */
Brian Cavagnoloe6007072011-03-17 11:58:44 -07002172 for (i = 0; i < mwl8k_tx_queues(priv); i++) {
2173 int j = mwl8k_tx_queues(priv) - 1 - i;
Nishant Sarmukadam85c92052011-02-17 14:45:18 -08002174 cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[j].txd_dma);
2175 }
2176
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01002177 cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT |
2178 MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP |
2179 MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON);
Lennert Buytenhek42fba212009-10-22 20:21:30 +02002180 cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);
2181 cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS);
2182
2183 rc = mwl8k_post_cmd(hw, &cmd->header);
2184 kfree(cmd);
2185
2186 return rc;
2187}
2188
2189/*
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002190 * CMD_MAC_MULTICAST_ADR.
2191 */
2192struct mwl8k_cmd_mac_multicast_adr {
2193 struct mwl8k_cmd_pkt header;
2194 __le16 action;
2195 __le16 numaddr;
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02002196 __u8 addr[0][ETH_ALEN];
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002197};
2198
Lennert Buytenhekd5e30842009-10-22 20:19:41 +02002199#define MWL8K_ENABLE_RX_DIRECTED 0x0001
2200#define MWL8K_ENABLE_RX_MULTICAST 0x0002
2201#define MWL8K_ENABLE_RX_ALL_MULTICAST 0x0004
2202#define MWL8K_ENABLE_RX_BROADCAST 0x0008
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02002203
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02002204static struct mwl8k_cmd_pkt *
Lennert Buytenhek447ced02009-10-22 20:19:50 +02002205__mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti,
Jiri Pirko22bedad2010-04-01 21:22:57 +00002206 struct netdev_hw_addr_list *mc_list)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002207{
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02002208 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002209 struct mwl8k_cmd_mac_multicast_adr *cmd;
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02002210 int size;
Jiri Pirko22bedad2010-04-01 21:22:57 +00002211 int mc_count = 0;
2212
2213 if (mc_list)
2214 mc_count = netdev_hw_addr_list_count(mc_list);
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02002215
Lennert Buytenhek447ced02009-10-22 20:19:50 +02002216 if (allmulti || mc_count > priv->num_mcaddrs) {
Lennert Buytenhekd5e30842009-10-22 20:19:41 +02002217 allmulti = 1;
2218 mc_count = 0;
2219 }
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02002220
2221 size = sizeof(*cmd) + mc_count * ETH_ALEN;
2222
2223 cmd = kzalloc(size, GFP_ATOMIC);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002224 if (cmd == NULL)
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02002225 return NULL;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002226
2227 cmd->header.code = cpu_to_le16(MWL8K_CMD_MAC_MULTICAST_ADR);
2228 cmd->header.length = cpu_to_le16(size);
Lennert Buytenhekd5e30842009-10-22 20:19:41 +02002229 cmd->action = cpu_to_le16(MWL8K_ENABLE_RX_DIRECTED |
2230 MWL8K_ENABLE_RX_BROADCAST);
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02002231
Lennert Buytenhekd5e30842009-10-22 20:19:41 +02002232 if (allmulti) {
2233 cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_ALL_MULTICAST);
2234 } else if (mc_count) {
Jiri Pirko22bedad2010-04-01 21:22:57 +00002235 struct netdev_hw_addr *ha;
2236 int i = 0;
Lennert Buytenhekd5e30842009-10-22 20:19:41 +02002237
2238 cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST);
2239 cmd->numaddr = cpu_to_le16(mc_count);
Jiri Pirko22bedad2010-04-01 21:22:57 +00002240 netdev_hw_addr_list_for_each(ha, mc_list) {
2241 memcpy(cmd->addr[i], ha->addr, ETH_ALEN);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002242 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002243 }
2244
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02002245 return &cmd->header;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002246}
2247
2248/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002249 * CMD_GET_STAT.
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002250 */
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002251struct mwl8k_cmd_get_stat {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002252 struct mwl8k_cmd_pkt header;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002253 __le32 stats[64];
Eric Dumazetba2d3582010-06-02 18:10:09 +00002254} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002255
2256#define MWL8K_STAT_ACK_FAILURE 9
2257#define MWL8K_STAT_RTS_FAILURE 12
2258#define MWL8K_STAT_FCS_ERROR 24
2259#define MWL8K_STAT_RTS_SUCCESS 11
2260
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002261static int mwl8k_cmd_get_stat(struct ieee80211_hw *hw,
2262 struct ieee80211_low_level_stats *stats)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002263{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002264 struct mwl8k_cmd_get_stat *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002265 int rc;
2266
2267 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2268 if (cmd == NULL)
2269 return -ENOMEM;
2270
2271 cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_STAT);
2272 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002273
2274 rc = mwl8k_post_cmd(hw, &cmd->header);
2275 if (!rc) {
2276 stats->dot11ACKFailureCount =
2277 le32_to_cpu(cmd->stats[MWL8K_STAT_ACK_FAILURE]);
2278 stats->dot11RTSFailureCount =
2279 le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_FAILURE]);
2280 stats->dot11FCSErrorCount =
2281 le32_to_cpu(cmd->stats[MWL8K_STAT_FCS_ERROR]);
2282 stats->dot11RTSSuccessCount =
2283 le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_SUCCESS]);
2284 }
2285 kfree(cmd);
2286
2287 return rc;
2288}
2289
2290/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002291 * CMD_RADIO_CONTROL.
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002292 */
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002293struct mwl8k_cmd_radio_control {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002294 struct mwl8k_cmd_pkt header;
2295 __le16 action;
2296 __le16 control;
2297 __le16 radio_on;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002298} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002299
Lennert Buytenhekc46563b2009-07-16 12:14:58 +02002300static int
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002301mwl8k_cmd_radio_control(struct ieee80211_hw *hw, bool enable, bool force)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002302{
2303 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002304 struct mwl8k_cmd_radio_control *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002305 int rc;
2306
Lennert Buytenhekc46563b2009-07-16 12:14:58 +02002307 if (enable == priv->radio_on && !force)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002308 return 0;
2309
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002310 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2311 if (cmd == NULL)
2312 return -ENOMEM;
2313
2314 cmd->header.code = cpu_to_le16(MWL8K_CMD_RADIO_CONTROL);
2315 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2316 cmd->action = cpu_to_le16(MWL8K_CMD_SET);
Lennert Buytenhek68ce3882009-07-16 12:26:57 +02002317 cmd->control = cpu_to_le16(priv->radio_short_preamble ? 3 : 1);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002318 cmd->radio_on = cpu_to_le16(enable ? 0x0001 : 0x0000);
2319
2320 rc = mwl8k_post_cmd(hw, &cmd->header);
2321 kfree(cmd);
2322
2323 if (!rc)
Lennert Buytenhekc46563b2009-07-16 12:14:58 +02002324 priv->radio_on = enable;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002325
2326 return rc;
2327}
2328
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002329static int mwl8k_cmd_radio_disable(struct ieee80211_hw *hw)
Lennert Buytenhekc46563b2009-07-16 12:14:58 +02002330{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002331 return mwl8k_cmd_radio_control(hw, 0, 0);
Lennert Buytenhekc46563b2009-07-16 12:14:58 +02002332}
2333
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002334static int mwl8k_cmd_radio_enable(struct ieee80211_hw *hw)
Lennert Buytenhekc46563b2009-07-16 12:14:58 +02002335{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002336 return mwl8k_cmd_radio_control(hw, 1, 0);
Lennert Buytenhekc46563b2009-07-16 12:14:58 +02002337}
2338
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002339static int
2340mwl8k_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble)
2341{
Lennert Buytenhek99200a992009-11-30 18:31:40 +01002342 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002343
Lennert Buytenhek68ce3882009-07-16 12:26:57 +02002344 priv->radio_short_preamble = short_preamble;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002345
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002346 return mwl8k_cmd_radio_control(hw, 1, 1);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002347}
2348
2349/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002350 * CMD_RF_TX_POWER.
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002351 */
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -08002352#define MWL8K_RF_TX_POWER_LEVEL_TOTAL 8
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002353
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002354struct mwl8k_cmd_rf_tx_power {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002355 struct mwl8k_cmd_pkt header;
2356 __le16 action;
2357 __le16 support_level;
2358 __le16 current_level;
2359 __le16 reserved;
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -08002360 __le16 power_level_list[MWL8K_RF_TX_POWER_LEVEL_TOTAL];
Eric Dumazetba2d3582010-06-02 18:10:09 +00002361} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002362
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002363static int mwl8k_cmd_rf_tx_power(struct ieee80211_hw *hw, int dBm)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002364{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002365 struct mwl8k_cmd_rf_tx_power *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002366 int rc;
2367
2368 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2369 if (cmd == NULL)
2370 return -ENOMEM;
2371
2372 cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_TX_POWER);
2373 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2374 cmd->action = cpu_to_le16(MWL8K_CMD_SET);
2375 cmd->support_level = cpu_to_le16(dBm);
2376
2377 rc = mwl8k_post_cmd(hw, &cmd->header);
2378 kfree(cmd);
2379
2380 return rc;
2381}
2382
2383/*
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -08002384 * CMD_TX_POWER.
2385 */
2386#define MWL8K_TX_POWER_LEVEL_TOTAL 12
2387
2388struct mwl8k_cmd_tx_power {
2389 struct mwl8k_cmd_pkt header;
2390 __le16 action;
2391 __le16 band;
2392 __le16 channel;
2393 __le16 bw;
2394 __le16 sub_ch;
2395 __le16 power_level_list[MWL8K_TX_POWER_LEVEL_TOTAL];
2396} __attribute__((packed));
2397
2398static int mwl8k_cmd_tx_power(struct ieee80211_hw *hw,
2399 struct ieee80211_conf *conf,
2400 unsigned short pwr)
2401{
2402 struct ieee80211_channel *channel = conf->channel;
2403 struct mwl8k_cmd_tx_power *cmd;
2404 int rc;
2405 int i;
2406
2407 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2408 if (cmd == NULL)
2409 return -ENOMEM;
2410
2411 cmd->header.code = cpu_to_le16(MWL8K_CMD_TX_POWER);
2412 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2413 cmd->action = cpu_to_le16(MWL8K_CMD_SET_LIST);
2414
2415 if (channel->band == IEEE80211_BAND_2GHZ)
2416 cmd->band = cpu_to_le16(0x1);
2417 else if (channel->band == IEEE80211_BAND_5GHZ)
2418 cmd->band = cpu_to_le16(0x4);
2419
2420 cmd->channel = channel->hw_value;
2421
2422 if (conf->channel_type == NL80211_CHAN_NO_HT ||
2423 conf->channel_type == NL80211_CHAN_HT20) {
2424 cmd->bw = cpu_to_le16(0x2);
2425 } else {
2426 cmd->bw = cpu_to_le16(0x4);
2427 if (conf->channel_type == NL80211_CHAN_HT40MINUS)
2428 cmd->sub_ch = cpu_to_le16(0x3);
2429 else if (conf->channel_type == NL80211_CHAN_HT40PLUS)
2430 cmd->sub_ch = cpu_to_le16(0x1);
2431 }
2432
2433 for (i = 0; i < MWL8K_TX_POWER_LEVEL_TOTAL; i++)
2434 cmd->power_level_list[i] = cpu_to_le16(pwr);
2435
2436 rc = mwl8k_post_cmd(hw, &cmd->header);
2437 kfree(cmd);
2438
2439 return rc;
2440}
2441
2442/*
Lennert Buytenhek08b06342009-10-22 20:21:33 +02002443 * CMD_RF_ANTENNA.
2444 */
2445struct mwl8k_cmd_rf_antenna {
2446 struct mwl8k_cmd_pkt header;
2447 __le16 antenna;
2448 __le16 mode;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002449} __packed;
Lennert Buytenhek08b06342009-10-22 20:21:33 +02002450
2451#define MWL8K_RF_ANTENNA_RX 1
2452#define MWL8K_RF_ANTENNA_TX 2
2453
2454static int
2455mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask)
2456{
2457 struct mwl8k_cmd_rf_antenna *cmd;
2458 int rc;
2459
2460 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2461 if (cmd == NULL)
2462 return -ENOMEM;
2463
2464 cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_ANTENNA);
2465 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2466 cmd->antenna = cpu_to_le16(antenna);
2467 cmd->mode = cpu_to_le16(mask);
2468
2469 rc = mwl8k_post_cmd(hw, &cmd->header);
2470 kfree(cmd);
2471
2472 return rc;
2473}
2474
2475/*
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01002476 * CMD_SET_BEACON.
2477 */
2478struct mwl8k_cmd_set_beacon {
2479 struct mwl8k_cmd_pkt header;
2480 __le16 beacon_len;
2481 __u8 beacon[0];
2482};
2483
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01002484static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw,
2485 struct ieee80211_vif *vif, u8 *beacon, int len)
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01002486{
2487 struct mwl8k_cmd_set_beacon *cmd;
2488 int rc;
2489
2490 cmd = kzalloc(sizeof(*cmd) + len, GFP_KERNEL);
2491 if (cmd == NULL)
2492 return -ENOMEM;
2493
2494 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_BEACON);
2495 cmd->header.length = cpu_to_le16(sizeof(*cmd) + len);
2496 cmd->beacon_len = cpu_to_le16(len);
2497 memcpy(cmd->beacon, beacon, len);
2498
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01002499 rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01002500 kfree(cmd);
2501
2502 return rc;
2503}
2504
2505/*
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002506 * CMD_SET_PRE_SCAN.
2507 */
2508struct mwl8k_cmd_set_pre_scan {
2509 struct mwl8k_cmd_pkt header;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002510} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002511
2512static int mwl8k_cmd_set_pre_scan(struct ieee80211_hw *hw)
2513{
2514 struct mwl8k_cmd_set_pre_scan *cmd;
2515 int rc;
2516
2517 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2518 if (cmd == NULL)
2519 return -ENOMEM;
2520
2521 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_PRE_SCAN);
2522 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2523
2524 rc = mwl8k_post_cmd(hw, &cmd->header);
2525 kfree(cmd);
2526
2527 return rc;
2528}
2529
2530/*
2531 * CMD_SET_POST_SCAN.
2532 */
2533struct mwl8k_cmd_set_post_scan {
2534 struct mwl8k_cmd_pkt header;
2535 __le32 isibss;
Lennert Buytenhekd89173f2009-07-16 09:54:27 +02002536 __u8 bssid[ETH_ALEN];
Eric Dumazetba2d3582010-06-02 18:10:09 +00002537} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002538
2539static int
Lennert Buytenhek0a11dfc2010-01-04 21:55:21 +01002540mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002541{
2542 struct mwl8k_cmd_set_post_scan *cmd;
2543 int rc;
2544
2545 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2546 if (cmd == NULL)
2547 return -ENOMEM;
2548
2549 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_POST_SCAN);
2550 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2551 cmd->isibss = 0;
Lennert Buytenhekd89173f2009-07-16 09:54:27 +02002552 memcpy(cmd->bssid, mac, ETH_ALEN);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002553
2554 rc = mwl8k_post_cmd(hw, &cmd->header);
2555 kfree(cmd);
2556
2557 return rc;
2558}
2559
2560/*
2561 * CMD_SET_RF_CHANNEL.
2562 */
2563struct mwl8k_cmd_set_rf_channel {
2564 struct mwl8k_cmd_pkt header;
2565 __le16 action;
2566 __u8 current_channel;
2567 __le32 channel_flags;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002568} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002569
2570static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw,
Lennert Buytenhek610677d2010-01-04 21:56:46 +01002571 struct ieee80211_conf *conf)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002572{
Lennert Buytenhek610677d2010-01-04 21:56:46 +01002573 struct ieee80211_channel *channel = conf->channel;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002574 struct mwl8k_cmd_set_rf_channel *cmd;
2575 int rc;
2576
2577 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2578 if (cmd == NULL)
2579 return -ENOMEM;
2580
2581 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RF_CHANNEL);
2582 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2583 cmd->action = cpu_to_le16(MWL8K_CMD_SET);
2584 cmd->current_channel = channel->hw_value;
Lennert Buytenhek610677d2010-01-04 21:56:46 +01002585
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002586 if (channel->band == IEEE80211_BAND_2GHZ)
Lennert Buytenhek610677d2010-01-04 21:56:46 +01002587 cmd->channel_flags |= cpu_to_le32(0x00000001);
Lennert Buytenhek42574ea2010-01-12 13:49:18 +01002588 else if (channel->band == IEEE80211_BAND_5GHZ)
2589 cmd->channel_flags |= cpu_to_le32(0x00000004);
Lennert Buytenhek610677d2010-01-04 21:56:46 +01002590
2591 if (conf->channel_type == NL80211_CHAN_NO_HT ||
2592 conf->channel_type == NL80211_CHAN_HT20)
2593 cmd->channel_flags |= cpu_to_le32(0x00000080);
2594 else if (conf->channel_type == NL80211_CHAN_HT40MINUS)
2595 cmd->channel_flags |= cpu_to_le32(0x000001900);
2596 else if (conf->channel_type == NL80211_CHAN_HT40PLUS)
2597 cmd->channel_flags |= cpu_to_le32(0x000000900);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002598
2599 rc = mwl8k_post_cmd(hw, &cmd->header);
2600 kfree(cmd);
2601
2602 return rc;
2603}
2604
2605/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002606 * CMD_SET_AID.
2607 */
2608#define MWL8K_FRAME_PROT_DISABLED 0x00
2609#define MWL8K_FRAME_PROT_11G 0x07
2610#define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY 0x02
2611#define MWL8K_FRAME_PROT_11N_HT_ALL 0x06
2612
2613struct mwl8k_cmd_update_set_aid {
2614 struct mwl8k_cmd_pkt header;
2615 __le16 aid;
2616
2617 /* AP's MAC address (BSSID) */
2618 __u8 bssid[ETH_ALEN];
2619 __le16 protection_mode;
2620 __u8 supp_rates[14];
Eric Dumazetba2d3582010-06-02 18:10:09 +00002621} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002622
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01002623static void legacy_rate_mask_to_array(u8 *rates, u32 mask)
2624{
2625 int i;
2626 int j;
2627
2628 /*
2629 * Clear nonstandard rates 4 and 13.
2630 */
2631 mask &= 0x1fef;
2632
2633 for (i = 0, j = 0; i < 14; i++) {
2634 if (mask & (1 << i))
Lennert Buytenhek777ad372010-01-12 13:48:04 +01002635 rates[j++] = mwl8k_rates_24[i].hw_value;
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01002636 }
2637}
2638
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002639static int
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01002640mwl8k_cmd_set_aid(struct ieee80211_hw *hw,
2641 struct ieee80211_vif *vif, u32 legacy_rate_mask)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002642{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002643 struct mwl8k_cmd_update_set_aid *cmd;
2644 u16 prot_mode;
2645 int rc;
2646
2647 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2648 if (cmd == NULL)
2649 return -ENOMEM;
2650
2651 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID);
2652 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenhek7dc6a7a2009-11-30 18:33:09 +01002653 cmd->aid = cpu_to_le16(vif->bss_conf.aid);
Lennert Buytenhek0a11dfc2010-01-04 21:55:21 +01002654 memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002655
Lennert Buytenhek7dc6a7a2009-11-30 18:33:09 +01002656 if (vif->bss_conf.use_cts_prot) {
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002657 prot_mode = MWL8K_FRAME_PROT_11G;
2658 } else {
Lennert Buytenhek7dc6a7a2009-11-30 18:33:09 +01002659 switch (vif->bss_conf.ht_operation_mode &
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002660 IEEE80211_HT_OP_MODE_PROTECTION) {
2661 case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
2662 prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY;
2663 break;
2664 case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
2665 prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL;
2666 break;
2667 default:
2668 prot_mode = MWL8K_FRAME_PROT_DISABLED;
2669 break;
2670 }
2671 }
2672 cmd->protection_mode = cpu_to_le16(prot_mode);
2673
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01002674 legacy_rate_mask_to_array(cmd->supp_rates, legacy_rate_mask);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002675
2676 rc = mwl8k_post_cmd(hw, &cmd->header);
2677 kfree(cmd);
2678
2679 return rc;
2680}
2681
2682/*
2683 * CMD_SET_RATE.
2684 */
2685struct mwl8k_cmd_set_rate {
2686 struct mwl8k_cmd_pkt header;
2687 __u8 legacy_rates[14];
2688
2689 /* Bitmap for supported MCS codes. */
2690 __u8 mcs_set[16];
2691 __u8 reserved[16];
Eric Dumazetba2d3582010-06-02 18:10:09 +00002692} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002693
2694static int
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01002695mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
Lennert Buytenhek13935e22010-01-04 21:57:59 +01002696 u32 legacy_rate_mask, u8 *mcs_rates)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002697{
2698 struct mwl8k_cmd_set_rate *cmd;
2699 int rc;
2700
2701 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2702 if (cmd == NULL)
2703 return -ENOMEM;
2704
2705 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE);
2706 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01002707 legacy_rate_mask_to_array(cmd->legacy_rates, legacy_rate_mask);
Lennert Buytenhek13935e22010-01-04 21:57:59 +01002708 memcpy(cmd->mcs_set, mcs_rates, 16);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002709
2710 rc = mwl8k_post_cmd(hw, &cmd->header);
2711 kfree(cmd);
2712
2713 return rc;
2714}
2715
2716/*
2717 * CMD_FINALIZE_JOIN.
2718 */
2719#define MWL8K_FJ_BEACON_MAXLEN 128
2720
2721struct mwl8k_cmd_finalize_join {
2722 struct mwl8k_cmd_pkt header;
2723 __le32 sleep_interval; /* Number of beacon periods to sleep */
2724 __u8 beacon_data[MWL8K_FJ_BEACON_MAXLEN];
Eric Dumazetba2d3582010-06-02 18:10:09 +00002725} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002726
2727static int mwl8k_cmd_finalize_join(struct ieee80211_hw *hw, void *frame,
2728 int framelen, int dtim)
2729{
2730 struct mwl8k_cmd_finalize_join *cmd;
2731 struct ieee80211_mgmt *payload = frame;
2732 int payload_len;
2733 int rc;
2734
2735 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2736 if (cmd == NULL)
2737 return -ENOMEM;
2738
2739 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN);
2740 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2741 cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1);
2742
2743 payload_len = framelen - ieee80211_hdrlen(payload->frame_control);
2744 if (payload_len < 0)
2745 payload_len = 0;
2746 else if (payload_len > MWL8K_FJ_BEACON_MAXLEN)
2747 payload_len = MWL8K_FJ_BEACON_MAXLEN;
2748
2749 memcpy(cmd->beacon_data, &payload->u.beacon, payload_len);
2750
2751 rc = mwl8k_post_cmd(hw, &cmd->header);
2752 kfree(cmd);
2753
2754 return rc;
2755}
2756
2757/*
2758 * CMD_SET_RTS_THRESHOLD.
2759 */
2760struct mwl8k_cmd_set_rts_threshold {
2761 struct mwl8k_cmd_pkt header;
2762 __le16 action;
2763 __le16 threshold;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002764} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002765
Lennert Buytenhekc2c2b122010-01-08 18:27:59 +01002766static int
2767mwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, int rts_thresh)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002768{
2769 struct mwl8k_cmd_set_rts_threshold *cmd;
2770 int rc;
2771
2772 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2773 if (cmd == NULL)
2774 return -ENOMEM;
2775
2776 cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD);
2777 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenhekc2c2b122010-01-08 18:27:59 +01002778 cmd->action = cpu_to_le16(MWL8K_CMD_SET);
2779 cmd->threshold = cpu_to_le16(rts_thresh);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002780
2781 rc = mwl8k_post_cmd(hw, &cmd->header);
2782 kfree(cmd);
2783
2784 return rc;
2785}
2786
2787/*
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002788 * CMD_SET_SLOT.
2789 */
2790struct mwl8k_cmd_set_slot {
2791 struct mwl8k_cmd_pkt header;
2792 __le16 action;
2793 __u8 short_slot;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002794} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002795
Lennert Buytenhek5539bb52009-07-16 16:06:53 +02002796static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002797{
2798 struct mwl8k_cmd_set_slot *cmd;
2799 int rc;
2800
2801 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2802 if (cmd == NULL)
2803 return -ENOMEM;
2804
2805 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT);
2806 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2807 cmd->action = cpu_to_le16(MWL8K_CMD_SET);
Lennert Buytenhek5539bb52009-07-16 16:06:53 +02002808 cmd->short_slot = short_slot_time;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002809
2810 rc = mwl8k_post_cmd(hw, &cmd->header);
2811 kfree(cmd);
2812
2813 return rc;
2814}
2815
2816/*
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002817 * CMD_SET_EDCA_PARAMS.
2818 */
2819struct mwl8k_cmd_set_edca_params {
2820 struct mwl8k_cmd_pkt header;
2821
2822 /* See MWL8K_SET_EDCA_XXX below */
2823 __le16 action;
2824
2825 /* TX opportunity in units of 32 us */
2826 __le16 txop;
2827
Lennert Buytenhek2e484c82009-10-22 20:21:43 +02002828 union {
2829 struct {
2830 /* Log exponent of max contention period: 0...15 */
2831 __le32 log_cw_max;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002832
Lennert Buytenhek2e484c82009-10-22 20:21:43 +02002833 /* Log exponent of min contention period: 0...15 */
2834 __le32 log_cw_min;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002835
Lennert Buytenhek2e484c82009-10-22 20:21:43 +02002836 /* Adaptive interframe spacing in units of 32us */
2837 __u8 aifs;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002838
Lennert Buytenhek2e484c82009-10-22 20:21:43 +02002839 /* TX queue to configure */
2840 __u8 txq;
2841 } ap;
2842 struct {
2843 /* Log exponent of max contention period: 0...15 */
2844 __u8 log_cw_max;
2845
2846 /* Log exponent of min contention period: 0...15 */
2847 __u8 log_cw_min;
2848
2849 /* Adaptive interframe spacing in units of 32us */
2850 __u8 aifs;
2851
2852 /* TX queue to configure */
2853 __u8 txq;
2854 } sta;
2855 };
Eric Dumazetba2d3582010-06-02 18:10:09 +00002856} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002857
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002858#define MWL8K_SET_EDCA_CW 0x01
2859#define MWL8K_SET_EDCA_TXOP 0x02
2860#define MWL8K_SET_EDCA_AIFS 0x04
2861
2862#define MWL8K_SET_EDCA_ALL (MWL8K_SET_EDCA_CW | \
2863 MWL8K_SET_EDCA_TXOP | \
2864 MWL8K_SET_EDCA_AIFS)
2865
2866static int
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002867mwl8k_cmd_set_edca_params(struct ieee80211_hw *hw, __u8 qnum,
2868 __u16 cw_min, __u16 cw_max,
2869 __u8 aifs, __u16 txop)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002870{
Lennert Buytenhek2e484c82009-10-22 20:21:43 +02002871 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002872 struct mwl8k_cmd_set_edca_params *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002873 int rc;
2874
2875 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2876 if (cmd == NULL)
2877 return -ENOMEM;
2878
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002879 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS);
2880 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002881 cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL);
2882 cmd->txop = cpu_to_le16(txop);
Lennert Buytenhek2e484c82009-10-22 20:21:43 +02002883 if (priv->ap_fw) {
2884 cmd->ap.log_cw_max = cpu_to_le32(ilog2(cw_max + 1));
2885 cmd->ap.log_cw_min = cpu_to_le32(ilog2(cw_min + 1));
2886 cmd->ap.aifs = aifs;
2887 cmd->ap.txq = qnum;
2888 } else {
2889 cmd->sta.log_cw_max = (u8)ilog2(cw_max + 1);
2890 cmd->sta.log_cw_min = (u8)ilog2(cw_min + 1);
2891 cmd->sta.aifs = aifs;
2892 cmd->sta.txq = qnum;
2893 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002894
2895 rc = mwl8k_post_cmd(hw, &cmd->header);
2896 kfree(cmd);
2897
2898 return rc;
2899}
2900
2901/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002902 * CMD_SET_WMM_MODE.
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002903 */
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002904struct mwl8k_cmd_set_wmm_mode {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002905 struct mwl8k_cmd_pkt header;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002906 __le16 action;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002907} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002908
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002909static int mwl8k_cmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002910{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002911 struct mwl8k_priv *priv = hw->priv;
2912 struct mwl8k_cmd_set_wmm_mode *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002913 int rc;
2914
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002915 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2916 if (cmd == NULL)
2917 return -ENOMEM;
2918
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002919 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_WMM_MODE);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002920 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002921 cmd->action = cpu_to_le16(!!enable);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002922
2923 rc = mwl8k_post_cmd(hw, &cmd->header);
2924 kfree(cmd);
Lennert Buytenhek16cec432009-11-30 18:14:23 +01002925
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002926 if (!rc)
2927 priv->wmm_enabled = enable;
2928
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002929 return rc;
2930}
2931
2932/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002933 * CMD_MIMO_CONFIG.
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002934 */
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002935struct mwl8k_cmd_mimo_config {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002936 struct mwl8k_cmd_pkt header;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002937 __le32 action;
2938 __u8 rx_antenna_map;
2939 __u8 tx_antenna_map;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002940} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002941
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002942static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002943{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002944 struct mwl8k_cmd_mimo_config *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002945 int rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002946
2947 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2948 if (cmd == NULL)
2949 return -ENOMEM;
2950
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002951 cmd->header.code = cpu_to_le16(MWL8K_CMD_MIMO_CONFIG);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002952 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002953 cmd->action = cpu_to_le32((u32)MWL8K_CMD_SET);
2954 cmd->rx_antenna_map = rx;
2955 cmd->tx_antenna_map = tx;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002956
2957 rc = mwl8k_post_cmd(hw, &cmd->header);
2958 kfree(cmd);
2959
2960 return rc;
2961}
2962
2963/*
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01002964 * CMD_USE_FIXED_RATE (STA version).
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002965 */
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01002966struct mwl8k_cmd_use_fixed_rate_sta {
2967 struct mwl8k_cmd_pkt header;
2968 __le32 action;
2969 __le32 allow_rate_drop;
2970 __le32 num_rates;
2971 struct {
2972 __le32 is_ht_rate;
2973 __le32 enable_retry;
2974 __le32 rate;
2975 __le32 retry_count;
2976 } rate_entry[8];
2977 __le32 rate_type;
2978 __le32 reserved1;
2979 __le32 reserved2;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002980} __packed;
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01002981
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002982#define MWL8K_USE_AUTO_RATE 0x0002
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01002983#define MWL8K_UCAST_RATE 0
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002984
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01002985static int mwl8k_cmd_use_fixed_rate_sta(struct ieee80211_hw *hw)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002986{
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01002987 struct mwl8k_cmd_use_fixed_rate_sta *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002988 int rc;
2989
2990 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2991 if (cmd == NULL)
2992 return -ENOMEM;
2993
2994 cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE);
2995 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01002996 cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE);
2997 cmd->rate_type = cpu_to_le32(MWL8K_UCAST_RATE);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002998
2999 rc = mwl8k_post_cmd(hw, &cmd->header);
3000 kfree(cmd);
3001
3002 return rc;
3003}
3004
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003005/*
Lennert Buytenhek088aab82010-01-08 18:30:36 +01003006 * CMD_USE_FIXED_RATE (AP version).
3007 */
3008struct mwl8k_cmd_use_fixed_rate_ap {
3009 struct mwl8k_cmd_pkt header;
3010 __le32 action;
3011 __le32 allow_rate_drop;
3012 __le32 num_rates;
3013 struct mwl8k_rate_entry_ap {
3014 __le32 is_ht_rate;
3015 __le32 enable_retry;
3016 __le32 rate;
3017 __le32 retry_count;
3018 } rate_entry[4];
3019 u8 multicast_rate;
3020 u8 multicast_rate_type;
3021 u8 management_rate;
Eric Dumazetba2d3582010-06-02 18:10:09 +00003022} __packed;
Lennert Buytenhek088aab82010-01-08 18:30:36 +01003023
3024static int
3025mwl8k_cmd_use_fixed_rate_ap(struct ieee80211_hw *hw, int mcast, int mgmt)
3026{
3027 struct mwl8k_cmd_use_fixed_rate_ap *cmd;
3028 int rc;
3029
3030 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3031 if (cmd == NULL)
3032 return -ENOMEM;
3033
3034 cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE);
3035 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3036 cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE);
3037 cmd->multicast_rate = mcast;
3038 cmd->management_rate = mgmt;
3039
3040 rc = mwl8k_post_cmd(hw, &cmd->header);
3041 kfree(cmd);
3042
3043 return rc;
3044}
3045
3046/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003047 * CMD_ENABLE_SNIFFER.
3048 */
3049struct mwl8k_cmd_enable_sniffer {
3050 struct mwl8k_cmd_pkt header;
3051 __le32 action;
Eric Dumazetba2d3582010-06-02 18:10:09 +00003052} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003053
3054static int mwl8k_cmd_enable_sniffer(struct ieee80211_hw *hw, bool enable)
3055{
3056 struct mwl8k_cmd_enable_sniffer *cmd;
3057 int rc;
3058
3059 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3060 if (cmd == NULL)
3061 return -ENOMEM;
3062
3063 cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER);
3064 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3065 cmd->action = cpu_to_le32(!!enable);
3066
3067 rc = mwl8k_post_cmd(hw, &cmd->header);
3068 kfree(cmd);
3069
3070 return rc;
3071}
3072
3073/*
3074 * CMD_SET_MAC_ADDR.
3075 */
3076struct mwl8k_cmd_set_mac_addr {
3077 struct mwl8k_cmd_pkt header;
3078 union {
3079 struct {
3080 __le16 mac_type;
3081 __u8 mac_addr[ETH_ALEN];
3082 } mbss;
3083 __u8 mac_addr[ETH_ALEN];
3084 };
Eric Dumazetba2d3582010-06-02 18:10:09 +00003085} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003086
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01003087#define MWL8K_MAC_TYPE_PRIMARY_CLIENT 0
3088#define MWL8K_MAC_TYPE_SECONDARY_CLIENT 1
3089#define MWL8K_MAC_TYPE_PRIMARY_AP 2
3090#define MWL8K_MAC_TYPE_SECONDARY_AP 3
Lennert Buytenheka9e00b12010-01-08 18:31:30 +01003091
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01003092static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw,
3093 struct ieee80211_vif *vif, u8 *mac)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003094{
3095 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01003096 struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003097 struct mwl8k_cmd_set_mac_addr *cmd;
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01003098 int mac_type;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003099 int rc;
3100
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01003101 mac_type = MWL8K_MAC_TYPE_PRIMARY_AP;
3102 if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) {
3103 if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported))
3104 mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT;
3105 else
3106 mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT;
3107 } else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) {
3108 if (mwl8k_vif->macid + 1 == ffs(priv->ap_macids_supported))
3109 mac_type = MWL8K_MAC_TYPE_PRIMARY_AP;
3110 else
3111 mac_type = MWL8K_MAC_TYPE_SECONDARY_AP;
3112 }
3113
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003114 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3115 if (cmd == NULL)
3116 return -ENOMEM;
3117
3118 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR);
3119 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3120 if (priv->ap_fw) {
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01003121 cmd->mbss.mac_type = cpu_to_le16(mac_type);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003122 memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN);
3123 } else {
3124 memcpy(cmd->mac_addr, mac, ETH_ALEN);
3125 }
3126
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01003127 rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003128 kfree(cmd);
3129
3130 return rc;
3131}
3132
3133/*
3134 * CMD_SET_RATEADAPT_MODE.
3135 */
3136struct mwl8k_cmd_set_rate_adapt_mode {
3137 struct mwl8k_cmd_pkt header;
3138 __le16 action;
3139 __le16 mode;
Eric Dumazetba2d3582010-06-02 18:10:09 +00003140} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003141
3142static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode)
3143{
3144 struct mwl8k_cmd_set_rate_adapt_mode *cmd;
3145 int rc;
3146
3147 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3148 if (cmd == NULL)
3149 return -ENOMEM;
3150
3151 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATEADAPT_MODE);
3152 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3153 cmd->action = cpu_to_le16(MWL8K_CMD_SET);
3154 cmd->mode = cpu_to_le16(mode);
3155
3156 rc = mwl8k_post_cmd(hw, &cmd->header);
3157 kfree(cmd);
3158
3159 return rc;
3160}
3161
3162/*
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01003163 * CMD_BSS_START.
3164 */
3165struct mwl8k_cmd_bss_start {
3166 struct mwl8k_cmd_pkt header;
3167 __le32 enable;
Eric Dumazetba2d3582010-06-02 18:10:09 +00003168} __packed;
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01003169
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01003170static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw,
3171 struct ieee80211_vif *vif, int enable)
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01003172{
3173 struct mwl8k_cmd_bss_start *cmd;
3174 int rc;
3175
3176 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3177 if (cmd == NULL)
3178 return -ENOMEM;
3179
3180 cmd->header.code = cpu_to_le16(MWL8K_CMD_BSS_START);
3181 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3182 cmd->enable = cpu_to_le32(enable);
3183
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01003184 rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01003185 kfree(cmd);
3186
3187 return rc;
3188}
3189
3190/*
Nishant Sarmukadam5faa1af2011-03-17 11:58:43 -07003191 * CMD_BASTREAM.
3192 */
3193
3194/*
3195 * UPSTREAM is tx direction
3196 */
3197#define BASTREAM_FLAG_DIRECTION_UPSTREAM 0x00
3198#define BASTREAM_FLAG_IMMEDIATE_TYPE 0x01
3199
3200enum {
3201 MWL8K_BA_CREATE,
3202 MWL8K_BA_UPDATE,
3203 MWL8K_BA_DESTROY,
3204 MWL8K_BA_FLUSH,
3205 MWL8K_BA_CHECK,
3206} ba_stream_action_type;
3207
3208
3209struct mwl8k_create_ba_stream {
3210 __le32 flags;
3211 __le32 idle_thrs;
3212 __le32 bar_thrs;
3213 __le32 window_size;
3214 u8 peer_mac_addr[6];
3215 u8 dialog_token;
3216 u8 tid;
3217 u8 queue_id;
3218 u8 param_info;
3219 __le32 ba_context;
3220 u8 reset_seq_no_flag;
3221 __le16 curr_seq_no;
3222 u8 sta_src_mac_addr[6];
3223} __packed;
3224
3225struct mwl8k_destroy_ba_stream {
3226 __le32 flags;
3227 __le32 ba_context;
3228} __packed;
3229
3230struct mwl8k_cmd_bastream {
3231 struct mwl8k_cmd_pkt header;
3232 __le32 action;
3233 union {
3234 struct mwl8k_create_ba_stream create_params;
3235 struct mwl8k_destroy_ba_stream destroy_params;
3236 };
3237} __packed;
3238
3239static int
3240mwl8k_check_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream)
3241{
3242 struct mwl8k_cmd_bastream *cmd;
3243 int rc;
3244
3245 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3246 if (cmd == NULL)
3247 return -ENOMEM;
3248
3249 cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM);
3250 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3251
3252 cmd->action = cpu_to_le32(MWL8K_BA_CHECK);
3253
3254 cmd->create_params.queue_id = stream->idx;
3255 memcpy(&cmd->create_params.peer_mac_addr[0], stream->sta->addr,
3256 ETH_ALEN);
3257 cmd->create_params.tid = stream->tid;
3258
3259 cmd->create_params.flags =
3260 cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE) |
3261 cpu_to_le32(BASTREAM_FLAG_DIRECTION_UPSTREAM);
3262
3263 rc = mwl8k_post_cmd(hw, &cmd->header);
3264
3265 kfree(cmd);
3266
3267 return rc;
3268}
3269
3270static int
3271mwl8k_create_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream,
3272 u8 buf_size)
3273{
3274 struct mwl8k_cmd_bastream *cmd;
3275 int rc;
3276
3277 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3278 if (cmd == NULL)
3279 return -ENOMEM;
3280
3281
3282 cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM);
3283 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3284
3285 cmd->action = cpu_to_le32(MWL8K_BA_CREATE);
3286
3287 cmd->create_params.bar_thrs = cpu_to_le32((u32)buf_size);
3288 cmd->create_params.window_size = cpu_to_le32((u32)buf_size);
3289 cmd->create_params.queue_id = stream->idx;
3290
3291 memcpy(cmd->create_params.peer_mac_addr, stream->sta->addr, ETH_ALEN);
3292 cmd->create_params.tid = stream->tid;
3293 cmd->create_params.curr_seq_no = cpu_to_le16(0);
3294 cmd->create_params.reset_seq_no_flag = 1;
3295
3296 cmd->create_params.param_info =
3297 (stream->sta->ht_cap.ampdu_factor &
3298 IEEE80211_HT_AMPDU_PARM_FACTOR) |
3299 ((stream->sta->ht_cap.ampdu_density << 2) &
3300 IEEE80211_HT_AMPDU_PARM_DENSITY);
3301
3302 cmd->create_params.flags =
3303 cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE |
3304 BASTREAM_FLAG_DIRECTION_UPSTREAM);
3305
3306 rc = mwl8k_post_cmd(hw, &cmd->header);
3307
3308 wiphy_debug(hw->wiphy, "Created a BA stream for %pM : tid %d\n",
3309 stream->sta->addr, stream->tid);
3310 kfree(cmd);
3311
3312 return rc;
3313}
3314
3315static void mwl8k_destroy_ba(struct ieee80211_hw *hw,
3316 struct mwl8k_ampdu_stream *stream)
3317{
3318 struct mwl8k_cmd_bastream *cmd;
3319
3320 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3321 if (cmd == NULL)
3322 return;
3323
3324 cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM);
3325 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3326 cmd->action = cpu_to_le32(MWL8K_BA_DESTROY);
3327
3328 cmd->destroy_params.ba_context = cpu_to_le32(stream->idx);
3329 mwl8k_post_cmd(hw, &cmd->header);
3330
3331 wiphy_debug(hw->wiphy, "Deleted BA stream index %d\n", stream->idx);
3332
3333 kfree(cmd);
3334}
3335
3336/*
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003337 * CMD_SET_NEW_STN.
3338 */
3339struct mwl8k_cmd_set_new_stn {
3340 struct mwl8k_cmd_pkt header;
3341 __le16 aid;
3342 __u8 mac_addr[6];
3343 __le16 stn_id;
3344 __le16 action;
3345 __le16 rsvd;
3346 __le32 legacy_rates;
3347 __u8 ht_rates[4];
3348 __le16 cap_info;
3349 __le16 ht_capabilities_info;
3350 __u8 mac_ht_param_info;
3351 __u8 rev;
3352 __u8 control_channel;
3353 __u8 add_channel;
3354 __le16 op_mode;
3355 __le16 stbc;
3356 __u8 add_qos_info;
3357 __u8 is_qos_sta;
3358 __le32 fw_sta_ptr;
Eric Dumazetba2d3582010-06-02 18:10:09 +00003359} __packed;
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003360
3361#define MWL8K_STA_ACTION_ADD 0
3362#define MWL8K_STA_ACTION_REMOVE 2
3363
3364static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw,
3365 struct ieee80211_vif *vif,
3366 struct ieee80211_sta *sta)
3367{
3368 struct mwl8k_cmd_set_new_stn *cmd;
Lennert Buytenhek8707d022010-01-12 13:49:15 +01003369 u32 rates;
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003370 int rc;
3371
3372 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3373 if (cmd == NULL)
3374 return -ENOMEM;
3375
3376 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
3377 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3378 cmd->aid = cpu_to_le16(sta->aid);
3379 memcpy(cmd->mac_addr, sta->addr, ETH_ALEN);
3380 cmd->stn_id = cpu_to_le16(sta->aid);
3381 cmd->action = cpu_to_le16(MWL8K_STA_ACTION_ADD);
Lennert Buytenhek8707d022010-01-12 13:49:15 +01003382 if (hw->conf.channel->band == IEEE80211_BAND_2GHZ)
3383 rates = sta->supp_rates[IEEE80211_BAND_2GHZ];
3384 else
3385 rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5;
3386 cmd->legacy_rates = cpu_to_le32(rates);
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003387 if (sta->ht_cap.ht_supported) {
3388 cmd->ht_rates[0] = sta->ht_cap.mcs.rx_mask[0];
3389 cmd->ht_rates[1] = sta->ht_cap.mcs.rx_mask[1];
3390 cmd->ht_rates[2] = sta->ht_cap.mcs.rx_mask[2];
3391 cmd->ht_rates[3] = sta->ht_cap.mcs.rx_mask[3];
3392 cmd->ht_capabilities_info = cpu_to_le16(sta->ht_cap.cap);
3393 cmd->mac_ht_param_info = (sta->ht_cap.ampdu_factor & 3) |
3394 ((sta->ht_cap.ampdu_density & 7) << 2);
3395 cmd->is_qos_sta = 1;
3396 }
3397
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01003398 rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003399 kfree(cmd);
3400
3401 return rc;
3402}
3403
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01003404static int mwl8k_cmd_set_new_stn_add_self(struct ieee80211_hw *hw,
3405 struct ieee80211_vif *vif)
3406{
3407 struct mwl8k_cmd_set_new_stn *cmd;
3408 int rc;
3409
3410 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3411 if (cmd == NULL)
3412 return -ENOMEM;
3413
3414 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
3415 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3416 memcpy(cmd->mac_addr, vif->addr, ETH_ALEN);
3417
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01003418 rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01003419 kfree(cmd);
3420
3421 return rc;
3422}
3423
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003424static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw,
3425 struct ieee80211_vif *vif, u8 *addr)
3426{
3427 struct mwl8k_cmd_set_new_stn *cmd;
3428 int rc;
3429
3430 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3431 if (cmd == NULL)
3432 return -ENOMEM;
3433
3434 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
3435 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3436 memcpy(cmd->mac_addr, addr, ETH_ALEN);
3437 cmd->action = cpu_to_le16(MWL8K_STA_ACTION_REMOVE);
3438
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01003439 rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003440 kfree(cmd);
3441
3442 return rc;
3443}
3444
3445/*
Nishant Sarmukadamfcdc4032010-12-30 11:23:34 -08003446 * CMD_UPDATE_ENCRYPTION.
3447 */
3448
3449#define MAX_ENCR_KEY_LENGTH 16
3450#define MIC_KEY_LENGTH 8
3451
3452struct mwl8k_cmd_update_encryption {
3453 struct mwl8k_cmd_pkt header;
3454
3455 __le32 action;
3456 __le32 reserved;
3457 __u8 mac_addr[6];
3458 __u8 encr_type;
3459
3460} __attribute__((packed));
3461
3462struct mwl8k_cmd_set_key {
3463 struct mwl8k_cmd_pkt header;
3464
3465 __le32 action;
3466 __le32 reserved;
3467 __le16 length;
3468 __le16 key_type_id;
3469 __le32 key_info;
3470 __le32 key_id;
3471 __le16 key_len;
3472 __u8 key_material[MAX_ENCR_KEY_LENGTH];
3473 __u8 tkip_tx_mic_key[MIC_KEY_LENGTH];
3474 __u8 tkip_rx_mic_key[MIC_KEY_LENGTH];
3475 __le16 tkip_rsc_low;
3476 __le32 tkip_rsc_high;
3477 __le16 tkip_tsc_low;
3478 __le32 tkip_tsc_high;
3479 __u8 mac_addr[6];
3480} __attribute__((packed));
3481
3482enum {
3483 MWL8K_ENCR_ENABLE,
3484 MWL8K_ENCR_SET_KEY,
3485 MWL8K_ENCR_REMOVE_KEY,
3486 MWL8K_ENCR_SET_GROUP_KEY,
3487};
3488
3489#define MWL8K_UPDATE_ENCRYPTION_TYPE_WEP 0
3490#define MWL8K_UPDATE_ENCRYPTION_TYPE_DISABLE 1
3491#define MWL8K_UPDATE_ENCRYPTION_TYPE_TKIP 4
3492#define MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED 7
3493#define MWL8K_UPDATE_ENCRYPTION_TYPE_AES 8
3494
3495enum {
3496 MWL8K_ALG_WEP,
3497 MWL8K_ALG_TKIP,
3498 MWL8K_ALG_CCMP,
3499};
3500
3501#define MWL8K_KEY_FLAG_TXGROUPKEY 0x00000004
3502#define MWL8K_KEY_FLAG_PAIRWISE 0x00000008
3503#define MWL8K_KEY_FLAG_TSC_VALID 0x00000040
3504#define MWL8K_KEY_FLAG_WEP_TXKEY 0x01000000
3505#define MWL8K_KEY_FLAG_MICKEY_VALID 0x02000000
3506
3507static int mwl8k_cmd_update_encryption_enable(struct ieee80211_hw *hw,
3508 struct ieee80211_vif *vif,
3509 u8 *addr,
3510 u8 encr_type)
3511{
3512 struct mwl8k_cmd_update_encryption *cmd;
3513 int rc;
3514
3515 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3516 if (cmd == NULL)
3517 return -ENOMEM;
3518
3519 cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION);
3520 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3521 cmd->action = cpu_to_le32(MWL8K_ENCR_ENABLE);
3522 memcpy(cmd->mac_addr, addr, ETH_ALEN);
3523 cmd->encr_type = encr_type;
3524
3525 rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
3526 kfree(cmd);
3527
3528 return rc;
3529}
3530
3531static int mwl8k_encryption_set_cmd_info(struct mwl8k_cmd_set_key *cmd,
3532 u8 *addr,
3533 struct ieee80211_key_conf *key)
3534{
3535 cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION);
3536 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3537 cmd->length = cpu_to_le16(sizeof(*cmd) -
3538 offsetof(struct mwl8k_cmd_set_key, length));
3539 cmd->key_id = cpu_to_le32(key->keyidx);
3540 cmd->key_len = cpu_to_le16(key->keylen);
3541 memcpy(cmd->mac_addr, addr, ETH_ALEN);
3542
3543 switch (key->cipher) {
3544 case WLAN_CIPHER_SUITE_WEP40:
3545 case WLAN_CIPHER_SUITE_WEP104:
3546 cmd->key_type_id = cpu_to_le16(MWL8K_ALG_WEP);
3547 if (key->keyidx == 0)
3548 cmd->key_info = cpu_to_le32(MWL8K_KEY_FLAG_WEP_TXKEY);
3549
3550 break;
3551 case WLAN_CIPHER_SUITE_TKIP:
3552 cmd->key_type_id = cpu_to_le16(MWL8K_ALG_TKIP);
3553 cmd->key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
3554 ? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE)
3555 : cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY);
3556 cmd->key_info |= cpu_to_le32(MWL8K_KEY_FLAG_MICKEY_VALID
3557 | MWL8K_KEY_FLAG_TSC_VALID);
3558 break;
3559 case WLAN_CIPHER_SUITE_CCMP:
3560 cmd->key_type_id = cpu_to_le16(MWL8K_ALG_CCMP);
3561 cmd->key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
3562 ? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE)
3563 : cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY);
3564 break;
3565 default:
3566 return -ENOTSUPP;
3567 }
3568
3569 return 0;
3570}
3571
3572static int mwl8k_cmd_encryption_set_key(struct ieee80211_hw *hw,
3573 struct ieee80211_vif *vif,
3574 u8 *addr,
3575 struct ieee80211_key_conf *key)
3576{
3577 struct mwl8k_cmd_set_key *cmd;
3578 int rc;
3579 int keymlen;
3580 u32 action;
3581 u8 idx;
3582 struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
3583
3584 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3585 if (cmd == NULL)
3586 return -ENOMEM;
3587
3588 rc = mwl8k_encryption_set_cmd_info(cmd, addr, key);
3589 if (rc < 0)
3590 goto done;
3591
3592 idx = key->keyidx;
3593
3594 if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
3595 action = MWL8K_ENCR_SET_KEY;
3596 else
3597 action = MWL8K_ENCR_SET_GROUP_KEY;
3598
3599 switch (key->cipher) {
3600 case WLAN_CIPHER_SUITE_WEP40:
3601 case WLAN_CIPHER_SUITE_WEP104:
3602 if (!mwl8k_vif->wep_key_conf[idx].enabled) {
3603 memcpy(mwl8k_vif->wep_key_conf[idx].key, key,
3604 sizeof(*key) + key->keylen);
3605 mwl8k_vif->wep_key_conf[idx].enabled = 1;
3606 }
3607
3608 keymlen = 0;
3609 action = MWL8K_ENCR_SET_KEY;
3610 break;
3611 case WLAN_CIPHER_SUITE_TKIP:
3612 keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH;
3613 break;
3614 case WLAN_CIPHER_SUITE_CCMP:
3615 keymlen = key->keylen;
3616 break;
3617 default:
3618 rc = -ENOTSUPP;
3619 goto done;
3620 }
3621
3622 memcpy(cmd->key_material, key->key, keymlen);
3623 cmd->action = cpu_to_le32(action);
3624
3625 rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
3626done:
3627 kfree(cmd);
3628
3629 return rc;
3630}
3631
3632static int mwl8k_cmd_encryption_remove_key(struct ieee80211_hw *hw,
3633 struct ieee80211_vif *vif,
3634 u8 *addr,
3635 struct ieee80211_key_conf *key)
3636{
3637 struct mwl8k_cmd_set_key *cmd;
3638 int rc;
3639 struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
3640
3641 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3642 if (cmd == NULL)
3643 return -ENOMEM;
3644
3645 rc = mwl8k_encryption_set_cmd_info(cmd, addr, key);
3646 if (rc < 0)
3647 goto done;
3648
3649 if (key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
3650 WLAN_CIPHER_SUITE_WEP104)
3651 mwl8k_vif->wep_key_conf[key->keyidx].enabled = 0;
3652
3653 cmd->action = cpu_to_le32(MWL8K_ENCR_REMOVE_KEY);
3654
3655 rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
3656done:
3657 kfree(cmd);
3658
3659 return rc;
3660}
3661
3662static int mwl8k_set_key(struct ieee80211_hw *hw,
3663 enum set_key_cmd cmd_param,
3664 struct ieee80211_vif *vif,
3665 struct ieee80211_sta *sta,
3666 struct ieee80211_key_conf *key)
3667{
3668 int rc = 0;
3669 u8 encr_type;
3670 u8 *addr;
3671 struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
3672
3673 if (vif->type == NL80211_IFTYPE_STATION)
3674 return -EOPNOTSUPP;
3675
3676 if (sta == NULL)
3677 addr = hw->wiphy->perm_addr;
3678 else
3679 addr = sta->addr;
3680
3681 if (cmd_param == SET_KEY) {
3682 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
3683 rc = mwl8k_cmd_encryption_set_key(hw, vif, addr, key);
3684 if (rc)
3685 goto out;
3686
3687 if ((key->cipher == WLAN_CIPHER_SUITE_WEP40)
3688 || (key->cipher == WLAN_CIPHER_SUITE_WEP104))
3689 encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_WEP;
3690 else
3691 encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED;
3692
3693 rc = mwl8k_cmd_update_encryption_enable(hw, vif, addr,
3694 encr_type);
3695 if (rc)
3696 goto out;
3697
3698 mwl8k_vif->is_hw_crypto_enabled = true;
3699
3700 } else {
3701 rc = mwl8k_cmd_encryption_remove_key(hw, vif, addr, key);
3702
3703 if (rc)
3704 goto out;
3705
3706 mwl8k_vif->is_hw_crypto_enabled = false;
3707
3708 }
3709out:
3710 return rc;
3711}
3712
3713/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003714 * CMD_UPDATE_STADB.
3715 */
Lennert Buytenhek25d81b12010-01-04 21:54:41 +01003716struct ewc_ht_info {
3717 __le16 control1;
3718 __le16 control2;
3719 __le16 control3;
Eric Dumazetba2d3582010-06-02 18:10:09 +00003720} __packed;
Lennert Buytenhek25d81b12010-01-04 21:54:41 +01003721
3722struct peer_capability_info {
3723 /* Peer type - AP vs. STA. */
3724 __u8 peer_type;
3725
3726 /* Basic 802.11 capabilities from assoc resp. */
3727 __le16 basic_caps;
3728
3729 /* Set if peer supports 802.11n high throughput (HT). */
3730 __u8 ht_support;
3731
3732 /* Valid if HT is supported. */
3733 __le16 ht_caps;
3734 __u8 extended_ht_caps;
3735 struct ewc_ht_info ewc_info;
3736
3737 /* Legacy rate table. Intersection of our rates and peer rates. */
3738 __u8 legacy_rates[12];
3739
3740 /* HT rate table. Intersection of our rates and peer rates. */
3741 __u8 ht_rates[16];
3742 __u8 pad[16];
3743
3744 /* If set, interoperability mode, no proprietary extensions. */
3745 __u8 interop;
3746 __u8 pad2;
3747 __u8 station_id;
3748 __le16 amsdu_enabled;
Eric Dumazetba2d3582010-06-02 18:10:09 +00003749} __packed;
Lennert Buytenhek25d81b12010-01-04 21:54:41 +01003750
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003751struct mwl8k_cmd_update_stadb {
3752 struct mwl8k_cmd_pkt header;
3753
3754 /* See STADB_ACTION_TYPE */
3755 __le32 action;
3756
3757 /* Peer MAC address */
3758 __u8 peer_addr[ETH_ALEN];
3759
3760 __le32 reserved;
3761
3762 /* Peer info - valid during add/update. */
3763 struct peer_capability_info peer_info;
Eric Dumazetba2d3582010-06-02 18:10:09 +00003764} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003765
Lennert Buytenheka6804002010-01-04 21:55:42 +01003766#define MWL8K_STA_DB_MODIFY_ENTRY 1
3767#define MWL8K_STA_DB_DEL_ENTRY 2
3768
3769/* Peer Entry flags - used to define the type of the peer node */
3770#define MWL8K_PEER_TYPE_ACCESSPOINT 2
3771
3772static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw,
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01003773 struct ieee80211_vif *vif,
Lennert Buytenhek13935e22010-01-04 21:57:59 +01003774 struct ieee80211_sta *sta)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003775{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003776 struct mwl8k_cmd_update_stadb *cmd;
Lennert Buytenheka6804002010-01-04 21:55:42 +01003777 struct peer_capability_info *p;
Lennert Buytenhek8707d022010-01-12 13:49:15 +01003778 u32 rates;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003779 int rc;
3780
3781 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3782 if (cmd == NULL)
3783 return -ENOMEM;
3784
3785 cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB);
3786 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenheka6804002010-01-04 21:55:42 +01003787 cmd->action = cpu_to_le32(MWL8K_STA_DB_MODIFY_ENTRY);
Lennert Buytenhek13935e22010-01-04 21:57:59 +01003788 memcpy(cmd->peer_addr, sta->addr, ETH_ALEN);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003789
Lennert Buytenheka6804002010-01-04 21:55:42 +01003790 p = &cmd->peer_info;
3791 p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT;
3792 p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability);
Lennert Buytenhek13935e22010-01-04 21:57:59 +01003793 p->ht_support = sta->ht_cap.ht_supported;
John W. Linvilleb6037422010-07-20 13:55:00 -04003794 p->ht_caps = cpu_to_le16(sta->ht_cap.cap);
Lennert Buytenhek13935e22010-01-04 21:57:59 +01003795 p->extended_ht_caps = (sta->ht_cap.ampdu_factor & 3) |
3796 ((sta->ht_cap.ampdu_density & 7) << 2);
Lennert Buytenhek8707d022010-01-12 13:49:15 +01003797 if (hw->conf.channel->band == IEEE80211_BAND_2GHZ)
3798 rates = sta->supp_rates[IEEE80211_BAND_2GHZ];
3799 else
3800 rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5;
3801 legacy_rate_mask_to_array(p->legacy_rates, rates);
Lennert Buytenhek13935e22010-01-04 21:57:59 +01003802 memcpy(p->ht_rates, sta->ht_cap.mcs.rx_mask, 16);
Lennert Buytenheka6804002010-01-04 21:55:42 +01003803 p->interop = 1;
3804 p->amsdu_enabled = 0;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003805
Lennert Buytenheka6804002010-01-04 21:55:42 +01003806 rc = mwl8k_post_cmd(hw, &cmd->header);
3807 kfree(cmd);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003808
Lennert Buytenheka6804002010-01-04 21:55:42 +01003809 return rc ? rc : p->station_id;
3810}
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003811
Lennert Buytenheka6804002010-01-04 21:55:42 +01003812static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw,
3813 struct ieee80211_vif *vif, u8 *addr)
3814{
3815 struct mwl8k_cmd_update_stadb *cmd;
3816 int rc;
3817
3818 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3819 if (cmd == NULL)
3820 return -ENOMEM;
3821
3822 cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB);
3823 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3824 cmd->action = cpu_to_le32(MWL8K_STA_DB_DEL_ENTRY);
3825 memcpy(cmd->peer_addr, addr, ETH_ALEN);
3826
3827 rc = mwl8k_post_cmd(hw, &cmd->header);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003828 kfree(cmd);
3829
3830 return rc;
3831}
3832
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003833
3834/*
3835 * Interrupt handling.
3836 */
3837static irqreturn_t mwl8k_interrupt(int irq, void *dev_id)
3838{
3839 struct ieee80211_hw *hw = dev_id;
3840 struct mwl8k_priv *priv = hw->priv;
3841 u32 status;
3842
3843 status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003844 if (!status)
3845 return IRQ_NONE;
3846
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +01003847 if (status & MWL8K_A2H_INT_TX_DONE) {
3848 status &= ~MWL8K_A2H_INT_TX_DONE;
3849 tasklet_schedule(&priv->poll_tx_task);
3850 }
3851
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01003852 if (status & MWL8K_A2H_INT_RX_READY) {
3853 status &= ~MWL8K_A2H_INT_RX_READY;
3854 tasklet_schedule(&priv->poll_rx_task);
3855 }
3856
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +01003857 if (status)
3858 iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003859
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003860 if (status & MWL8K_A2H_INT_OPC_DONE) {
Lennert Buytenhek618952a2009-08-18 03:18:01 +02003861 if (priv->hostcmd_wait != NULL)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003862 complete(priv->hostcmd_wait);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003863 }
3864
3865 if (status & MWL8K_A2H_INT_QUEUE_EMPTY) {
Lennert Buytenhek618952a2009-08-18 03:18:01 +02003866 if (!mutex_is_locked(&priv->fw_mutex) &&
Lennert Buytenhek88de754a2009-10-22 20:19:37 +02003867 priv->radio_on && priv->pending_tx_pkts)
Lennert Buytenhek618952a2009-08-18 03:18:01 +02003868 mwl8k_tx_start(priv);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003869 }
3870
3871 return IRQ_HANDLED;
3872}
3873
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +01003874static void mwl8k_tx_poll(unsigned long data)
3875{
3876 struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
3877 struct mwl8k_priv *priv = hw->priv;
3878 int limit;
3879 int i;
3880
3881 limit = 32;
3882
3883 spin_lock_bh(&priv->tx_lock);
3884
Brian Cavagnoloe6007072011-03-17 11:58:44 -07003885 for (i = 0; i < mwl8k_tx_queues(priv); i++)
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +01003886 limit -= mwl8k_txq_reclaim(hw, i, limit, 0);
3887
3888 if (!priv->pending_tx_pkts && priv->tx_wait != NULL) {
3889 complete(priv->tx_wait);
3890 priv->tx_wait = NULL;
3891 }
3892
3893 spin_unlock_bh(&priv->tx_lock);
3894
3895 if (limit) {
3896 writel(~MWL8K_A2H_INT_TX_DONE,
3897 priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
3898 } else {
3899 tasklet_schedule(&priv->poll_tx_task);
3900 }
3901}
3902
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01003903static void mwl8k_rx_poll(unsigned long data)
3904{
3905 struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
3906 struct mwl8k_priv *priv = hw->priv;
3907 int limit;
3908
3909 limit = 32;
3910 limit -= rxq_process(hw, 0, limit);
3911 limit -= rxq_refill(hw, 0, limit);
3912
3913 if (limit) {
3914 writel(~MWL8K_A2H_INT_RX_READY,
3915 priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
3916 } else {
3917 tasklet_schedule(&priv->poll_rx_task);
3918 }
3919}
3920
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003921
3922/*
3923 * Core driver operations.
3924 */
Johannes Berg7bb45682011-02-24 14:42:06 +01003925static void mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003926{
3927 struct mwl8k_priv *priv = hw->priv;
3928 int index = skb_get_queue_mapping(skb);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003929
Lennert Buytenhek9189c102010-01-12 13:47:32 +01003930 if (!priv->radio_on) {
Joe Perchesc96c31e2010-07-26 14:39:58 -07003931 wiphy_debug(hw->wiphy,
3932 "dropped TX frame since radio disabled\n");
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003933 dev_kfree_skb(skb);
Johannes Berg7bb45682011-02-24 14:42:06 +01003934 return;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003935 }
3936
Johannes Berg7bb45682011-02-24 14:42:06 +01003937 mwl8k_txq_xmit(hw, index, skb);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003938}
3939
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003940static int mwl8k_start(struct ieee80211_hw *hw)
3941{
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003942 struct mwl8k_priv *priv = hw->priv;
3943 int rc;
3944
Joe Perchesa0607fd2009-11-18 23:29:17 -08003945 rc = request_irq(priv->pdev->irq, mwl8k_interrupt,
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003946 IRQF_SHARED, MWL8K_NAME, hw);
3947 if (rc) {
Joe Perches5db55842010-08-11 19:11:19 -07003948 wiphy_err(hw->wiphy, "failed to register IRQ handler\n");
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003949 return -EIO;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003950 }
3951
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01003952 /* Enable TX reclaim and RX tasklets. */
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +01003953 tasklet_enable(&priv->poll_tx_task);
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01003954 tasklet_enable(&priv->poll_rx_task);
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003955
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003956 /* Enable interrupts */
Lennert Buytenhekc23b5a62009-07-16 13:49:55 +02003957 iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003958
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003959 rc = mwl8k_fw_lock(hw);
3960 if (!rc) {
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003961 rc = mwl8k_cmd_radio_enable(hw);
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003962
Lennert Buytenhek5e4cf162009-10-22 20:21:38 +02003963 if (!priv->ap_fw) {
3964 if (!rc)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003965 rc = mwl8k_cmd_enable_sniffer(hw, 0);
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003966
Lennert Buytenhek5e4cf162009-10-22 20:21:38 +02003967 if (!rc)
3968 rc = mwl8k_cmd_set_pre_scan(hw);
3969
3970 if (!rc)
3971 rc = mwl8k_cmd_set_post_scan(hw,
3972 "\x00\x00\x00\x00\x00\x00");
3973 }
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003974
3975 if (!rc)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003976 rc = mwl8k_cmd_set_rateadapt_mode(hw, 0);
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003977
3978 if (!rc)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003979 rc = mwl8k_cmd_set_wmm_mode(hw, 0);
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003980
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003981 mwl8k_fw_unlock(hw);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003982 }
3983
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003984 if (rc) {
3985 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
3986 free_irq(priv->pdev->irq, hw);
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +01003987 tasklet_disable(&priv->poll_tx_task);
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01003988 tasklet_disable(&priv->poll_rx_task);
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003989 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003990
3991 return rc;
3992}
3993
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003994static void mwl8k_stop(struct ieee80211_hw *hw)
3995{
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003996 struct mwl8k_priv *priv = hw->priv;
3997 int i;
3998
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003999 mwl8k_cmd_radio_disable(hw);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004000
4001 ieee80211_stop_queues(hw);
4002
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004003 /* Disable interrupts */
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004004 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004005 free_irq(priv->pdev->irq, hw);
4006
4007 /* Stop finalize join worker */
4008 cancel_work_sync(&priv->finalize_join_worker);
4009 if (priv->beacon_skb != NULL)
4010 dev_kfree_skb(priv->beacon_skb);
4011
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01004012 /* Stop TX reclaim and RX tasklets. */
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +01004013 tasklet_disable(&priv->poll_tx_task);
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01004014 tasklet_disable(&priv->poll_rx_task);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004015
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004016 /* Return all skbs to mac80211 */
Brian Cavagnoloe6007072011-03-17 11:58:44 -07004017 for (i = 0; i < mwl8k_tx_queues(priv); i++)
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01004018 mwl8k_txq_reclaim(hw, i, INT_MAX, 1);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004019}
4020
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08004021static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image);
4022
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004023static int mwl8k_add_interface(struct ieee80211_hw *hw,
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01004024 struct ieee80211_vif *vif)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004025{
4026 struct mwl8k_priv *priv = hw->priv;
4027 struct mwl8k_vif *mwl8k_vif;
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01004028 u32 macids_supported;
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08004029 int macid, rc;
4030 struct mwl8k_device_info *di;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004031
4032 /*
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02004033 * Reject interface creation if sniffer mode is active, as
4034 * STA operation is mutually exclusive with hardware sniffer
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01004035 * mode. (Sniffer mode is only used on STA firmware.)
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02004036 */
4037 if (priv->sniffer_enabled) {
Joe Perchesc96c31e2010-07-26 14:39:58 -07004038 wiphy_info(hw->wiphy,
4039 "unable to create STA interface because sniffer mode is enabled\n");
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02004040 return -EINVAL;
4041 }
4042
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08004043 di = priv->device_info;
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01004044 switch (vif->type) {
4045 case NL80211_IFTYPE_AP:
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08004046 if (!priv->ap_fw && di->fw_image_ap) {
4047 /* we must load the ap fw to meet this request */
4048 if (!list_empty(&priv->vif_list))
4049 return -EBUSY;
4050 rc = mwl8k_reload_firmware(hw, di->fw_image_ap);
4051 if (rc)
4052 return rc;
4053 }
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01004054 macids_supported = priv->ap_macids_supported;
4055 break;
4056 case NL80211_IFTYPE_STATION:
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08004057 if (priv->ap_fw && di->fw_image_sta) {
4058 /* we must load the sta fw to meet this request */
4059 if (!list_empty(&priv->vif_list))
4060 return -EBUSY;
4061 rc = mwl8k_reload_firmware(hw, di->fw_image_sta);
4062 if (rc)
4063 return rc;
4064 }
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01004065 macids_supported = priv->sta_macids_supported;
4066 break;
4067 default:
4068 return -EINVAL;
4069 }
4070
4071 macid = ffs(macids_supported & ~priv->macids_used);
4072 if (!macid--)
4073 return -EBUSY;
4074
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01004075 /* Setup driver private area. */
Johannes Berg1ed32e42009-12-23 13:15:45 +01004076 mwl8k_vif = MWL8K_VIF(vif);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004077 memset(mwl8k_vif, 0, sizeof(*mwl8k_vif));
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01004078 mwl8k_vif->vif = vif;
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01004079 mwl8k_vif->macid = macid;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004080 mwl8k_vif->seqno = 0;
Nishant Sarmukadamd9a07d42010-12-30 11:23:33 -08004081 memcpy(mwl8k_vif->bssid, vif->addr, ETH_ALEN);
4082 mwl8k_vif->is_hw_crypto_enabled = false;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004083
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01004084 /* Set the mac address. */
4085 mwl8k_cmd_set_mac_addr(hw, vif, vif->addr);
4086
4087 if (priv->ap_fw)
4088 mwl8k_cmd_set_new_stn_add_self(hw, vif);
4089
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01004090 priv->macids_used |= 1 << mwl8k_vif->macid;
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01004091 list_add_tail(&mwl8k_vif->list, &priv->vif_list);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004092
4093 return 0;
4094}
4095
4096static void mwl8k_remove_interface(struct ieee80211_hw *hw,
Johannes Berg1ed32e42009-12-23 13:15:45 +01004097 struct ieee80211_vif *vif)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004098{
4099 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01004100 struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004101
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01004102 if (priv->ap_fw)
4103 mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr);
4104
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01004105 mwl8k_cmd_set_mac_addr(hw, vif, "\x00\x00\x00\x00\x00\x00");
Lennert Buytenhek32060e12009-10-22 20:20:04 +02004106
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01004107 priv->macids_used &= ~(1 << mwl8k_vif->macid);
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01004108 list_del(&mwl8k_vif->list);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004109}
4110
Lennert Buytenhekee03a932009-07-17 07:19:37 +02004111static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004112{
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004113 struct ieee80211_conf *conf = &hw->conf;
4114 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenhekee03a932009-07-17 07:19:37 +02004115 int rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004116
Lennert Buytenhek7595d672009-08-17 23:59:40 +02004117 if (conf->flags & IEEE80211_CONF_IDLE) {
Lennert Buytenhek55489b62009-11-30 18:31:33 +01004118 mwl8k_cmd_radio_disable(hw);
Lennert Buytenhekee03a932009-07-17 07:19:37 +02004119 return 0;
Lennert Buytenhek7595d672009-08-17 23:59:40 +02004120 }
4121
Lennert Buytenhekee03a932009-07-17 07:19:37 +02004122 rc = mwl8k_fw_lock(hw);
4123 if (rc)
4124 return rc;
4125
Lennert Buytenhek55489b62009-11-30 18:31:33 +01004126 rc = mwl8k_cmd_radio_enable(hw);
Lennert Buytenhekee03a932009-07-17 07:19:37 +02004127 if (rc)
4128 goto out;
4129
Lennert Buytenhek610677d2010-01-04 21:56:46 +01004130 rc = mwl8k_cmd_set_rf_channel(hw, conf);
Lennert Buytenhekee03a932009-07-17 07:19:37 +02004131 if (rc)
4132 goto out;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004133
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004134 if (conf->power_level > 18)
4135 conf->power_level = 18;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004136
Lennert Buytenhek08b06342009-10-22 20:21:33 +02004137 if (priv->ap_fw) {
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -08004138 rc = mwl8k_cmd_tx_power(hw, conf, conf->power_level);
4139 if (rc)
4140 goto out;
4141
Nishant Sarmukadamda62b762011-02-17 14:45:16 -08004142 rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3);
4143 if (rc)
4144 wiphy_warn(hw->wiphy, "failed to set # of RX antennas");
4145 rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7);
4146 if (rc)
4147 wiphy_warn(hw->wiphy, "failed to set # of TX antennas");
4148
Lennert Buytenhek08b06342009-10-22 20:21:33 +02004149 } else {
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -08004150 rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level);
4151 if (rc)
4152 goto out;
Lennert Buytenhek08b06342009-10-22 20:21:33 +02004153 rc = mwl8k_cmd_mimo_config(hw, 0x7, 0x7);
4154 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004155
Lennert Buytenhekee03a932009-07-17 07:19:37 +02004156out:
4157 mwl8k_fw_unlock(hw);
4158
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004159 return rc;
4160}
4161
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01004162static void
4163mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
4164 struct ieee80211_bss_conf *info, u32 changed)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004165{
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004166 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004167 u32 ap_legacy_rates;
Lennert Buytenhek13935e22010-01-04 21:57:59 +01004168 u8 ap_mcs_rates[16];
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02004169 int rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004170
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004171 if (mwl8k_fw_lock(hw))
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02004172 return;
4173
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004174 /*
4175 * No need to capture a beacon if we're no longer associated.
4176 */
4177 if ((changed & BSS_CHANGED_ASSOC) && !vif->bss_conf.assoc)
4178 priv->capture_beacon = false;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004179
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004180 /*
Lennert Buytenhek13935e22010-01-04 21:57:59 +01004181 * Get the AP's legacy and MCS rates.
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004182 */
Lennert Buytenhek7dc6a7a2009-11-30 18:33:09 +01004183 if (vif->bss_conf.assoc) {
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01004184 struct ieee80211_sta *ap;
Lennert Buytenhekc97470d2010-01-12 13:47:37 +01004185
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01004186 rcu_read_lock();
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004187
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01004188 ap = ieee80211_find_sta(vif, vif->bss_conf.bssid);
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004189 if (ap == NULL) {
4190 rcu_read_unlock();
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01004191 goto out;
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004192 }
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01004193
Lennert Buytenhek8707d022010-01-12 13:49:15 +01004194 if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) {
4195 ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ];
4196 } else {
4197 ap_legacy_rates =
4198 ap->supp_rates[IEEE80211_BAND_5GHZ] << 5;
4199 }
Lennert Buytenhek13935e22010-01-04 21:57:59 +01004200 memcpy(ap_mcs_rates, ap->ht_cap.mcs.rx_mask, 16);
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004201
4202 rcu_read_unlock();
4203 }
4204
4205 if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) {
Lennert Buytenhek13935e22010-01-04 21:57:59 +01004206 rc = mwl8k_cmd_set_rate(hw, vif, ap_legacy_rates, ap_mcs_rates);
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02004207 if (rc)
4208 goto out;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004209
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01004210 rc = mwl8k_cmd_use_fixed_rate_sta(hw);
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02004211 if (rc)
4212 goto out;
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004213 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004214
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004215 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
Lennert Buytenhek7dc6a7a2009-11-30 18:33:09 +01004216 rc = mwl8k_set_radio_preamble(hw,
4217 vif->bss_conf.use_short_preamble);
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02004218 if (rc)
4219 goto out;
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004220 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004221
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004222 if (changed & BSS_CHANGED_ERP_SLOT) {
Lennert Buytenhek7dc6a7a2009-11-30 18:33:09 +01004223 rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot);
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02004224 if (rc)
4225 goto out;
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004226 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004227
Lennert Buytenhekc97470d2010-01-12 13:47:37 +01004228 if (vif->bss_conf.assoc &&
4229 (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT |
4230 BSS_CHANGED_HT))) {
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004231 rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates);
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02004232 if (rc)
4233 goto out;
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004234 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004235
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01004236 if (vif->bss_conf.assoc &&
4237 (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INT))) {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004238 /*
4239 * Finalize the join. Tell rx handler to process
4240 * next beacon from our BSSID.
4241 */
Lennert Buytenhek0a11dfc2010-01-04 21:55:21 +01004242 memcpy(priv->capture_bssid, vif->bss_conf.bssid, ETH_ALEN);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004243 priv->capture_beacon = true;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004244 }
4245
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02004246out:
4247 mwl8k_fw_unlock(hw);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004248}
4249
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01004250static void
4251mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
4252 struct ieee80211_bss_conf *info, u32 changed)
4253{
4254 int rc;
4255
4256 if (mwl8k_fw_lock(hw))
4257 return;
4258
4259 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
4260 rc = mwl8k_set_radio_preamble(hw,
4261 vif->bss_conf.use_short_preamble);
4262 if (rc)
4263 goto out;
4264 }
4265
4266 if (changed & BSS_CHANGED_BASIC_RATES) {
4267 int idx;
4268 int rate;
4269
4270 /*
4271 * Use lowest supported basic rate for multicasts
4272 * and management frames (such as probe responses --
4273 * beacons will always go out at 1 Mb/s).
4274 */
4275 idx = ffs(vif->bss_conf.basic_rates);
Lennert Buytenhek8707d022010-01-12 13:49:15 +01004276 if (idx)
4277 idx--;
4278
4279 if (hw->conf.channel->band == IEEE80211_BAND_2GHZ)
4280 rate = mwl8k_rates_24[idx].hw_value;
4281 else
4282 rate = mwl8k_rates_50[idx].hw_value;
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01004283
4284 mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate);
4285 }
4286
4287 if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) {
4288 struct sk_buff *skb;
4289
4290 skb = ieee80211_beacon_get(hw, vif);
4291 if (skb != NULL) {
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01004292 mwl8k_cmd_set_beacon(hw, vif, skb->data, skb->len);
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01004293 kfree_skb(skb);
4294 }
4295 }
4296
4297 if (changed & BSS_CHANGED_BEACON_ENABLED)
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01004298 mwl8k_cmd_bss_start(hw, vif, info->enable_beacon);
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01004299
4300out:
4301 mwl8k_fw_unlock(hw);
4302}
4303
4304static void
4305mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
4306 struct ieee80211_bss_conf *info, u32 changed)
4307{
4308 struct mwl8k_priv *priv = hw->priv;
4309
4310 if (!priv->ap_fw)
4311 mwl8k_bss_info_changed_sta(hw, vif, info, changed);
4312 else
4313 mwl8k_bss_info_changed_ap(hw, vif, info, changed);
4314}
4315
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02004316static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw,
Jiri Pirko22bedad2010-04-01 21:22:57 +00004317 struct netdev_hw_addr_list *mc_list)
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02004318{
4319 struct mwl8k_cmd_pkt *cmd;
4320
Lennert Buytenhek447ced02009-10-22 20:19:50 +02004321 /*
4322 * Synthesize and return a command packet that programs the
4323 * hardware multicast address filter. At this point we don't
4324 * know whether FIF_ALLMULTI is being requested, but if it is,
4325 * we'll end up throwing this packet away and creating a new
4326 * one in mwl8k_configure_filter().
4327 */
Jiri Pirko22bedad2010-04-01 21:22:57 +00004328 cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_list);
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02004329
4330 return (unsigned long)cmd;
4331}
4332
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02004333static int
4334mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw,
4335 unsigned int changed_flags,
4336 unsigned int *total_flags)
4337{
4338 struct mwl8k_priv *priv = hw->priv;
4339
4340 /*
4341 * Hardware sniffer mode is mutually exclusive with STA
4342 * operation, so refuse to enable sniffer mode if a STA
4343 * interface is active.
4344 */
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01004345 if (!list_empty(&priv->vif_list)) {
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02004346 if (net_ratelimit())
Joe Perchesc96c31e2010-07-26 14:39:58 -07004347 wiphy_info(hw->wiphy,
4348 "not enabling sniffer mode because STA interface is active\n");
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02004349 return 0;
4350 }
4351
4352 if (!priv->sniffer_enabled) {
Lennert Buytenhek55489b62009-11-30 18:31:33 +01004353 if (mwl8k_cmd_enable_sniffer(hw, 1))
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02004354 return 0;
4355 priv->sniffer_enabled = true;
4356 }
4357
4358 *total_flags &= FIF_PROMISC_IN_BSS | FIF_ALLMULTI |
4359 FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL |
4360 FIF_OTHER_BSS;
4361
4362 return 1;
4363}
4364
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01004365static struct mwl8k_vif *mwl8k_first_vif(struct mwl8k_priv *priv)
4366{
4367 if (!list_empty(&priv->vif_list))
4368 return list_entry(priv->vif_list.next, struct mwl8k_vif, list);
4369
4370 return NULL;
4371}
4372
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02004373static void mwl8k_configure_filter(struct ieee80211_hw *hw,
4374 unsigned int changed_flags,
4375 unsigned int *total_flags,
4376 u64 multicast)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004377{
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004378 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02004379 struct mwl8k_cmd_pkt *cmd = (void *)(unsigned long)multicast;
4380
4381 /*
Lennert Buytenhekc0adae22009-10-22 20:21:36 +02004382 * AP firmware doesn't allow fine-grained control over
4383 * the receive filter.
4384 */
4385 if (priv->ap_fw) {
4386 *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC;
4387 kfree(cmd);
4388 return;
4389 }
4390
4391 /*
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02004392 * Enable hardware sniffer mode if FIF_CONTROL or
4393 * FIF_OTHER_BSS is requested.
4394 */
4395 if (*total_flags & (FIF_CONTROL | FIF_OTHER_BSS) &&
4396 mwl8k_configure_filter_sniffer(hw, changed_flags, total_flags)) {
4397 kfree(cmd);
4398 return;
4399 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004400
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02004401 /* Clear unsupported feature flags */
Lennert Buytenhek447ced02009-10-22 20:19:50 +02004402 *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC;
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02004403
Lennert Buytenhek90852f72010-01-02 10:31:42 +01004404 if (mwl8k_fw_lock(hw)) {
4405 kfree(cmd);
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02004406 return;
Lennert Buytenhek90852f72010-01-02 10:31:42 +01004407 }
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02004408
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02004409 if (priv->sniffer_enabled) {
Lennert Buytenhek55489b62009-11-30 18:31:33 +01004410 mwl8k_cmd_enable_sniffer(hw, 0);
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02004411 priv->sniffer_enabled = false;
4412 }
4413
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02004414 if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
Lennert Buytenhek77165d82009-10-22 20:19:53 +02004415 if (*total_flags & FIF_BCN_PRBRESP_PROMISC) {
4416 /*
4417 * Disable the BSS filter.
4418 */
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02004419 mwl8k_cmd_set_pre_scan(hw);
Lennert Buytenhek77165d82009-10-22 20:19:53 +02004420 } else {
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01004421 struct mwl8k_vif *mwl8k_vif;
Lennert Buytenhek0a11dfc2010-01-04 21:55:21 +01004422 const u8 *bssid;
Lennert Buytenheka94cc972009-08-03 21:58:57 +02004423
Lennert Buytenhek77165d82009-10-22 20:19:53 +02004424 /*
4425 * Enable the BSS filter.
4426 *
4427 * If there is an active STA interface, use that
4428 * interface's BSSID, otherwise use a dummy one
4429 * (where the OUI part needs to be nonzero for
4430 * the BSSID to be accepted by POST_SCAN).
4431 */
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01004432 mwl8k_vif = mwl8k_first_vif(priv);
4433 if (mwl8k_vif != NULL)
4434 bssid = mwl8k_vif->vif->bss_conf.bssid;
4435 else
4436 bssid = "\x01\x00\x00\x00\x00\x00";
Lennert Buytenheka94cc972009-08-03 21:58:57 +02004437
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02004438 mwl8k_cmd_set_post_scan(hw, bssid);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004439 }
4440 }
4441
Lennert Buytenhek447ced02009-10-22 20:19:50 +02004442 /*
4443 * If FIF_ALLMULTI is being requested, throw away the command
4444 * packet that ->prepare_multicast() built and replace it with
4445 * a command packet that enables reception of all multicast
4446 * packets.
4447 */
4448 if (*total_flags & FIF_ALLMULTI) {
4449 kfree(cmd);
Jiri Pirko22bedad2010-04-01 21:22:57 +00004450 cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, NULL);
Lennert Buytenhek447ced02009-10-22 20:19:50 +02004451 }
4452
4453 if (cmd != NULL) {
4454 mwl8k_post_cmd(hw, cmd);
4455 kfree(cmd);
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02004456 }
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02004457
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02004458 mwl8k_fw_unlock(hw);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004459}
4460
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004461static int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
4462{
Lennert Buytenhekc2c2b122010-01-08 18:27:59 +01004463 return mwl8k_cmd_set_rts_threshold(hw, value);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004464}
4465
Johannes Berg4a6967b2010-02-19 19:18:37 +01004466static int mwl8k_sta_remove(struct ieee80211_hw *hw,
4467 struct ieee80211_vif *vif,
4468 struct ieee80211_sta *sta)
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01004469{
4470 struct mwl8k_priv *priv = hw->priv;
4471
Johannes Berg4a6967b2010-02-19 19:18:37 +01004472 if (priv->ap_fw)
4473 return mwl8k_cmd_set_new_stn_del(hw, vif, sta->addr);
4474 else
4475 return mwl8k_cmd_update_stadb_del(hw, vif, sta->addr);
4476}
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01004477
Johannes Berg4a6967b2010-02-19 19:18:37 +01004478static int mwl8k_sta_add(struct ieee80211_hw *hw,
4479 struct ieee80211_vif *vif,
4480 struct ieee80211_sta *sta)
4481{
4482 struct mwl8k_priv *priv = hw->priv;
4483 int ret;
Nishant Sarmukadamfcdc4032010-12-30 11:23:34 -08004484 int i;
4485 struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
4486 struct ieee80211_key_conf *key;
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01004487
Johannes Berg4a6967b2010-02-19 19:18:37 +01004488 if (!priv->ap_fw) {
4489 ret = mwl8k_cmd_update_stadb_add(hw, vif, sta);
4490 if (ret >= 0) {
4491 MWL8K_STA(sta)->peer_id = ret;
Nishant Sarmukadamfcdc4032010-12-30 11:23:34 -08004492 ret = 0;
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01004493 }
Johannes Berg4a6967b2010-02-19 19:18:37 +01004494
Nishant Sarmukadamd9a07d42010-12-30 11:23:33 -08004495 } else {
4496 ret = mwl8k_cmd_set_new_stn_add(hw, vif, sta);
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01004497 }
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01004498
Nishant Sarmukadamd9a07d42010-12-30 11:23:33 -08004499 for (i = 0; i < NUM_WEP_KEYS; i++) {
4500 key = IEEE80211_KEY_CONF(mwl8k_vif->wep_key_conf[i].key);
4501 if (mwl8k_vif->wep_key_conf[i].enabled)
4502 mwl8k_set_key(hw, SET_KEY, vif, sta, key);
4503 }
Nishant Sarmukadamfcdc4032010-12-30 11:23:34 -08004504 return ret;
Lennert Buytenhekbbfd9122010-01-04 21:55:12 +01004505}
4506
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004507static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue,
4508 const struct ieee80211_tx_queue_params *params)
4509{
Lennert Buytenhek3e4f5422009-07-17 07:25:59 +02004510 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004511 int rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004512
Lennert Buytenhek3e4f5422009-07-17 07:25:59 +02004513 rc = mwl8k_fw_lock(hw);
4514 if (!rc) {
Brian Cavagnoloe6007072011-03-17 11:58:44 -07004515 BUG_ON(queue > MWL8K_TX_WMM_QUEUES - 1);
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08004516 memcpy(&priv->wmm_params[queue], params, sizeof(*params));
4517
Lennert Buytenhek3e4f5422009-07-17 07:25:59 +02004518 if (!priv->wmm_enabled)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01004519 rc = mwl8k_cmd_set_wmm_mode(hw, 1);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004520
Nishant Sarmukadam85c92052011-02-17 14:45:18 -08004521 if (!rc) {
Brian Cavagnoloe6007072011-03-17 11:58:44 -07004522 int q = MWL8K_TX_WMM_QUEUES - 1 - queue;
Nishant Sarmukadam85c92052011-02-17 14:45:18 -08004523 rc = mwl8k_cmd_set_edca_params(hw, q,
Lennert Buytenhek55489b62009-11-30 18:31:33 +01004524 params->cw_min,
4525 params->cw_max,
4526 params->aifs,
4527 params->txop);
Nishant Sarmukadam85c92052011-02-17 14:45:18 -08004528 }
Lennert Buytenhek3e4f5422009-07-17 07:25:59 +02004529
4530 mwl8k_fw_unlock(hw);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004531 }
Lennert Buytenhek3e4f5422009-07-17 07:25:59 +02004532
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004533 return rc;
4534}
4535
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004536static int mwl8k_get_stats(struct ieee80211_hw *hw,
4537 struct ieee80211_low_level_stats *stats)
4538{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01004539 return mwl8k_cmd_get_stat(hw, stats);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004540}
4541
John W. Linville0d462bb2010-07-28 14:04:24 -04004542static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx,
4543 struct survey_info *survey)
4544{
4545 struct mwl8k_priv *priv = hw->priv;
4546 struct ieee80211_conf *conf = &hw->conf;
4547
4548 if (idx != 0)
4549 return -ENOENT;
4550
4551 survey->channel = conf->channel;
4552 survey->filled = SURVEY_INFO_NOISE_DBM;
4553 survey->noise = priv->noise;
4554
4555 return 0;
4556}
4557
Lennert Buytenheka2292d82010-01-04 21:58:12 +01004558static int
4559mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
4560 enum ieee80211_ampdu_mlme_action action,
Johannes Berg0b01f032011-01-18 13:51:05 +01004561 struct ieee80211_sta *sta, u16 tid, u16 *ssn,
4562 u8 buf_size)
Lennert Buytenheka2292d82010-01-04 21:58:12 +01004563{
4564 switch (action) {
4565 case IEEE80211_AMPDU_RX_START:
4566 case IEEE80211_AMPDU_RX_STOP:
4567 if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION))
4568 return -ENOTSUPP;
4569 return 0;
4570 default:
4571 return -ENOTSUPP;
4572 }
4573}
4574
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004575static const struct ieee80211_ops mwl8k_ops = {
4576 .tx = mwl8k_tx,
4577 .start = mwl8k_start,
4578 .stop = mwl8k_stop,
4579 .add_interface = mwl8k_add_interface,
4580 .remove_interface = mwl8k_remove_interface,
4581 .config = mwl8k_config,
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004582 .bss_info_changed = mwl8k_bss_info_changed,
Johannes Berg3ac64be2009-08-17 16:16:53 +02004583 .prepare_multicast = mwl8k_prepare_multicast,
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004584 .configure_filter = mwl8k_configure_filter,
Nishant Sarmukadamfcdc4032010-12-30 11:23:34 -08004585 .set_key = mwl8k_set_key,
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004586 .set_rts_threshold = mwl8k_set_rts_threshold,
Johannes Berg4a6967b2010-02-19 19:18:37 +01004587 .sta_add = mwl8k_sta_add,
4588 .sta_remove = mwl8k_sta_remove,
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004589 .conf_tx = mwl8k_conf_tx,
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004590 .get_stats = mwl8k_get_stats,
John W. Linville0d462bb2010-07-28 14:04:24 -04004591 .get_survey = mwl8k_get_survey,
Lennert Buytenheka2292d82010-01-04 21:58:12 +01004592 .ampdu_action = mwl8k_ampdu_action,
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004593};
4594
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004595static void mwl8k_finalize_join_worker(struct work_struct *work)
4596{
4597 struct mwl8k_priv *priv =
4598 container_of(work, struct mwl8k_priv, finalize_join_worker);
4599 struct sk_buff *skb = priv->beacon_skb;
Johannes Berg56007a02010-01-26 14:19:52 +01004600 struct ieee80211_mgmt *mgmt = (void *)skb->data;
4601 int len = skb->len - offsetof(struct ieee80211_mgmt, u.beacon.variable);
4602 const u8 *tim = cfg80211_find_ie(WLAN_EID_TIM,
4603 mgmt->u.beacon.variable, len);
4604 int dtim_period = 1;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004605
Johannes Berg56007a02010-01-26 14:19:52 +01004606 if (tim && tim[1] >= 2)
4607 dtim_period = tim[3];
4608
4609 mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len, dtim_period);
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01004610
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004611 dev_kfree_skb(skb);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004612 priv->beacon_skb = NULL;
4613}
4614
John W. Linvillebcb628d2009-11-06 16:40:16 -05004615enum {
Lennert Buytenhek9e1b17e2010-01-04 21:56:19 +01004616 MWL8363 = 0,
4617 MWL8687,
John W. Linvillebcb628d2009-11-06 16:40:16 -05004618 MWL8366,
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +02004619};
4620
Brian Cavagnolo8a7a5782011-03-17 11:58:42 -07004621#define MWL8K_8366_AP_FW_API 2
Brian Cavagnolo952a0e92010-11-12 17:23:51 -08004622#define _MWL8K_8366_AP_FW(api) "mwl8k/fmimage_8366_ap-" #api ".fw"
4623#define MWL8K_8366_AP_FW(api) _MWL8K_8366_AP_FW(api)
4624
John W. Linvillebcb628d2009-11-06 16:40:16 -05004625static struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = {
Lennert Buytenhek9e1b17e2010-01-04 21:56:19 +01004626 [MWL8363] = {
4627 .part_name = "88w8363",
4628 .helper_image = "mwl8k/helper_8363.fw",
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08004629 .fw_image_sta = "mwl8k/fmimage_8363.fw",
Lennert Buytenhek9e1b17e2010-01-04 21:56:19 +01004630 },
Lennert Buytenhek49eb6912009-11-30 18:32:13 +01004631 [MWL8687] = {
John W. Linvillebcb628d2009-11-06 16:40:16 -05004632 .part_name = "88w8687",
4633 .helper_image = "mwl8k/helper_8687.fw",
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08004634 .fw_image_sta = "mwl8k/fmimage_8687.fw",
John W. Linvillebcb628d2009-11-06 16:40:16 -05004635 },
Lennert Buytenhek49eb6912009-11-30 18:32:13 +01004636 [MWL8366] = {
John W. Linvillebcb628d2009-11-06 16:40:16 -05004637 .part_name = "88w8366",
4638 .helper_image = "mwl8k/helper_8366.fw",
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08004639 .fw_image_sta = "mwl8k/fmimage_8366.fw",
Brian Cavagnolo952a0e92010-11-12 17:23:51 -08004640 .fw_image_ap = MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API),
4641 .fw_api_ap = MWL8K_8366_AP_FW_API,
Lennert Buytenhek89a91f42009-11-30 18:32:54 +01004642 .ap_rxd_ops = &rxd_8366_ap_ops,
John W. Linvillebcb628d2009-11-06 16:40:16 -05004643 },
Lennert Buytenhek45a390d2009-10-22 20:20:47 +02004644};
4645
Lennert Buytenhekc92d4ed2010-01-12 13:47:22 +01004646MODULE_FIRMWARE("mwl8k/helper_8363.fw");
4647MODULE_FIRMWARE("mwl8k/fmimage_8363.fw");
4648MODULE_FIRMWARE("mwl8k/helper_8687.fw");
4649MODULE_FIRMWARE("mwl8k/fmimage_8687.fw");
4650MODULE_FIRMWARE("mwl8k/helper_8366.fw");
4651MODULE_FIRMWARE("mwl8k/fmimage_8366.fw");
Brian Cavagnolo952a0e92010-11-12 17:23:51 -08004652MODULE_FIRMWARE(MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API));
Lennert Buytenhekc92d4ed2010-01-12 13:47:22 +01004653
Lennert Buytenhek45a390d2009-10-22 20:20:47 +02004654static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = {
Benjamin Larssone5868ba2010-03-19 01:46:10 +01004655 { PCI_VDEVICE(MARVELL, 0x2a0a), .driver_data = MWL8363, },
Lennert Buytenhek9e1b17e2010-01-04 21:56:19 +01004656 { PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, },
4657 { PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, },
John W. Linvillebcb628d2009-11-06 16:40:16 -05004658 { PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, },
4659 { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, },
4660 { PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, },
Lennert Buytenhekca665272010-01-12 13:47:53 +01004661 { PCI_VDEVICE(MARVELL, 0x2a43), .driver_data = MWL8366, },
John W. Linvillebcb628d2009-11-06 16:40:16 -05004662 { },
Lennert Buytenhek45a390d2009-10-22 20:20:47 +02004663};
4664MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table);
4665
Brian Cavagnolo99020472010-11-12 17:23:52 -08004666static int mwl8k_request_alt_fw(struct mwl8k_priv *priv)
4667{
4668 int rc;
4669 printk(KERN_ERR "%s: Error requesting preferred fw %s.\n"
4670 "Trying alternative firmware %s\n", pci_name(priv->pdev),
4671 priv->fw_pref, priv->fw_alt);
4672 rc = mwl8k_request_fw(priv, priv->fw_alt, &priv->fw_ucode, true);
4673 if (rc) {
4674 printk(KERN_ERR "%s: Error requesting alt fw %s\n",
4675 pci_name(priv->pdev), priv->fw_alt);
4676 return rc;
4677 }
4678 return 0;
4679}
4680
4681static int mwl8k_firmware_load_success(struct mwl8k_priv *priv);
4682static void mwl8k_fw_state_machine(const struct firmware *fw, void *context)
4683{
4684 struct mwl8k_priv *priv = context;
4685 struct mwl8k_device_info *di = priv->device_info;
4686 int rc;
4687
4688 switch (priv->fw_state) {
4689 case FW_STATE_INIT:
4690 if (!fw) {
4691 printk(KERN_ERR "%s: Error requesting helper fw %s\n",
4692 pci_name(priv->pdev), di->helper_image);
4693 goto fail;
4694 }
4695 priv->fw_helper = fw;
4696 rc = mwl8k_request_fw(priv, priv->fw_pref, &priv->fw_ucode,
4697 true);
4698 if (rc && priv->fw_alt) {
4699 rc = mwl8k_request_alt_fw(priv);
4700 if (rc)
4701 goto fail;
4702 priv->fw_state = FW_STATE_LOADING_ALT;
4703 } else if (rc)
4704 goto fail;
4705 else
4706 priv->fw_state = FW_STATE_LOADING_PREF;
4707 break;
4708
4709 case FW_STATE_LOADING_PREF:
4710 if (!fw) {
4711 if (priv->fw_alt) {
4712 rc = mwl8k_request_alt_fw(priv);
4713 if (rc)
4714 goto fail;
4715 priv->fw_state = FW_STATE_LOADING_ALT;
4716 } else
4717 goto fail;
4718 } else {
4719 priv->fw_ucode = fw;
4720 rc = mwl8k_firmware_load_success(priv);
4721 if (rc)
4722 goto fail;
4723 else
4724 complete(&priv->firmware_loading_complete);
4725 }
4726 break;
4727
4728 case FW_STATE_LOADING_ALT:
4729 if (!fw) {
4730 printk(KERN_ERR "%s: Error requesting alt fw %s\n",
4731 pci_name(priv->pdev), di->helper_image);
4732 goto fail;
4733 }
4734 priv->fw_ucode = fw;
4735 rc = mwl8k_firmware_load_success(priv);
4736 if (rc)
4737 goto fail;
4738 else
4739 complete(&priv->firmware_loading_complete);
4740 break;
4741
4742 default:
4743 printk(KERN_ERR "%s: Unexpected firmware loading state: %d\n",
4744 MWL8K_NAME, priv->fw_state);
4745 BUG_ON(1);
4746 }
4747
4748 return;
4749
4750fail:
4751 priv->fw_state = FW_STATE_ERROR;
4752 complete(&priv->firmware_loading_complete);
4753 device_release_driver(&priv->pdev->dev);
4754 mwl8k_release_firmware(priv);
4755}
4756
4757static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image,
4758 bool nowait)
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004759{
4760 struct mwl8k_priv *priv = hw->priv;
4761 int rc;
4762
4763 /* Reset firmware and hardware */
4764 mwl8k_hw_reset(priv);
4765
4766 /* Ask userland hotplug daemon for the device firmware */
Brian Cavagnolo99020472010-11-12 17:23:52 -08004767 rc = mwl8k_request_firmware(priv, fw_image, nowait);
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004768 if (rc) {
4769 wiphy_err(hw->wiphy, "Firmware files not found\n");
4770 return rc;
4771 }
4772
Brian Cavagnolo99020472010-11-12 17:23:52 -08004773 if (nowait)
4774 return rc;
4775
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004776 /* Load firmware into hardware */
4777 rc = mwl8k_load_firmware(hw);
4778 if (rc)
4779 wiphy_err(hw->wiphy, "Cannot start firmware\n");
4780
4781 /* Reclaim memory once firmware is successfully loaded */
4782 mwl8k_release_firmware(priv);
4783
4784 return rc;
4785}
4786
Brian Cavagnolo73b46322011-03-17 11:58:41 -07004787static int mwl8k_init_txqs(struct ieee80211_hw *hw)
4788{
4789 struct mwl8k_priv *priv = hw->priv;
4790 int rc = 0;
4791 int i;
4792
Brian Cavagnoloe6007072011-03-17 11:58:44 -07004793 for (i = 0; i < mwl8k_tx_queues(priv); i++) {
Brian Cavagnolo73b46322011-03-17 11:58:41 -07004794 rc = mwl8k_txq_init(hw, i);
4795 if (rc)
4796 break;
4797 if (priv->ap_fw)
4798 iowrite32(priv->txq[i].txd_dma,
4799 priv->sram + priv->txq_offset[i]);
4800 }
4801 return rc;
4802}
4803
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004804/* initialize hw after successfully loading a firmware image */
4805static int mwl8k_probe_hw(struct ieee80211_hw *hw)
4806{
4807 struct mwl8k_priv *priv = hw->priv;
4808 int rc = 0;
4809 int i;
4810
4811 if (priv->ap_fw) {
4812 priv->rxd_ops = priv->device_info->ap_rxd_ops;
4813 if (priv->rxd_ops == NULL) {
4814 wiphy_err(hw->wiphy,
4815 "Driver does not have AP firmware image support for this hardware\n");
4816 goto err_stop_firmware;
4817 }
4818 } else {
4819 priv->rxd_ops = &rxd_sta_ops;
4820 }
4821
4822 priv->sniffer_enabled = false;
4823 priv->wmm_enabled = false;
4824 priv->pending_tx_pkts = 0;
4825
4826 rc = mwl8k_rxq_init(hw, 0);
4827 if (rc)
4828 goto err_stop_firmware;
4829 rxq_refill(hw, 0, INT_MAX);
4830
Brian Cavagnolo73b46322011-03-17 11:58:41 -07004831 /* For the sta firmware, we need to know the dma addresses of tx queues
4832 * before sending MWL8K_CMD_GET_HW_SPEC. So we must initialize them
4833 * prior to issuing this command. But for the AP case, we learn the
4834 * total number of queues from the result CMD_GET_HW_SPEC, so for this
4835 * case we must initialize the tx queues after.
4836 */
Brian Cavagnolo8a7a5782011-03-17 11:58:42 -07004837 priv->num_ampdu_queues = 0;
Brian Cavagnolo73b46322011-03-17 11:58:41 -07004838 if (!priv->ap_fw) {
4839 rc = mwl8k_init_txqs(hw);
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004840 if (rc)
4841 goto err_free_queues;
4842 }
4843
4844 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
4845 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
4846 iowrite32(MWL8K_A2H_INT_TX_DONE | MWL8K_A2H_INT_RX_READY,
4847 priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL);
4848 iowrite32(0xffffffff, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
4849
4850 rc = request_irq(priv->pdev->irq, mwl8k_interrupt,
4851 IRQF_SHARED, MWL8K_NAME, hw);
4852 if (rc) {
4853 wiphy_err(hw->wiphy, "failed to register IRQ handler\n");
4854 goto err_free_queues;
4855 }
4856
4857 /*
4858 * Temporarily enable interrupts. Initial firmware host
4859 * commands use interrupts and avoid polling. Disable
4860 * interrupts when done.
4861 */
4862 iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
4863
4864 /* Get config data, mac addrs etc */
4865 if (priv->ap_fw) {
4866 rc = mwl8k_cmd_get_hw_spec_ap(hw);
4867 if (!rc)
Brian Cavagnolo73b46322011-03-17 11:58:41 -07004868 rc = mwl8k_init_txqs(hw);
4869 if (!rc)
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004870 rc = mwl8k_cmd_set_hw_spec(hw);
4871 } else {
4872 rc = mwl8k_cmd_get_hw_spec_sta(hw);
4873 }
4874 if (rc) {
4875 wiphy_err(hw->wiphy, "Cannot initialise firmware\n");
4876 goto err_free_irq;
4877 }
4878
4879 /* Turn radio off */
4880 rc = mwl8k_cmd_radio_disable(hw);
4881 if (rc) {
4882 wiphy_err(hw->wiphy, "Cannot disable\n");
4883 goto err_free_irq;
4884 }
4885
4886 /* Clear MAC address */
4887 rc = mwl8k_cmd_set_mac_addr(hw, NULL, "\x00\x00\x00\x00\x00\x00");
4888 if (rc) {
4889 wiphy_err(hw->wiphy, "Cannot clear MAC address\n");
4890 goto err_free_irq;
4891 }
4892
4893 /* Disable interrupts */
4894 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
4895 free_irq(priv->pdev->irq, hw);
4896
4897 wiphy_info(hw->wiphy, "%s v%d, %pm, %s firmware %u.%u.%u.%u\n",
4898 priv->device_info->part_name,
4899 priv->hw_rev, hw->wiphy->perm_addr,
4900 priv->ap_fw ? "AP" : "STA",
4901 (priv->fw_rev >> 24) & 0xff, (priv->fw_rev >> 16) & 0xff,
4902 (priv->fw_rev >> 8) & 0xff, priv->fw_rev & 0xff);
4903
4904 return 0;
4905
4906err_free_irq:
4907 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
4908 free_irq(priv->pdev->irq, hw);
4909
4910err_free_queues:
Brian Cavagnoloe6007072011-03-17 11:58:44 -07004911 for (i = 0; i < mwl8k_tx_queues(priv); i++)
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004912 mwl8k_txq_deinit(hw, i);
4913 mwl8k_rxq_deinit(hw, 0);
4914
4915err_stop_firmware:
4916 mwl8k_hw_reset(priv);
4917
4918 return rc;
4919}
4920
4921/*
4922 * invoke mwl8k_reload_firmware to change the firmware image after the device
4923 * has already been registered
4924 */
4925static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)
4926{
4927 int i, rc = 0;
4928 struct mwl8k_priv *priv = hw->priv;
4929
4930 mwl8k_stop(hw);
4931 mwl8k_rxq_deinit(hw, 0);
4932
Brian Cavagnoloe6007072011-03-17 11:58:44 -07004933 for (i = 0; i < mwl8k_tx_queues(priv); i++)
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004934 mwl8k_txq_deinit(hw, i);
4935
Brian Cavagnolo99020472010-11-12 17:23:52 -08004936 rc = mwl8k_init_firmware(hw, fw_image, false);
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004937 if (rc)
4938 goto fail;
4939
4940 rc = mwl8k_probe_hw(hw);
4941 if (rc)
4942 goto fail;
4943
4944 rc = mwl8k_start(hw);
4945 if (rc)
4946 goto fail;
4947
4948 rc = mwl8k_config(hw, ~0);
4949 if (rc)
4950 goto fail;
4951
Brian Cavagnoloe6007072011-03-17 11:58:44 -07004952 for (i = 0; i < MWL8K_TX_WMM_QUEUES; i++) {
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004953 rc = mwl8k_conf_tx(hw, i, &priv->wmm_params[i]);
4954 if (rc)
4955 goto fail;
4956 }
4957
4958 return rc;
4959
4960fail:
4961 printk(KERN_WARNING "mwl8k: Failed to reload firmware image.\n");
4962 return rc;
4963}
4964
4965static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
4966{
4967 struct ieee80211_hw *hw = priv->hw;
4968 int i, rc;
4969
Brian Cavagnolo99020472010-11-12 17:23:52 -08004970 rc = mwl8k_load_firmware(hw);
4971 mwl8k_release_firmware(priv);
4972 if (rc) {
4973 wiphy_err(hw->wiphy, "Cannot start firmware\n");
4974 return rc;
4975 }
4976
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004977 /*
4978 * Extra headroom is the size of the required DMA header
4979 * minus the size of the smallest 802.11 frame (CTS frame).
4980 */
4981 hw->extra_tx_headroom =
4982 sizeof(struct mwl8k_dma_data) - sizeof(struct ieee80211_cts);
4983
4984 hw->channel_change_time = 10;
4985
Brian Cavagnoloe6007072011-03-17 11:58:44 -07004986 hw->queues = MWL8K_TX_WMM_QUEUES;
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004987
4988 /* Set rssi values to dBm */
Nishant Sarmukadam0bf22c32011-02-17 14:45:17 -08004989 hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_HAS_RATE_CONTROL;
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004990 hw->vif_data_size = sizeof(struct mwl8k_vif);
4991 hw->sta_data_size = sizeof(struct mwl8k_sta);
4992
4993 priv->macids_used = 0;
4994 INIT_LIST_HEAD(&priv->vif_list);
4995
4996 /* Set default radio state and preamble */
4997 priv->radio_on = 0;
4998 priv->radio_short_preamble = 0;
4999
5000 /* Finalize join worker */
5001 INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker);
5002
5003 /* TX reclaim and RX tasklets. */
5004 tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw);
5005 tasklet_disable(&priv->poll_tx_task);
5006 tasklet_init(&priv->poll_rx_task, mwl8k_rx_poll, (unsigned long)hw);
5007 tasklet_disable(&priv->poll_rx_task);
5008
5009 /* Power management cookie */
5010 priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma);
5011 if (priv->cookie == NULL)
5012 return -ENOMEM;
5013
5014 mutex_init(&priv->fw_mutex);
5015 priv->fw_mutex_owner = NULL;
5016 priv->fw_mutex_depth = 0;
5017 priv->hostcmd_wait = NULL;
5018
5019 spin_lock_init(&priv->tx_lock);
5020
5021 priv->tx_wait = NULL;
5022
5023 rc = mwl8k_probe_hw(hw);
5024 if (rc)
5025 goto err_free_cookie;
5026
5027 hw->wiphy->interface_modes = 0;
5028 if (priv->ap_macids_supported || priv->device_info->fw_image_ap)
5029 hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP);
5030 if (priv->sta_macids_supported || priv->device_info->fw_image_sta)
5031 hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
5032
5033 rc = ieee80211_register_hw(hw);
5034 if (rc) {
5035 wiphy_err(hw->wiphy, "Cannot register device\n");
5036 goto err_unprobe_hw;
5037 }
5038
5039 return 0;
5040
5041err_unprobe_hw:
Brian Cavagnoloe6007072011-03-17 11:58:44 -07005042 for (i = 0; i < mwl8k_tx_queues(priv); i++)
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08005043 mwl8k_txq_deinit(hw, i);
5044 mwl8k_rxq_deinit(hw, 0);
5045
5046err_free_cookie:
5047 if (priv->cookie != NULL)
5048 pci_free_consistent(priv->pdev, 4,
5049 priv->cookie, priv->cookie_dma);
5050
5051 return rc;
5052}
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005053static int __devinit mwl8k_probe(struct pci_dev *pdev,
5054 const struct pci_device_id *id)
5055{
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08005056 static int printed_version;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005057 struct ieee80211_hw *hw;
5058 struct mwl8k_priv *priv;
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08005059 struct mwl8k_device_info *di;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005060 int rc;
Lennert Buytenhek2aa7b012009-08-24 15:48:07 +02005061
5062 if (!printed_version) {
5063 printk(KERN_INFO "%s version %s\n", MWL8K_DESC, MWL8K_VERSION);
5064 printed_version = 1;
5065 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005066
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +01005067
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005068 rc = pci_enable_device(pdev);
5069 if (rc) {
5070 printk(KERN_ERR "%s: Cannot enable new PCI device\n",
5071 MWL8K_NAME);
5072 return rc;
5073 }
5074
5075 rc = pci_request_regions(pdev, MWL8K_NAME);
5076 if (rc) {
5077 printk(KERN_ERR "%s: Cannot obtain PCI resources\n",
5078 MWL8K_NAME);
Lennert Buytenhek3db95e52009-11-30 18:13:34 +01005079 goto err_disable_device;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005080 }
5081
5082 pci_set_master(pdev);
5083
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +01005084
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005085 hw = ieee80211_alloc_hw(sizeof(*priv), &mwl8k_ops);
5086 if (hw == NULL) {
5087 printk(KERN_ERR "%s: ieee80211 alloc failed\n", MWL8K_NAME);
5088 rc = -ENOMEM;
5089 goto err_free_reg;
5090 }
5091
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +01005092 SET_IEEE80211_DEV(hw, &pdev->dev);
5093 pci_set_drvdata(pdev, hw);
5094
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005095 priv = hw->priv;
5096 priv->hw = hw;
5097 priv->pdev = pdev;
John W. Linvillebcb628d2009-11-06 16:40:16 -05005098 priv->device_info = &mwl8k_info_tbl[id->driver_data];
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005099
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005100
Lennert Buytenhek5b9482d2009-10-22 20:20:43 +02005101 priv->sram = pci_iomap(pdev, 0, 0x10000);
5102 if (priv->sram == NULL) {
Joe Perches5db55842010-08-11 19:11:19 -07005103 wiphy_err(hw->wiphy, "Cannot map device SRAM\n");
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005104 goto err_iounmap;
5105 }
5106
Lennert Buytenhek5b9482d2009-10-22 20:20:43 +02005107 /*
5108 * If BAR0 is a 32 bit BAR, the register BAR will be BAR1.
5109 * If BAR0 is a 64 bit BAR, the register BAR will be BAR2.
5110 */
5111 priv->regs = pci_iomap(pdev, 1, 0x10000);
5112 if (priv->regs == NULL) {
5113 priv->regs = pci_iomap(pdev, 2, 0x10000);
5114 if (priv->regs == NULL) {
Joe Perches5db55842010-08-11 19:11:19 -07005115 wiphy_err(hw->wiphy, "Cannot map device registers\n");
Lennert Buytenhek5b9482d2009-10-22 20:20:43 +02005116 goto err_iounmap;
5117 }
5118 }
5119
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08005120 /*
Brian Cavagnolo99020472010-11-12 17:23:52 -08005121 * Choose the initial fw image depending on user input. If a second
5122 * image is available, make it the alternative image that will be
5123 * loaded if the first one fails.
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08005124 */
Brian Cavagnolo99020472010-11-12 17:23:52 -08005125 init_completion(&priv->firmware_loading_complete);
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08005126 di = priv->device_info;
Brian Cavagnolo99020472010-11-12 17:23:52 -08005127 if (ap_mode_default && di->fw_image_ap) {
5128 priv->fw_pref = di->fw_image_ap;
5129 priv->fw_alt = di->fw_image_sta;
5130 } else if (!ap_mode_default && di->fw_image_sta) {
5131 priv->fw_pref = di->fw_image_sta;
5132 priv->fw_alt = di->fw_image_ap;
5133 } else if (ap_mode_default && !di->fw_image_ap && di->fw_image_sta) {
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08005134 printk(KERN_WARNING "AP fw is unavailable. Using STA fw.");
Brian Cavagnolo99020472010-11-12 17:23:52 -08005135 priv->fw_pref = di->fw_image_sta;
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08005136 } else if (!ap_mode_default && !di->fw_image_sta && di->fw_image_ap) {
5137 printk(KERN_WARNING "STA fw is unavailable. Using AP fw.");
Brian Cavagnolo99020472010-11-12 17:23:52 -08005138 priv->fw_pref = di->fw_image_ap;
5139 }
5140 rc = mwl8k_init_firmware(hw, priv->fw_pref, true);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005141 if (rc)
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08005142 goto err_stop_firmware;
Brian Cavagnolo99020472010-11-12 17:23:52 -08005143 return rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005144
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +01005145err_stop_firmware:
5146 mwl8k_hw_reset(priv);
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +01005147
5148err_iounmap:
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005149 if (priv->regs != NULL)
5150 pci_iounmap(pdev, priv->regs);
5151
Lennert Buytenhek5b9482d2009-10-22 20:20:43 +02005152 if (priv->sram != NULL)
5153 pci_iounmap(pdev, priv->sram);
5154
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005155 pci_set_drvdata(pdev, NULL);
5156 ieee80211_free_hw(hw);
5157
5158err_free_reg:
5159 pci_release_regions(pdev);
Lennert Buytenhek3db95e52009-11-30 18:13:34 +01005160
5161err_disable_device:
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005162 pci_disable_device(pdev);
5163
5164 return rc;
5165}
5166
Joerg Albert230f7af2009-04-18 02:10:45 +02005167static void __devexit mwl8k_shutdown(struct pci_dev *pdev)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005168{
5169 printk(KERN_ERR "===>%s(%u)\n", __func__, __LINE__);
5170}
5171
Joerg Albert230f7af2009-04-18 02:10:45 +02005172static void __devexit mwl8k_remove(struct pci_dev *pdev)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005173{
5174 struct ieee80211_hw *hw = pci_get_drvdata(pdev);
5175 struct mwl8k_priv *priv;
5176 int i;
5177
5178 if (hw == NULL)
5179 return;
5180 priv = hw->priv;
5181
Brian Cavagnolo99020472010-11-12 17:23:52 -08005182 wait_for_completion(&priv->firmware_loading_complete);
5183
5184 if (priv->fw_state == FW_STATE_ERROR) {
5185 mwl8k_hw_reset(priv);
5186 goto unmap;
5187 }
5188
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005189 ieee80211_stop_queues(hw);
5190
Lennert Buytenhek60aa5692009-08-03 21:59:09 +02005191 ieee80211_unregister_hw(hw);
5192
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01005193 /* Remove TX reclaim and RX tasklets. */
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +01005194 tasklet_kill(&priv->poll_tx_task);
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01005195 tasklet_kill(&priv->poll_rx_task);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005196
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005197 /* Stop hardware */
5198 mwl8k_hw_reset(priv);
5199
5200 /* Return all skbs to mac80211 */
Brian Cavagnoloe6007072011-03-17 11:58:44 -07005201 for (i = 0; i < mwl8k_tx_queues(priv); i++)
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01005202 mwl8k_txq_reclaim(hw, i, INT_MAX, 1);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005203
Brian Cavagnoloe6007072011-03-17 11:58:44 -07005204 for (i = 0; i < mwl8k_tx_queues(priv); i++)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005205 mwl8k_txq_deinit(hw, i);
5206
5207 mwl8k_rxq_deinit(hw, 0);
5208
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +02005209 pci_free_consistent(priv->pdev, 4, priv->cookie, priv->cookie_dma);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005210
Brian Cavagnolo99020472010-11-12 17:23:52 -08005211unmap:
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005212 pci_iounmap(pdev, priv->regs);
Lennert Buytenhek5b9482d2009-10-22 20:20:43 +02005213 pci_iounmap(pdev, priv->sram);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005214 pci_set_drvdata(pdev, NULL);
5215 ieee80211_free_hw(hw);
5216 pci_release_regions(pdev);
5217 pci_disable_device(pdev);
5218}
5219
5220static struct pci_driver mwl8k_driver = {
5221 .name = MWL8K_NAME,
Lennert Buytenhek45a390d2009-10-22 20:20:47 +02005222 .id_table = mwl8k_pci_id_table,
Lennert Buytenheka66098d2009-03-10 10:13:33 +01005223 .probe = mwl8k_probe,
5224 .remove = __devexit_p(mwl8k_remove),
5225 .shutdown = __devexit_p(mwl8k_shutdown),
5226};
5227
5228static int __init mwl8k_init(void)
5229{
5230 return pci_register_driver(&mwl8k_driver);
5231}
5232
5233static void __exit mwl8k_exit(void)
5234{
5235 pci_unregister_driver(&mwl8k_driver);
5236}
5237
5238module_init(mwl8k_init);
5239module_exit(mwl8k_exit);
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +02005240
5241MODULE_DESCRIPTION(MWL8K_DESC);
5242MODULE_VERSION(MWL8K_VERSION);
5243MODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com>");
5244MODULE_LICENSE("GPL");