blob: cbf72714e74dca0469d708f71c66cd5c046a48a8 [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
88#define MWL8K_TX_QUEUES 4
89
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +020090struct rxd_ops {
91 int rxd_size;
92 void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr);
93 void (*rxd_refill)(void *rxd, dma_addr_t addr, int len);
Lennert Buytenhek20f09c32009-11-30 18:12:08 +010094 int (*rxd_process)(void *rxd, struct ieee80211_rx_status *status,
John W. Linville0d462bb2010-07-28 14:04:24 -040095 __le16 *qos, s8 *noise);
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +020096};
97
Lennert Buytenhek45a390d2009-10-22 20:20:47 +020098struct mwl8k_device_info {
Lennert Buytenheka74b2952009-10-22 20:20:50 +020099 char *part_name;
100 char *helper_image;
Brian Cavagnolo0863ade2010-11-12 17:23:50 -0800101 char *fw_image_sta;
102 char *fw_image_ap;
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100103 struct rxd_ops *ap_rxd_ops;
Lennert Buytenhek45a390d2009-10-22 20:20:47 +0200104};
105
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100106struct mwl8k_rx_queue {
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200107 int rxd_count;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100108
109 /* hw receives here */
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200110 int head;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100111
112 /* refill descs here */
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200113 int tail;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100114
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200115 void *rxd;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200116 dma_addr_t rxd_dma;
Lennert Buytenhek788838e2009-10-22 20:20:56 +0200117 struct {
118 struct sk_buff *skb;
FUJITA Tomonori53b1b3e2010-04-02 13:10:38 +0900119 DEFINE_DMA_UNMAP_ADDR(dma);
Lennert Buytenhek788838e2009-10-22 20:20:56 +0200120 } *buf;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100121};
122
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100123struct mwl8k_tx_queue {
124 /* hw transmits here */
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200125 int head;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100126
127 /* sw appends here */
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200128 int tail;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100129
Kalle Valo8ccbc3b2010-02-07 10:20:52 +0200130 unsigned int len;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200131 struct mwl8k_tx_desc *txd;
132 dma_addr_t txd_dma;
133 struct sk_buff **skb;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100134};
135
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100136struct mwl8k_priv {
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100137 struct ieee80211_hw *hw;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100138 struct pci_dev *pdev;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100139
Lennert Buytenhek45a390d2009-10-22 20:20:47 +0200140 struct mwl8k_device_info *device_info;
141
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +0100142 void __iomem *sram;
143 void __iomem *regs;
144
145 /* firmware */
Lennert Buytenhek22be40d2009-11-30 18:32:38 +0100146 struct firmware *fw_helper;
147 struct firmware *fw_ucode;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100148
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +0100149 /* hardware/firmware parameters */
150 bool ap_fw;
151 struct rxd_ops *rxd_ops;
Lennert Buytenhek777ad372010-01-12 13:48:04 +0100152 struct ieee80211_supported_band band_24;
153 struct ieee80211_channel channels_24[14];
154 struct ieee80211_rate rates_24[14];
Lennert Buytenhek4eae9ed2010-01-12 13:48:32 +0100155 struct ieee80211_supported_band band_50;
156 struct ieee80211_channel channels_50[4];
157 struct ieee80211_rate rates_50[9];
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +0100158 u32 ap_macids_supported;
159 u32 sta_macids_supported;
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +0100160
Lennert Buytenhek618952a2009-08-18 03:18:01 +0200161 /* firmware access */
162 struct mutex fw_mutex;
163 struct task_struct *fw_mutex_owner;
164 int fw_mutex_depth;
Lennert Buytenhek618952a2009-08-18 03:18:01 +0200165 struct completion *hostcmd_wait;
166
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100167 /* lock held over TX and TX reap */
168 spinlock_t tx_lock;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100169
Lennert Buytenhek88de754a2009-10-22 20:19:37 +0200170 /* TX quiesce completion, protected by fw_mutex and tx_lock */
171 struct completion *tx_wait;
172
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +0100173 /* List of interfaces. */
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +0100174 u32 macids_used;
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +0100175 struct list_head vif_list;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100176
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100177 /* power management status cookie from firmware */
178 u32 *cookie;
179 dma_addr_t cookie_dma;
180
181 u16 num_mcaddrs;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100182 u8 hw_rev;
Lennert Buytenhek2aa7b012009-08-24 15:48:07 +0200183 u32 fw_rev;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100184
185 /*
186 * Running count of TX packets in flight, to avoid
187 * iterating over the transmit rings each time.
188 */
189 int pending_tx_pkts;
190
191 struct mwl8k_rx_queue rxq[MWL8K_RX_QUEUES];
192 struct mwl8k_tx_queue txq[MWL8K_TX_QUEUES];
193
Lennert Buytenhekc46563b2009-07-16 12:14:58 +0200194 bool radio_on;
Lennert Buytenhek68ce3882009-07-16 12:26:57 +0200195 bool radio_short_preamble;
Lennert Buytenheka43c49a2009-10-22 20:20:32 +0200196 bool sniffer_enabled;
Lennert Buytenhek0439b1f2009-07-16 12:34:02 +0200197 bool wmm_enabled;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100198
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100199 /* XXX need to convert this to handle multiple interfaces */
200 bool capture_beacon;
Lennert Buytenhekd89173f2009-07-16 09:54:27 +0200201 u8 capture_bssid[ETH_ALEN];
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100202 struct sk_buff *beacon_skb;
203
204 /*
205 * This FJ worker has to be global as it is scheduled from the
206 * RX handler. At this point we don't know which interface it
207 * belongs to until the list of bssids waiting to complete join
208 * is checked.
209 */
210 struct work_struct finalize_join_worker;
211
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +0100212 /* Tasklet to perform TX reclaim. */
213 struct tasklet_struct poll_tx_task;
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +0100214
215 /* Tasklet to perform RX. */
216 struct tasklet_struct poll_rx_task;
John W. Linville0d462bb2010-07-28 14:04:24 -0400217
218 /* Most recently reported noise in dBm */
219 s8 noise;
Brian Cavagnolo0863ade2010-11-12 17:23:50 -0800220
221 /*
222 * preserve the queue configurations so they can be restored if/when
223 * the firmware image is swapped.
224 */
225 struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_QUEUES];
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100226};
227
228/* Per interface specific private data */
229struct mwl8k_vif {
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +0100230 struct list_head list;
231 struct ieee80211_vif *vif;
232
Lennert Buytenhekf57ca9c2010-01-12 13:50:14 +0100233 /* Firmware macid for this vif. */
234 int macid;
235
Lennert Buytenhekc2c2b122010-01-08 18:27:59 +0100236 /* Non AMPDU sequence number assigned by driver. */
Lennert Buytenheka6804002010-01-04 21:55:42 +0100237 u16 seqno;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100238};
Lennert Buytenheka94cc972009-08-03 21:58:57 +0200239#define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv))
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100240
Lennert Buytenheka6804002010-01-04 21:55:42 +0100241struct mwl8k_sta {
242 /* Index into station database. Returned by UPDATE_STADB. */
243 u8 peer_id;
244};
245#define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv))
246
Lennert Buytenhek777ad372010-01-12 13:48:04 +0100247static const struct ieee80211_channel mwl8k_channels_24[] = {
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100248 { .center_freq = 2412, .hw_value = 1, },
249 { .center_freq = 2417, .hw_value = 2, },
250 { .center_freq = 2422, .hw_value = 3, },
251 { .center_freq = 2427, .hw_value = 4, },
252 { .center_freq = 2432, .hw_value = 5, },
253 { .center_freq = 2437, .hw_value = 6, },
254 { .center_freq = 2442, .hw_value = 7, },
255 { .center_freq = 2447, .hw_value = 8, },
256 { .center_freq = 2452, .hw_value = 9, },
257 { .center_freq = 2457, .hw_value = 10, },
258 { .center_freq = 2462, .hw_value = 11, },
Lennert Buytenhek647ca6b2009-11-30 18:32:20 +0100259 { .center_freq = 2467, .hw_value = 12, },
260 { .center_freq = 2472, .hw_value = 13, },
261 { .center_freq = 2484, .hw_value = 14, },
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100262};
263
Lennert Buytenhek777ad372010-01-12 13:48:04 +0100264static const struct ieee80211_rate mwl8k_rates_24[] = {
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100265 { .bitrate = 10, .hw_value = 2, },
266 { .bitrate = 20, .hw_value = 4, },
267 { .bitrate = 55, .hw_value = 11, },
Lennert Buytenhek5dfd3e22009-10-22 20:20:29 +0200268 { .bitrate = 110, .hw_value = 22, },
269 { .bitrate = 220, .hw_value = 44, },
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100270 { .bitrate = 60, .hw_value = 12, },
271 { .bitrate = 90, .hw_value = 18, },
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100272 { .bitrate = 120, .hw_value = 24, },
273 { .bitrate = 180, .hw_value = 36, },
274 { .bitrate = 240, .hw_value = 48, },
275 { .bitrate = 360, .hw_value = 72, },
276 { .bitrate = 480, .hw_value = 96, },
277 { .bitrate = 540, .hw_value = 108, },
Lennert Buytenhek140eb5e2009-11-30 18:11:44 +0100278 { .bitrate = 720, .hw_value = 144, },
279};
280
Lennert Buytenhek4eae9ed2010-01-12 13:48:32 +0100281static const struct ieee80211_channel mwl8k_channels_50[] = {
282 { .center_freq = 5180, .hw_value = 36, },
283 { .center_freq = 5200, .hw_value = 40, },
284 { .center_freq = 5220, .hw_value = 44, },
285 { .center_freq = 5240, .hw_value = 48, },
286};
287
288static const struct ieee80211_rate mwl8k_rates_50[] = {
289 { .bitrate = 60, .hw_value = 12, },
290 { .bitrate = 90, .hw_value = 18, },
291 { .bitrate = 120, .hw_value = 24, },
292 { .bitrate = 180, .hw_value = 36, },
293 { .bitrate = 240, .hw_value = 48, },
294 { .bitrate = 360, .hw_value = 72, },
295 { .bitrate = 480, .hw_value = 96, },
296 { .bitrate = 540, .hw_value = 108, },
297 { .bitrate = 720, .hw_value = 144, },
298};
299
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100300/* Set or get info from Firmware */
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100301#define MWL8K_CMD_GET 0x0000
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -0800302#define MWL8K_CMD_SET 0x0001
303#define MWL8K_CMD_SET_LIST 0x0002
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100304
305/* Firmware command codes */
306#define MWL8K_CMD_CODE_DNLD 0x0001
307#define MWL8K_CMD_GET_HW_SPEC 0x0003
Lennert Buytenhek42fba212009-10-22 20:21:30 +0200308#define MWL8K_CMD_SET_HW_SPEC 0x0004
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100309#define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010
310#define MWL8K_CMD_GET_STAT 0x0014
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200311#define MWL8K_CMD_RADIO_CONTROL 0x001c
312#define MWL8K_CMD_RF_TX_POWER 0x001e
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -0800313#define MWL8K_CMD_TX_POWER 0x001f
Lennert Buytenhek08b06342009-10-22 20:21:33 +0200314#define MWL8K_CMD_RF_ANTENNA 0x0020
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +0100315#define MWL8K_CMD_SET_BEACON 0x0100 /* per-vif */
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100316#define MWL8K_CMD_SET_PRE_SCAN 0x0107
317#define MWL8K_CMD_SET_POST_SCAN 0x0108
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200318#define MWL8K_CMD_SET_RF_CHANNEL 0x010a
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100319#define MWL8K_CMD_SET_AID 0x010d
320#define MWL8K_CMD_SET_RATE 0x0110
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200321#define MWL8K_CMD_SET_FINALIZE_JOIN 0x0111
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100322#define MWL8K_CMD_RTS_THRESHOLD 0x0113
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200323#define MWL8K_CMD_SET_SLOT 0x0114
324#define MWL8K_CMD_SET_EDCA_PARAMS 0x0115
325#define MWL8K_CMD_SET_WMM_MODE 0x0123
326#define MWL8K_CMD_MIMO_CONFIG 0x0125
327#define MWL8K_CMD_USE_FIXED_RATE 0x0126
328#define MWL8K_CMD_ENABLE_SNIFFER 0x0150
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +0100329#define MWL8K_CMD_SET_MAC_ADDR 0x0202 /* per-vif */
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200330#define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +0100331#define MWL8K_CMD_BSS_START 0x1100 /* per-vif */
332#define MWL8K_CMD_SET_NEW_STN 0x1111 /* per-vif */
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200333#define MWL8K_CMD_UPDATE_STADB 0x1123
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100334
John W. Linvilleb6037422010-07-20 13:55:00 -0400335static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize)
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100336{
John W. Linvilleb6037422010-07-20 13:55:00 -0400337 u16 command = le16_to_cpu(cmd);
338
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100339#define MWL8K_CMDNAME(x) case MWL8K_CMD_##x: do {\
340 snprintf(buf, bufsize, "%s", #x);\
341 return buf;\
342 } while (0)
John W. Linvilleb6037422010-07-20 13:55:00 -0400343 switch (command & ~0x8000) {
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100344 MWL8K_CMDNAME(CODE_DNLD);
345 MWL8K_CMDNAME(GET_HW_SPEC);
Lennert Buytenhek42fba212009-10-22 20:21:30 +0200346 MWL8K_CMDNAME(SET_HW_SPEC);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100347 MWL8K_CMDNAME(MAC_MULTICAST_ADR);
348 MWL8K_CMDNAME(GET_STAT);
349 MWL8K_CMDNAME(RADIO_CONTROL);
350 MWL8K_CMDNAME(RF_TX_POWER);
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -0800351 MWL8K_CMDNAME(TX_POWER);
Lennert Buytenhek08b06342009-10-22 20:21:33 +0200352 MWL8K_CMDNAME(RF_ANTENNA);
Lennert Buytenhekb64fe612010-01-08 18:31:39 +0100353 MWL8K_CMDNAME(SET_BEACON);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100354 MWL8K_CMDNAME(SET_PRE_SCAN);
355 MWL8K_CMDNAME(SET_POST_SCAN);
356 MWL8K_CMDNAME(SET_RF_CHANNEL);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100357 MWL8K_CMDNAME(SET_AID);
358 MWL8K_CMDNAME(SET_RATE);
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200359 MWL8K_CMDNAME(SET_FINALIZE_JOIN);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100360 MWL8K_CMDNAME(RTS_THRESHOLD);
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200361 MWL8K_CMDNAME(SET_SLOT);
362 MWL8K_CMDNAME(SET_EDCA_PARAMS);
363 MWL8K_CMDNAME(SET_WMM_MODE);
364 MWL8K_CMDNAME(MIMO_CONFIG);
365 MWL8K_CMDNAME(USE_FIXED_RATE);
366 MWL8K_CMDNAME(ENABLE_SNIFFER);
Lennert Buytenhek32060e12009-10-22 20:20:04 +0200367 MWL8K_CMDNAME(SET_MAC_ADDR);
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200368 MWL8K_CMDNAME(SET_RATEADAPT_MODE);
Lennert Buytenhekb64fe612010-01-08 18:31:39 +0100369 MWL8K_CMDNAME(BSS_START);
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +0100370 MWL8K_CMDNAME(SET_NEW_STN);
Lennert Buytenhekff45fc62009-07-16 11:50:36 +0200371 MWL8K_CMDNAME(UPDATE_STADB);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100372 default:
373 snprintf(buf, bufsize, "0x%x", cmd);
374 }
375#undef MWL8K_CMDNAME
376
377 return buf;
378}
379
380/* Hardware and firmware reset */
381static void mwl8k_hw_reset(struct mwl8k_priv *priv)
382{
383 iowrite32(MWL8K_H2A_INT_RESET,
384 priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
385 iowrite32(MWL8K_H2A_INT_RESET,
386 priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
387 msleep(20);
388}
389
390/* Release fw image */
391static void mwl8k_release_fw(struct firmware **fw)
392{
393 if (*fw == NULL)
394 return;
395 release_firmware(*fw);
396 *fw = NULL;
397}
398
399static void mwl8k_release_firmware(struct mwl8k_priv *priv)
400{
Lennert Buytenhek22be40d2009-11-30 18:32:38 +0100401 mwl8k_release_fw(&priv->fw_ucode);
402 mwl8k_release_fw(&priv->fw_helper);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100403}
404
405/* Request fw image */
406static int mwl8k_request_fw(struct mwl8k_priv *priv,
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200407 const char *fname, struct firmware **fw)
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100408{
409 /* release current image */
410 if (*fw != NULL)
411 mwl8k_release_fw(fw);
412
413 return request_firmware((const struct firmware **)fw,
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200414 fname, &priv->pdev->dev);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100415}
416
Brian Cavagnolo0863ade2010-11-12 17:23:50 -0800417static int mwl8k_request_firmware(struct mwl8k_priv *priv, char *fw_image)
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100418{
Lennert Buytenheka74b2952009-10-22 20:20:50 +0200419 struct mwl8k_device_info *di = priv->device_info;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100420 int rc;
421
Lennert Buytenheka74b2952009-10-22 20:20:50 +0200422 if (di->helper_image != NULL) {
Lennert Buytenhek22be40d2009-11-30 18:32:38 +0100423 rc = mwl8k_request_fw(priv, di->helper_image, &priv->fw_helper);
Lennert Buytenheka74b2952009-10-22 20:20:50 +0200424 if (rc) {
425 printk(KERN_ERR "%s: Error requesting helper "
426 "firmware file %s\n", pci_name(priv->pdev),
427 di->helper_image);
428 return rc;
429 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100430 }
431
Brian Cavagnolo0863ade2010-11-12 17:23:50 -0800432 rc = mwl8k_request_fw(priv, fw_image, &priv->fw_ucode);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100433 if (rc) {
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200434 printk(KERN_ERR "%s: Error requesting firmware file %s\n",
Brian Cavagnolo0863ade2010-11-12 17:23:50 -0800435 pci_name(priv->pdev), fw_image);
Lennert Buytenhek22be40d2009-11-30 18:32:38 +0100436 mwl8k_release_fw(&priv->fw_helper);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100437 return rc;
438 }
439
440 return 0;
441}
442
443struct mwl8k_cmd_pkt {
444 __le16 code;
445 __le16 length;
Lennert Buytenhekf57ca9c2010-01-12 13:50:14 +0100446 __u8 seq_num;
447 __u8 macid;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100448 __le16 result;
449 char payload[0];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000450} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100451
452/*
453 * Firmware loading.
454 */
455static int
456mwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length)
457{
458 void __iomem *regs = priv->regs;
459 dma_addr_t dma_addr;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100460 int loops;
461
462 dma_addr = pci_map_single(priv->pdev, data, length, PCI_DMA_TODEVICE);
463 if (pci_dma_mapping_error(priv->pdev, dma_addr))
464 return -ENOMEM;
465
466 iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR);
467 iowrite32(0, regs + MWL8K_HIU_INT_CODE);
468 iowrite32(MWL8K_H2A_INT_DOORBELL,
469 regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
470 iowrite32(MWL8K_H2A_INT_DUMMY,
471 regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
472
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100473 loops = 1000;
474 do {
475 u32 int_code;
476
477 int_code = ioread32(regs + MWL8K_HIU_INT_CODE);
478 if (int_code == MWL8K_INT_CODE_CMD_FINISHED) {
479 iowrite32(0, regs + MWL8K_HIU_INT_CODE);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100480 break;
481 }
482
Lennert Buytenhek3d76e822009-10-22 20:20:16 +0200483 cond_resched();
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100484 udelay(1);
485 } while (--loops);
486
487 pci_unmap_single(priv->pdev, dma_addr, length, PCI_DMA_TODEVICE);
488
Lennert Buytenhekd4b705702009-07-16 12:44:45 +0200489 return loops ? 0 : -ETIMEDOUT;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100490}
491
492static int mwl8k_load_fw_image(struct mwl8k_priv *priv,
493 const u8 *data, size_t length)
494{
495 struct mwl8k_cmd_pkt *cmd;
496 int done;
497 int rc = 0;
498
499 cmd = kmalloc(sizeof(*cmd) + 256, GFP_KERNEL);
500 if (cmd == NULL)
501 return -ENOMEM;
502
503 cmd->code = cpu_to_le16(MWL8K_CMD_CODE_DNLD);
504 cmd->seq_num = 0;
Lennert Buytenhekf57ca9c2010-01-12 13:50:14 +0100505 cmd->macid = 0;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100506 cmd->result = 0;
507
508 done = 0;
509 while (length) {
510 int block_size = length > 256 ? 256 : length;
511
512 memcpy(cmd->payload, data + done, block_size);
513 cmd->length = cpu_to_le16(block_size);
514
515 rc = mwl8k_send_fw_load_cmd(priv, cmd,
516 sizeof(*cmd) + block_size);
517 if (rc)
518 break;
519
520 done += block_size;
521 length -= block_size;
522 }
523
524 if (!rc) {
525 cmd->length = 0;
526 rc = mwl8k_send_fw_load_cmd(priv, cmd, sizeof(*cmd));
527 }
528
529 kfree(cmd);
530
531 return rc;
532}
533
534static int mwl8k_feed_fw_image(struct mwl8k_priv *priv,
535 const u8 *data, size_t length)
536{
537 unsigned char *buffer;
538 int may_continue, rc = 0;
539 u32 done, prev_block_size;
540
541 buffer = kmalloc(1024, GFP_KERNEL);
542 if (buffer == NULL)
543 return -ENOMEM;
544
545 done = 0;
546 prev_block_size = 0;
547 may_continue = 1000;
548 while (may_continue > 0) {
549 u32 block_size;
550
551 block_size = ioread32(priv->regs + MWL8K_HIU_SCRATCH);
552 if (block_size & 1) {
553 block_size &= ~1;
554 may_continue--;
555 } else {
556 done += prev_block_size;
557 length -= prev_block_size;
558 }
559
560 if (block_size > 1024 || block_size > length) {
561 rc = -EOVERFLOW;
562 break;
563 }
564
565 if (length == 0) {
566 rc = 0;
567 break;
568 }
569
570 if (block_size == 0) {
571 rc = -EPROTO;
572 may_continue--;
573 udelay(1);
574 continue;
575 }
576
577 prev_block_size = block_size;
578 memcpy(buffer, data + done, block_size);
579
580 rc = mwl8k_send_fw_load_cmd(priv, buffer, block_size);
581 if (rc)
582 break;
583 }
584
585 if (!rc && length != 0)
586 rc = -EREMOTEIO;
587
588 kfree(buffer);
589
590 return rc;
591}
592
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200593static int mwl8k_load_firmware(struct ieee80211_hw *hw)
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100594{
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200595 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenhek22be40d2009-11-30 18:32:38 +0100596 struct firmware *fw = priv->fw_ucode;
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200597 int rc;
598 int loops;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100599
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200600 if (!memcmp(fw->data, "\x01\x00\x00\x00", 4)) {
Lennert Buytenhek22be40d2009-11-30 18:32:38 +0100601 struct firmware *helper = priv->fw_helper;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100602
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200603 if (helper == NULL) {
604 printk(KERN_ERR "%s: helper image needed but none "
605 "given\n", pci_name(priv->pdev));
606 return -EINVAL;
607 }
608
609 rc = mwl8k_load_fw_image(priv, helper->data, helper->size);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100610 if (rc) {
611 printk(KERN_ERR "%s: unable to load firmware "
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200612 "helper image\n", pci_name(priv->pdev));
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100613 return rc;
614 }
Lennert Buytenhek89b872e2009-11-30 18:13:20 +0100615 msleep(5);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100616
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200617 rc = mwl8k_feed_fw_image(priv, fw->data, fw->size);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100618 } else {
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200619 rc = mwl8k_load_fw_image(priv, fw->data, fw->size);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100620 }
621
622 if (rc) {
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +0200623 printk(KERN_ERR "%s: unable to load firmware image\n",
624 pci_name(priv->pdev));
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100625 return rc;
626 }
627
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100628 iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100629
Lennert Buytenhek89b872e2009-11-30 18:13:20 +0100630 loops = 500000;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100631 do {
Lennert Buytenhekeae74e62009-10-22 20:20:53 +0200632 u32 ready_code;
633
634 ready_code = ioread32(priv->regs + MWL8K_HIU_INT_CODE);
635 if (ready_code == MWL8K_FWAP_READY) {
636 priv->ap_fw = 1;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100637 break;
Lennert Buytenhekeae74e62009-10-22 20:20:53 +0200638 } else if (ready_code == MWL8K_FWSTA_READY) {
639 priv->ap_fw = 0;
640 break;
641 }
642
643 cond_resched();
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100644 udelay(1);
645 } while (--loops);
646
647 return loops ? 0 : -ETIMEDOUT;
648}
649
650
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100651/* DMA header used by firmware and hardware. */
652struct mwl8k_dma_data {
653 __le16 fwlen;
654 struct ieee80211_hdr wh;
Lennert Buytenhek20f09c32009-11-30 18:12:08 +0100655 char data[0];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000656} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100657
658/* Routines to add/remove DMA header from skb. */
Lennert Buytenhek20f09c32009-11-30 18:12:08 +0100659static inline void mwl8k_remove_dma_header(struct sk_buff *skb, __le16 qos)
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100660{
Lennert Buytenhek20f09c32009-11-30 18:12:08 +0100661 struct mwl8k_dma_data *tr;
662 int hdrlen;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100663
Lennert Buytenhek20f09c32009-11-30 18:12:08 +0100664 tr = (struct mwl8k_dma_data *)skb->data;
665 hdrlen = ieee80211_hdrlen(tr->wh.frame_control);
666
667 if (hdrlen != sizeof(tr->wh)) {
668 if (ieee80211_is_data_qos(tr->wh.frame_control)) {
669 memmove(tr->data - hdrlen, &tr->wh, hdrlen - 2);
670 *((__le16 *)(tr->data - 2)) = qos;
671 } else {
672 memmove(tr->data - hdrlen, &tr->wh, hdrlen);
673 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100674 }
Lennert Buytenhek20f09c32009-11-30 18:12:08 +0100675
676 if (hdrlen != sizeof(*tr))
677 skb_pull(skb, sizeof(*tr) - hdrlen);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100678}
679
Lennert Buytenhek76266b22009-07-16 11:07:09 +0200680static inline void mwl8k_add_dma_header(struct sk_buff *skb)
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100681{
682 struct ieee80211_hdr *wh;
Lennert Buytenhekca009302009-11-30 18:12:20 +0100683 int hdrlen;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100684 struct mwl8k_dma_data *tr;
685
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100686 /*
Lennert Buytenhekca009302009-11-30 18:12:20 +0100687 * Add a firmware DMA header; the firmware requires that we
688 * present a 2-byte payload length followed by a 4-address
689 * header (without QoS field), followed (optionally) by any
690 * WEP/ExtIV header (but only filled in for CCMP).
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100691 */
Lennert Buytenhekca009302009-11-30 18:12:20 +0100692 wh = (struct ieee80211_hdr *)skb->data;
693
694 hdrlen = ieee80211_hdrlen(wh->frame_control);
695 if (hdrlen != sizeof(*tr))
696 skb_push(skb, sizeof(*tr) - hdrlen);
697
698 if (ieee80211_is_data_qos(wh->frame_control))
699 hdrlen -= 2;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100700
701 tr = (struct mwl8k_dma_data *)skb->data;
702 if (wh != &tr->wh)
703 memmove(&tr->wh, wh, hdrlen);
Lennert Buytenhekca009302009-11-30 18:12:20 +0100704 if (hdrlen != sizeof(tr->wh))
705 memset(((void *)&tr->wh) + hdrlen, 0, sizeof(tr->wh) - hdrlen);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100706
707 /*
708 * Firmware length is the length of the fully formed "802.11
709 * payload". That is, everything except for the 802.11 header.
710 * This includes all crypto material including the MIC.
711 */
Lennert Buytenhekca009302009-11-30 18:12:20 +0100712 tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr));
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100713}
714
715
716/*
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100717 * Packet reception for 88w8366 AP firmware.
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200718 */
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100719struct mwl8k_rxd_8366_ap {
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200720 __le16 pkt_len;
721 __u8 sq2;
722 __u8 rate;
723 __le32 pkt_phys_addr;
724 __le32 next_rxd_phys_addr;
725 __le16 qos_control;
726 __le16 htsig2;
727 __le32 hw_rssi_info;
728 __le32 hw_noise_floor_info;
729 __u8 noise_floor;
730 __u8 pad0[3];
731 __u8 rssi;
732 __u8 rx_status;
733 __u8 channel;
734 __u8 rx_ctrl;
Eric Dumazetba2d3582010-06-02 18:10:09 +0000735} __packed;
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200736
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100737#define MWL8K_8366_AP_RATE_INFO_MCS_FORMAT 0x80
738#define MWL8K_8366_AP_RATE_INFO_40MHZ 0x40
739#define MWL8K_8366_AP_RATE_INFO_RATEID(x) ((x) & 0x3f)
Lennert Buytenhek8e9f33f2009-11-30 18:12:35 +0100740
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100741#define MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST 0x80
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200742
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100743static void mwl8k_rxd_8366_ap_init(void *_rxd, dma_addr_t next_dma_addr)
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200744{
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100745 struct mwl8k_rxd_8366_ap *rxd = _rxd;
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200746
747 rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr);
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100748 rxd->rx_ctrl = MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST;
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200749}
750
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100751static void mwl8k_rxd_8366_ap_refill(void *_rxd, dma_addr_t addr, int len)
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200752{
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100753 struct mwl8k_rxd_8366_ap *rxd = _rxd;
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200754
755 rxd->pkt_len = cpu_to_le16(len);
756 rxd->pkt_phys_addr = cpu_to_le32(addr);
757 wmb();
758 rxd->rx_ctrl = 0;
759}
760
761static int
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100762mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status,
John W. Linville0d462bb2010-07-28 14:04:24 -0400763 __le16 *qos, s8 *noise)
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200764{
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100765 struct mwl8k_rxd_8366_ap *rxd = _rxd;
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200766
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100767 if (!(rxd->rx_ctrl & MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST))
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200768 return -1;
769 rmb();
770
771 memset(status, 0, sizeof(*status));
772
773 status->signal = -rxd->rssi;
John W. Linville0d462bb2010-07-28 14:04:24 -0400774 *noise = -rxd->noise_floor;
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200775
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100776 if (rxd->rate & MWL8K_8366_AP_RATE_INFO_MCS_FORMAT) {
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200777 status->flag |= RX_FLAG_HT;
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100778 if (rxd->rate & MWL8K_8366_AP_RATE_INFO_40MHZ)
Lennert Buytenhek8e9f33f2009-11-30 18:12:35 +0100779 status->flag |= RX_FLAG_40MHZ;
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100780 status->rate_idx = MWL8K_8366_AP_RATE_INFO_RATEID(rxd->rate);
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200781 } else {
782 int i;
783
Lennert Buytenhek777ad372010-01-12 13:48:04 +0100784 for (i = 0; i < ARRAY_SIZE(mwl8k_rates_24); i++) {
785 if (mwl8k_rates_24[i].hw_value == rxd->rate) {
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200786 status->rate_idx = i;
787 break;
788 }
789 }
790 }
791
Lennert Buytenhek85478342010-01-12 13:48:56 +0100792 if (rxd->channel > 14) {
793 status->band = IEEE80211_BAND_5GHZ;
794 if (!(status->flag & RX_FLAG_HT))
795 status->rate_idx -= 5;
796 } else {
797 status->band = IEEE80211_BAND_2GHZ;
798 }
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200799 status->freq = ieee80211_channel_to_frequency(rxd->channel);
800
Lennert Buytenhek20f09c32009-11-30 18:12:08 +0100801 *qos = rxd->qos_control;
802
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200803 return le16_to_cpu(rxd->pkt_len);
804}
805
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100806static struct rxd_ops rxd_8366_ap_ops = {
807 .rxd_size = sizeof(struct mwl8k_rxd_8366_ap),
808 .rxd_init = mwl8k_rxd_8366_ap_init,
809 .rxd_refill = mwl8k_rxd_8366_ap_refill,
810 .rxd_process = mwl8k_rxd_8366_ap_process,
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +0200811};
812
813/*
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100814 * Packet reception for STA firmware.
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100815 */
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100816struct mwl8k_rxd_sta {
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100817 __le16 pkt_len;
818 __u8 link_quality;
819 __u8 noise_level;
820 __le32 pkt_phys_addr;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200821 __le32 next_rxd_phys_addr;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100822 __le16 qos_control;
823 __le16 rate_info;
824 __le32 pad0[4];
825 __u8 rssi;
826 __u8 channel;
827 __le16 pad1;
828 __u8 rx_ctrl;
829 __u8 rx_status;
830 __u8 pad2[2];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000831} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100832
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100833#define MWL8K_STA_RATE_INFO_SHORTPRE 0x8000
834#define MWL8K_STA_RATE_INFO_ANTSELECT(x) (((x) >> 11) & 0x3)
835#define MWL8K_STA_RATE_INFO_RATEID(x) (((x) >> 3) & 0x3f)
836#define MWL8K_STA_RATE_INFO_40MHZ 0x0004
837#define MWL8K_STA_RATE_INFO_SHORTGI 0x0002
838#define MWL8K_STA_RATE_INFO_MCS_FORMAT 0x0001
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200839
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100840#define MWL8K_STA_RX_CTRL_OWNED_BY_HOST 0x02
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200841
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100842static void mwl8k_rxd_sta_init(void *_rxd, dma_addr_t next_dma_addr)
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200843{
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100844 struct mwl8k_rxd_sta *rxd = _rxd;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200845
846 rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr);
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100847 rxd->rx_ctrl = MWL8K_STA_RX_CTRL_OWNED_BY_HOST;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200848}
849
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100850static void mwl8k_rxd_sta_refill(void *_rxd, dma_addr_t addr, int len)
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200851{
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100852 struct mwl8k_rxd_sta *rxd = _rxd;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200853
854 rxd->pkt_len = cpu_to_le16(len);
855 rxd->pkt_phys_addr = cpu_to_le32(addr);
856 wmb();
857 rxd->rx_ctrl = 0;
858}
859
860static int
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100861mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status,
John W. Linville0d462bb2010-07-28 14:04:24 -0400862 __le16 *qos, s8 *noise)
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200863{
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100864 struct mwl8k_rxd_sta *rxd = _rxd;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200865 u16 rate_info;
866
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100867 if (!(rxd->rx_ctrl & MWL8K_STA_RX_CTRL_OWNED_BY_HOST))
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200868 return -1;
869 rmb();
870
871 rate_info = le16_to_cpu(rxd->rate_info);
872
873 memset(status, 0, sizeof(*status));
874
875 status->signal = -rxd->rssi;
John W. Linville0d462bb2010-07-28 14:04:24 -0400876 *noise = -rxd->noise_level;
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100877 status->antenna = MWL8K_STA_RATE_INFO_ANTSELECT(rate_info);
878 status->rate_idx = MWL8K_STA_RATE_INFO_RATEID(rate_info);
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200879
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100880 if (rate_info & MWL8K_STA_RATE_INFO_SHORTPRE)
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200881 status->flag |= RX_FLAG_SHORTPRE;
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100882 if (rate_info & MWL8K_STA_RATE_INFO_40MHZ)
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200883 status->flag |= RX_FLAG_40MHZ;
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100884 if (rate_info & MWL8K_STA_RATE_INFO_SHORTGI)
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200885 status->flag |= RX_FLAG_SHORT_GI;
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100886 if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT)
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200887 status->flag |= RX_FLAG_HT;
888
Lennert Buytenhek85478342010-01-12 13:48:56 +0100889 if (rxd->channel > 14) {
890 status->band = IEEE80211_BAND_5GHZ;
891 if (!(status->flag & RX_FLAG_HT))
892 status->rate_idx -= 5;
893 } else {
894 status->band = IEEE80211_BAND_2GHZ;
895 }
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200896 status->freq = ieee80211_channel_to_frequency(rxd->channel);
897
Lennert Buytenhek20f09c32009-11-30 18:12:08 +0100898 *qos = rxd->qos_control;
899
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200900 return le16_to_cpu(rxd->pkt_len);
901}
902
Lennert Buytenhek89a91f42009-11-30 18:32:54 +0100903static struct rxd_ops rxd_sta_ops = {
904 .rxd_size = sizeof(struct mwl8k_rxd_sta),
905 .rxd_init = mwl8k_rxd_sta_init,
906 .rxd_refill = mwl8k_rxd_sta_refill,
907 .rxd_process = mwl8k_rxd_sta_process,
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200908};
909
910
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100911#define MWL8K_RX_DESCS 256
912#define MWL8K_RX_MAXSZ 3800
913
914static int mwl8k_rxq_init(struct ieee80211_hw *hw, int index)
915{
916 struct mwl8k_priv *priv = hw->priv;
917 struct mwl8k_rx_queue *rxq = priv->rxq + index;
918 int size;
919 int i;
920
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200921 rxq->rxd_count = 0;
922 rxq->head = 0;
923 rxq->tail = 0;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100924
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200925 size = MWL8K_RX_DESCS * priv->rxd_ops->rxd_size;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100926
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200927 rxq->rxd = pci_alloc_consistent(priv->pdev, size, &rxq->rxd_dma);
928 if (rxq->rxd == NULL) {
Joe Perches5db55842010-08-11 19:11:19 -0700929 wiphy_err(hw->wiphy, "failed to alloc RX descriptors\n");
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100930 return -ENOMEM;
931 }
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200932 memset(rxq->rxd, 0, size);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100933
Lennert Buytenhek788838e2009-10-22 20:20:56 +0200934 rxq->buf = kmalloc(MWL8K_RX_DESCS * sizeof(*rxq->buf), GFP_KERNEL);
935 if (rxq->buf == NULL) {
Joe Perches5db55842010-08-11 19:11:19 -0700936 wiphy_err(hw->wiphy, "failed to alloc RX skbuff list\n");
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200937 pci_free_consistent(priv->pdev, size, rxq->rxd, rxq->rxd_dma);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100938 return -ENOMEM;
939 }
Lennert Buytenhek788838e2009-10-22 20:20:56 +0200940 memset(rxq->buf, 0, MWL8K_RX_DESCS * sizeof(*rxq->buf));
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100941
942 for (i = 0; i < MWL8K_RX_DESCS; i++) {
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200943 int desc_size;
944 void *rxd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100945 int nexti;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200946 dma_addr_t next_dma_addr;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100947
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200948 desc_size = priv->rxd_ops->rxd_size;
949 rxd = rxq->rxd + (i * priv->rxd_ops->rxd_size);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100950
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200951 nexti = i + 1;
952 if (nexti == MWL8K_RX_DESCS)
953 nexti = 0;
954 next_dma_addr = rxq->rxd_dma + (nexti * desc_size);
955
956 priv->rxd_ops->rxd_init(rxd, next_dma_addr);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100957 }
958
959 return 0;
960}
961
962static int rxq_refill(struct ieee80211_hw *hw, int index, int limit)
963{
964 struct mwl8k_priv *priv = hw->priv;
965 struct mwl8k_rx_queue *rxq = priv->rxq + index;
966 int refilled;
967
968 refilled = 0;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +0200969 while (rxq->rxd_count < MWL8K_RX_DESCS && limit--) {
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100970 struct sk_buff *skb;
Lennert Buytenhek788838e2009-10-22 20:20:56 +0200971 dma_addr_t addr;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100972 int rx;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200973 void *rxd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100974
975 skb = dev_alloc_skb(MWL8K_RX_MAXSZ);
976 if (skb == NULL)
977 break;
978
Lennert Buytenhek788838e2009-10-22 20:20:56 +0200979 addr = pci_map_single(priv->pdev, skb->data,
980 MWL8K_RX_MAXSZ, DMA_FROM_DEVICE);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100981
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200982 rxq->rxd_count++;
983 rx = rxq->tail++;
984 if (rxq->tail == MWL8K_RX_DESCS)
985 rxq->tail = 0;
Lennert Buytenhek788838e2009-10-22 20:20:56 +0200986 rxq->buf[rx].skb = skb;
FUJITA Tomonori53b1b3e2010-04-02 13:10:38 +0900987 dma_unmap_addr_set(&rxq->buf[rx], dma, addr);
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +0200988
989 rxd = rxq->rxd + (rx * priv->rxd_ops->rxd_size);
990 priv->rxd_ops->rxd_refill(rxd, addr, MWL8K_RX_MAXSZ);
Lennert Buytenheka66098d2009-03-10 10:13:33 +0100991
992 refilled++;
993 }
994
995 return refilled;
996}
997
998/* Must be called only when the card's reception is completely halted */
999static void mwl8k_rxq_deinit(struct ieee80211_hw *hw, int index)
1000{
1001 struct mwl8k_priv *priv = hw->priv;
1002 struct mwl8k_rx_queue *rxq = priv->rxq + index;
1003 int i;
1004
1005 for (i = 0; i < MWL8K_RX_DESCS; i++) {
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001006 if (rxq->buf[i].skb != NULL) {
1007 pci_unmap_single(priv->pdev,
FUJITA Tomonori53b1b3e2010-04-02 13:10:38 +09001008 dma_unmap_addr(&rxq->buf[i], dma),
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001009 MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE);
FUJITA Tomonori53b1b3e2010-04-02 13:10:38 +09001010 dma_unmap_addr_set(&rxq->buf[i], dma, 0);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001011
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001012 kfree_skb(rxq->buf[i].skb);
1013 rxq->buf[i].skb = NULL;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001014 }
1015 }
1016
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001017 kfree(rxq->buf);
1018 rxq->buf = NULL;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001019
1020 pci_free_consistent(priv->pdev,
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001021 MWL8K_RX_DESCS * priv->rxd_ops->rxd_size,
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001022 rxq->rxd, rxq->rxd_dma);
1023 rxq->rxd = NULL;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001024}
1025
1026
1027/*
1028 * Scan a list of BSSIDs to process for finalize join.
1029 * Allows for extension to process multiple BSSIDs.
1030 */
1031static inline int
1032mwl8k_capture_bssid(struct mwl8k_priv *priv, struct ieee80211_hdr *wh)
1033{
1034 return priv->capture_beacon &&
1035 ieee80211_is_beacon(wh->frame_control) &&
1036 !compare_ether_addr(wh->addr3, priv->capture_bssid);
1037}
1038
Lennert Buytenhek37797522009-10-22 20:19:45 +02001039static inline void mwl8k_save_beacon(struct ieee80211_hw *hw,
1040 struct sk_buff *skb)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001041{
Lennert Buytenhek37797522009-10-22 20:19:45 +02001042 struct mwl8k_priv *priv = hw->priv;
1043
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001044 priv->capture_beacon = false;
Lennert Buytenhekd89173f2009-07-16 09:54:27 +02001045 memset(priv->capture_bssid, 0, ETH_ALEN);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001046
1047 /*
1048 * Use GFP_ATOMIC as rxq_process is called from
1049 * the primary interrupt handler, memory allocation call
1050 * must not sleep.
1051 */
1052 priv->beacon_skb = skb_copy(skb, GFP_ATOMIC);
1053 if (priv->beacon_skb != NULL)
Lennert Buytenhek37797522009-10-22 20:19:45 +02001054 ieee80211_queue_work(hw, &priv->finalize_join_worker);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001055}
1056
1057static int rxq_process(struct ieee80211_hw *hw, int index, int limit)
1058{
1059 struct mwl8k_priv *priv = hw->priv;
1060 struct mwl8k_rx_queue *rxq = priv->rxq + index;
1061 int processed;
1062
1063 processed = 0;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001064 while (rxq->rxd_count && limit--) {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001065 struct sk_buff *skb;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001066 void *rxd;
1067 int pkt_len;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001068 struct ieee80211_rx_status status;
Lennert Buytenhek20f09c32009-11-30 18:12:08 +01001069 __le16 qos;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001070
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001071 skb = rxq->buf[rxq->head].skb;
Lennert Buytenhekd25f9f12009-08-03 21:58:26 +02001072 if (skb == NULL)
1073 break;
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001074
1075 rxd = rxq->rxd + (rxq->head * priv->rxd_ops->rxd_size);
1076
John W. Linville0d462bb2010-07-28 14:04:24 -04001077 pkt_len = priv->rxd_ops->rxd_process(rxd, &status, &qos,
1078 &priv->noise);
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001079 if (pkt_len < 0)
1080 break;
1081
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001082 rxq->buf[rxq->head].skb = NULL;
1083
1084 pci_unmap_single(priv->pdev,
FUJITA Tomonori53b1b3e2010-04-02 13:10:38 +09001085 dma_unmap_addr(&rxq->buf[rxq->head], dma),
Lennert Buytenhek788838e2009-10-22 20:20:56 +02001086 MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE);
FUJITA Tomonori53b1b3e2010-04-02 13:10:38 +09001087 dma_unmap_addr_set(&rxq->buf[rxq->head], dma, 0);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001088
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001089 rxq->head++;
1090 if (rxq->head == MWL8K_RX_DESCS)
1091 rxq->head = 0;
1092
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001093 rxq->rxd_count--;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001094
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001095 skb_put(skb, pkt_len);
Lennert Buytenhek20f09c32009-11-30 18:12:08 +01001096 mwl8k_remove_dma_header(skb, qos);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001097
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001098 /*
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +02001099 * Check for a pending join operation. Save a
1100 * copy of the beacon and schedule a tasklet to
1101 * send a FINALIZE_JOIN command to the firmware.
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001102 */
Lennert Buytenhek54bc3a02009-10-22 20:20:59 +02001103 if (mwl8k_capture_bssid(priv, (void *)skb->data))
Lennert Buytenhek37797522009-10-22 20:19:45 +02001104 mwl8k_save_beacon(hw, skb);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001105
Johannes Bergf1d58c22009-06-17 13:13:00 +02001106 memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
1107 ieee80211_rx_irqsafe(hw, skb);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001108
1109 processed++;
1110 }
1111
1112 return processed;
1113}
1114
1115
1116/*
1117 * Packet transmission.
1118 */
1119
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001120#define MWL8K_TXD_STATUS_OK 0x00000001
1121#define MWL8K_TXD_STATUS_OK_RETRY 0x00000002
1122#define MWL8K_TXD_STATUS_OK_MORE_RETRY 0x00000004
1123#define MWL8K_TXD_STATUS_MULTICAST_TX 0x00000008
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001124#define MWL8K_TXD_STATUS_FW_OWNED 0x80000000
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001125
Lennert Buytenheke0493a82009-11-30 18:32:00 +01001126#define MWL8K_QOS_QLEN_UNSPEC 0xff00
1127#define MWL8K_QOS_ACK_POLICY_MASK 0x0060
1128#define MWL8K_QOS_ACK_POLICY_NORMAL 0x0000
1129#define MWL8K_QOS_ACK_POLICY_BLOCKACK 0x0060
1130#define MWL8K_QOS_EOSP 0x0010
1131
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001132struct mwl8k_tx_desc {
1133 __le32 status;
1134 __u8 data_rate;
1135 __u8 tx_priority;
1136 __le16 qos_control;
1137 __le32 pkt_phys_addr;
1138 __le16 pkt_len;
Lennert Buytenhekd89173f2009-07-16 09:54:27 +02001139 __u8 dest_MAC_addr[ETH_ALEN];
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001140 __le32 next_txd_phys_addr;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001141 __le32 reserved;
1142 __le16 rate_info;
1143 __u8 peer_id;
Brian Cavagnoloa1fe24b2010-11-12 17:23:47 -08001144 __u8 tx_frag_cnt;
Eric Dumazetba2d3582010-06-02 18:10:09 +00001145} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001146
1147#define MWL8K_TX_DESCS 128
1148
1149static int mwl8k_txq_init(struct ieee80211_hw *hw, int index)
1150{
1151 struct mwl8k_priv *priv = hw->priv;
1152 struct mwl8k_tx_queue *txq = priv->txq + index;
1153 int size;
1154 int i;
1155
Kalle Valo8ccbc3b2010-02-07 10:20:52 +02001156 txq->len = 0;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001157 txq->head = 0;
1158 txq->tail = 0;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001159
1160 size = MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc);
1161
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001162 txq->txd = pci_alloc_consistent(priv->pdev, size, &txq->txd_dma);
1163 if (txq->txd == NULL) {
Joe Perches5db55842010-08-11 19:11:19 -07001164 wiphy_err(hw->wiphy, "failed to alloc TX descriptors\n");
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001165 return -ENOMEM;
1166 }
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001167 memset(txq->txd, 0, size);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001168
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001169 txq->skb = kmalloc(MWL8K_TX_DESCS * sizeof(*txq->skb), GFP_KERNEL);
1170 if (txq->skb == NULL) {
Joe Perches5db55842010-08-11 19:11:19 -07001171 wiphy_err(hw->wiphy, "failed to alloc TX skbuff list\n");
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001172 pci_free_consistent(priv->pdev, size, txq->txd, txq->txd_dma);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001173 return -ENOMEM;
1174 }
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001175 memset(txq->skb, 0, MWL8K_TX_DESCS * sizeof(*txq->skb));
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001176
1177 for (i = 0; i < MWL8K_TX_DESCS; i++) {
1178 struct mwl8k_tx_desc *tx_desc;
1179 int nexti;
1180
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001181 tx_desc = txq->txd + i;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001182 nexti = (i + 1) % MWL8K_TX_DESCS;
1183
1184 tx_desc->status = 0;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001185 tx_desc->next_txd_phys_addr =
1186 cpu_to_le32(txq->txd_dma + nexti * sizeof(*tx_desc));
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001187 }
1188
1189 return 0;
1190}
1191
1192static inline void mwl8k_tx_start(struct mwl8k_priv *priv)
1193{
1194 iowrite32(MWL8K_H2A_INT_PPA_READY,
1195 priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
1196 iowrite32(MWL8K_H2A_INT_DUMMY,
1197 priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
1198 ioread32(priv->regs + MWL8K_HIU_INT_CODE);
1199}
1200
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001201static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001202{
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001203 struct mwl8k_priv *priv = hw->priv;
1204 int i;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001205
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001206 for (i = 0; i < MWL8K_TX_QUEUES; i++) {
1207 struct mwl8k_tx_queue *txq = priv->txq + i;
1208 int fw_owned = 0;
1209 int drv_owned = 0;
1210 int unused = 0;
1211 int desc;
Lennert Buytenhekc3f967d2009-08-18 04:15:22 +02001212
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001213 for (desc = 0; desc < MWL8K_TX_DESCS; desc++) {
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001214 struct mwl8k_tx_desc *tx_desc = txq->txd + desc;
1215 u32 status;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001216
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001217 status = le32_to_cpu(tx_desc->status);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001218 if (status & MWL8K_TXD_STATUS_FW_OWNED)
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001219 fw_owned++;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001220 else
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001221 drv_owned++;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001222
1223 if (tx_desc->pkt_len == 0)
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001224 unused++;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001225 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001226
Joe Perchesc96c31e2010-07-26 14:39:58 -07001227 wiphy_err(hw->wiphy,
1228 "txq[%d] len=%d head=%d tail=%d "
1229 "fw_owned=%d drv_owned=%d unused=%d\n",
1230 i,
1231 txq->len, txq->head, txq->tail,
1232 fw_owned, drv_owned, unused);
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001233 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001234}
1235
Lennert Buytenhek618952a2009-08-18 03:18:01 +02001236/*
Lennert Buytenhek88de754a2009-10-22 20:19:37 +02001237 * Must be called with priv->fw_mutex held and tx queues stopped.
Lennert Buytenhek618952a2009-08-18 03:18:01 +02001238 */
Lennert Buytenhek62abd3c2010-01-08 18:28:34 +01001239#define MWL8K_TX_WAIT_TIMEOUT_MS 5000
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001240
Lennert Buytenhek950d5b02009-07-17 01:48:41 +02001241static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001242{
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001243 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenhek88de754a2009-10-22 20:19:37 +02001244 DECLARE_COMPLETION_ONSTACK(tx_wait);
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001245 int retry;
1246 int rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001247
1248 might_sleep();
1249
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001250 /*
1251 * The TX queues are stopped at this point, so this test
1252 * doesn't need to take ->tx_lock.
1253 */
1254 if (!priv->pending_tx_pkts)
1255 return 0;
1256
1257 retry = 0;
1258 rc = 0;
1259
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001260 spin_lock_bh(&priv->tx_lock);
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001261 priv->tx_wait = &tx_wait;
1262 while (!rc) {
1263 int oldcount;
1264 unsigned long timeout;
1265
1266 oldcount = priv->pending_tx_pkts;
1267
1268 spin_unlock_bh(&priv->tx_lock);
1269 timeout = wait_for_completion_timeout(&tx_wait,
1270 msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS));
1271 spin_lock_bh(&priv->tx_lock);
1272
1273 if (timeout) {
1274 WARN_ON(priv->pending_tx_pkts);
1275 if (retry) {
Joe Perchesc96c31e2010-07-26 14:39:58 -07001276 wiphy_notice(hw->wiphy, "tx rings drained\n");
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001277 }
1278 break;
1279 }
1280
1281 if (priv->pending_tx_pkts < oldcount) {
Joe Perchesc96c31e2010-07-26 14:39:58 -07001282 wiphy_notice(hw->wiphy,
1283 "waiting for tx rings to drain (%d -> %d pkts)\n",
1284 oldcount, priv->pending_tx_pkts);
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001285 retry = 1;
1286 continue;
1287 }
1288
1289 priv->tx_wait = NULL;
1290
Joe Perchesc96c31e2010-07-26 14:39:58 -07001291 wiphy_err(hw->wiphy, "tx rings stuck for %d ms\n",
1292 MWL8K_TX_WAIT_TIMEOUT_MS);
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001293 mwl8k_dump_tx_rings(hw);
1294
1295 rc = -ETIMEDOUT;
1296 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001297 spin_unlock_bh(&priv->tx_lock);
1298
Lennert Buytenhek7e1112d2009-11-30 18:13:04 +01001299 return rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001300}
1301
Lennert Buytenhekc23b5a62009-07-16 13:49:55 +02001302#define MWL8K_TXD_SUCCESS(status) \
1303 ((status) & (MWL8K_TXD_STATUS_OK | \
1304 MWL8K_TXD_STATUS_OK_RETRY | \
1305 MWL8K_TXD_STATUS_OK_MORE_RETRY))
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001306
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01001307static int
1308mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001309{
1310 struct mwl8k_priv *priv = hw->priv;
1311 struct mwl8k_tx_queue *txq = priv->txq + index;
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01001312 int processed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001313
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01001314 processed = 0;
Kalle Valo8ccbc3b2010-02-07 10:20:52 +02001315 while (txq->len > 0 && limit--) {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001316 int tx;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001317 struct mwl8k_tx_desc *tx_desc;
1318 unsigned long addr;
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02001319 int size;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001320 struct sk_buff *skb;
1321 struct ieee80211_tx_info *info;
1322 u32 status;
1323
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001324 tx = txq->head;
1325 tx_desc = txq->txd + tx;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001326
1327 status = le32_to_cpu(tx_desc->status);
1328
1329 if (status & MWL8K_TXD_STATUS_FW_OWNED) {
1330 if (!force)
1331 break;
1332 tx_desc->status &=
1333 ~cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED);
1334 }
1335
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001336 txq->head = (tx + 1) % MWL8K_TX_DESCS;
Kalle Valo8ccbc3b2010-02-07 10:20:52 +02001337 BUG_ON(txq->len == 0);
1338 txq->len--;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001339 priv->pending_tx_pkts--;
1340
1341 addr = le32_to_cpu(tx_desc->pkt_phys_addr);
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02001342 size = le16_to_cpu(tx_desc->pkt_len);
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001343 skb = txq->skb[tx];
1344 txq->skb[tx] = NULL;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001345
1346 BUG_ON(skb == NULL);
1347 pci_unmap_single(priv->pdev, addr, size, PCI_DMA_TODEVICE);
1348
Lennert Buytenhek20f09c32009-11-30 18:12:08 +01001349 mwl8k_remove_dma_header(skb, tx_desc->qos_control);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001350
1351 /* Mark descriptor as unused */
1352 tx_desc->pkt_phys_addr = 0;
1353 tx_desc->pkt_len = 0;
1354
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001355 info = IEEE80211_SKB_CB(skb);
1356 ieee80211_tx_info_clear_status(info);
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02001357 if (MWL8K_TXD_SUCCESS(status))
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001358 info->flags |= IEEE80211_TX_STAT_ACK;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001359
1360 ieee80211_tx_status_irqsafe(hw, skb);
1361
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01001362 processed++;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001363 }
1364
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01001365 if (processed && priv->radio_on && !mutex_is_locked(&priv->fw_mutex))
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001366 ieee80211_wake_queue(hw, index);
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01001367
1368 return processed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001369}
1370
1371/* must be called only when the card's transmit is completely halted */
1372static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index)
1373{
1374 struct mwl8k_priv *priv = hw->priv;
1375 struct mwl8k_tx_queue *txq = priv->txq + index;
1376
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01001377 mwl8k_txq_reclaim(hw, index, INT_MAX, 1);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001378
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001379 kfree(txq->skb);
1380 txq->skb = NULL;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001381
1382 pci_free_consistent(priv->pdev,
1383 MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc),
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001384 txq->txd, txq->txd_dma);
1385 txq->txd = NULL;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001386}
1387
1388static int
1389mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
1390{
1391 struct mwl8k_priv *priv = hw->priv;
1392 struct ieee80211_tx_info *tx_info;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001393 struct mwl8k_vif *mwl8k_vif;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001394 struct ieee80211_hdr *wh;
1395 struct mwl8k_tx_queue *txq;
1396 struct mwl8k_tx_desc *tx;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001397 dma_addr_t dma;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001398 u32 txstatus;
1399 u8 txdatarate;
1400 u16 qos;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001401
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001402 wh = (struct ieee80211_hdr *)skb->data;
1403 if (ieee80211_is_data_qos(wh->frame_control))
1404 qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh)));
1405 else
1406 qos = 0;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001407
Lennert Buytenhek76266b22009-07-16 11:07:09 +02001408 mwl8k_add_dma_header(skb);
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001409 wh = &((struct mwl8k_dma_data *)skb->data)->wh;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001410
1411 tx_info = IEEE80211_SKB_CB(skb);
1412 mwl8k_vif = MWL8K_VIF(tx_info->control.vif);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001413
1414 if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001415 wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
Lennert Buytenhek657232b2010-01-12 13:47:47 +01001416 wh->seq_ctrl |= cpu_to_le16(mwl8k_vif->seqno);
1417 mwl8k_vif->seqno += 0x10;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001418 }
1419
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001420 /* Setup firmware control bit fields for each frame type. */
1421 txstatus = 0;
1422 txdatarate = 0;
1423 if (ieee80211_is_mgmt(wh->frame_control) ||
1424 ieee80211_is_ctl(wh->frame_control)) {
1425 txdatarate = 0;
Lennert Buytenheke0493a82009-11-30 18:32:00 +01001426 qos |= MWL8K_QOS_QLEN_UNSPEC | MWL8K_QOS_EOSP;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001427 } else if (ieee80211_is_data(wh->frame_control)) {
1428 txdatarate = 1;
1429 if (is_multicast_ether_addr(wh->addr1))
1430 txstatus |= MWL8K_TXD_STATUS_MULTICAST_TX;
1431
Lennert Buytenheke0493a82009-11-30 18:32:00 +01001432 qos &= ~MWL8K_QOS_ACK_POLICY_MASK;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001433 if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
Lennert Buytenheke0493a82009-11-30 18:32:00 +01001434 qos |= MWL8K_QOS_ACK_POLICY_BLOCKACK;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001435 else
Lennert Buytenheke0493a82009-11-30 18:32:00 +01001436 qos |= MWL8K_QOS_ACK_POLICY_NORMAL;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001437 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001438
1439 dma = pci_map_single(priv->pdev, skb->data,
1440 skb->len, PCI_DMA_TODEVICE);
1441
1442 if (pci_dma_mapping_error(priv->pdev, dma)) {
Joe Perchesc96c31e2010-07-26 14:39:58 -07001443 wiphy_debug(hw->wiphy,
1444 "failed to dma map skb, dropping TX frame.\n");
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001445 dev_kfree_skb(skb);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001446 return NETDEV_TX_OK;
1447 }
1448
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001449 spin_lock_bh(&priv->tx_lock);
1450
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001451 txq = priv->txq + index;
1452
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001453 BUG_ON(txq->skb[txq->tail] != NULL);
1454 txq->skb[txq->tail] = skb;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001455
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001456 tx = txq->txd + txq->tail;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001457 tx->data_rate = txdatarate;
1458 tx->tx_priority = index;
1459 tx->qos_control = cpu_to_le16(qos);
1460 tx->pkt_phys_addr = cpu_to_le32(dma);
1461 tx->pkt_len = cpu_to_le16(skb->len);
1462 tx->rate_info = 0;
Lennert Buytenheka6804002010-01-04 21:55:42 +01001463 if (!priv->ap_fw && tx_info->control.sta != NULL)
1464 tx->peer_id = MWL8K_STA(tx_info->control.sta)->peer_id;
1465 else
1466 tx->peer_id = 0;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001467 wmb();
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001468 tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus);
1469
Kalle Valo8ccbc3b2010-02-07 10:20:52 +02001470 txq->len++;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001471 priv->pending_tx_pkts++;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001472
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001473 txq->tail++;
1474 if (txq->tail == MWL8K_TX_DESCS)
1475 txq->tail = 0;
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001476
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001477 if (txq->head == txq->tail)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001478 ieee80211_stop_queue(hw, index);
1479
Lennert Buytenhek23b33902009-07-17 05:21:04 +02001480 mwl8k_tx_start(priv);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001481
1482 spin_unlock_bh(&priv->tx_lock);
1483
1484 return NETDEV_TX_OK;
1485}
1486
1487
1488/*
Lennert Buytenhek618952a2009-08-18 03:18:01 +02001489 * Firmware access.
1490 *
1491 * We have the following requirements for issuing firmware commands:
1492 * - Some commands require that the packet transmit path is idle when
1493 * the command is issued. (For simplicity, we'll just quiesce the
1494 * transmit path for every command.)
1495 * - There are certain sequences of commands that need to be issued to
1496 * the hardware sequentially, with no other intervening commands.
1497 *
1498 * This leads to an implementation of a "firmware lock" as a mutex that
1499 * can be taken recursively, and which is taken by both the low-level
1500 * command submission function (mwl8k_post_cmd) as well as any users of
1501 * that function that require issuing of an atomic sequence of commands,
1502 * and quiesces the transmit path whenever it's taken.
1503 */
1504static int mwl8k_fw_lock(struct ieee80211_hw *hw)
1505{
1506 struct mwl8k_priv *priv = hw->priv;
1507
1508 if (priv->fw_mutex_owner != current) {
1509 int rc;
1510
1511 mutex_lock(&priv->fw_mutex);
1512 ieee80211_stop_queues(hw);
1513
1514 rc = mwl8k_tx_wait_empty(hw);
1515 if (rc) {
1516 ieee80211_wake_queues(hw);
1517 mutex_unlock(&priv->fw_mutex);
1518
1519 return rc;
1520 }
1521
1522 priv->fw_mutex_owner = current;
1523 }
1524
1525 priv->fw_mutex_depth++;
1526
1527 return 0;
1528}
1529
1530static void mwl8k_fw_unlock(struct ieee80211_hw *hw)
1531{
1532 struct mwl8k_priv *priv = hw->priv;
1533
1534 if (!--priv->fw_mutex_depth) {
1535 ieee80211_wake_queues(hw);
1536 priv->fw_mutex_owner = NULL;
1537 mutex_unlock(&priv->fw_mutex);
1538 }
1539}
1540
1541
1542/*
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001543 * Command processing.
1544 */
1545
Lennert Buytenhek0c9cc642009-11-30 18:12:49 +01001546/* Timeout firmware commands after 10s */
1547#define MWL8K_CMD_TIMEOUT_MS 10000
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001548
1549static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
1550{
1551 DECLARE_COMPLETION_ONSTACK(cmd_wait);
1552 struct mwl8k_priv *priv = hw->priv;
1553 void __iomem *regs = priv->regs;
1554 dma_addr_t dma_addr;
1555 unsigned int dma_size;
1556 int rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001557 unsigned long timeout = 0;
1558 u8 buf[32];
1559
John W. Linvilleb6037422010-07-20 13:55:00 -04001560 cmd->result = (__force __le16) 0xffff;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001561 dma_size = le16_to_cpu(cmd->length);
1562 dma_addr = pci_map_single(priv->pdev, cmd, dma_size,
1563 PCI_DMA_BIDIRECTIONAL);
1564 if (pci_dma_mapping_error(priv->pdev, dma_addr))
1565 return -ENOMEM;
1566
Lennert Buytenhek618952a2009-08-18 03:18:01 +02001567 rc = mwl8k_fw_lock(hw);
Lennert Buytenhek39a1e422009-08-24 15:42:46 +02001568 if (rc) {
1569 pci_unmap_single(priv->pdev, dma_addr, dma_size,
1570 PCI_DMA_BIDIRECTIONAL);
Lennert Buytenhek618952a2009-08-18 03:18:01 +02001571 return rc;
Lennert Buytenhek39a1e422009-08-24 15:42:46 +02001572 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001573
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001574 priv->hostcmd_wait = &cmd_wait;
1575 iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR);
1576 iowrite32(MWL8K_H2A_INT_DOORBELL,
1577 regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
1578 iowrite32(MWL8K_H2A_INT_DUMMY,
1579 regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001580
1581 timeout = wait_for_completion_timeout(&cmd_wait,
1582 msecs_to_jiffies(MWL8K_CMD_TIMEOUT_MS));
1583
Lennert Buytenhek618952a2009-08-18 03:18:01 +02001584 priv->hostcmd_wait = NULL;
1585
1586 mwl8k_fw_unlock(hw);
1587
Lennert Buytenhek37055bd2009-08-03 21:58:47 +02001588 pci_unmap_single(priv->pdev, dma_addr, dma_size,
1589 PCI_DMA_BIDIRECTIONAL);
1590
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001591 if (!timeout) {
Joe Perches5db55842010-08-11 19:11:19 -07001592 wiphy_err(hw->wiphy, "Command %s timeout after %u ms\n",
Joe Perchesc96c31e2010-07-26 14:39:58 -07001593 mwl8k_cmd_name(cmd->code, buf, sizeof(buf)),
1594 MWL8K_CMD_TIMEOUT_MS);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001595 rc = -ETIMEDOUT;
1596 } else {
Lennert Buytenhek0c9cc642009-11-30 18:12:49 +01001597 int ms;
1598
1599 ms = MWL8K_CMD_TIMEOUT_MS - jiffies_to_msecs(timeout);
1600
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02001601 rc = cmd->result ? -EINVAL : 0;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001602 if (rc)
Joe Perches5db55842010-08-11 19:11:19 -07001603 wiphy_err(hw->wiphy, "Command %s error 0x%x\n",
Joe Perchesc96c31e2010-07-26 14:39:58 -07001604 mwl8k_cmd_name(cmd->code, buf, sizeof(buf)),
1605 le16_to_cpu(cmd->result));
Lennert Buytenhek0c9cc642009-11-30 18:12:49 +01001606 else if (ms > 2000)
Joe Perches5db55842010-08-11 19:11:19 -07001607 wiphy_notice(hw->wiphy, "Command %s took %d ms\n",
Joe Perchesc96c31e2010-07-26 14:39:58 -07001608 mwl8k_cmd_name(cmd->code,
1609 buf, sizeof(buf)),
1610 ms);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001611 }
1612
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001613 return rc;
1614}
1615
Lennert Buytenhekf57ca9c2010-01-12 13:50:14 +01001616static int mwl8k_post_pervif_cmd(struct ieee80211_hw *hw,
1617 struct ieee80211_vif *vif,
1618 struct mwl8k_cmd_pkt *cmd)
1619{
1620 if (vif != NULL)
1621 cmd->macid = MWL8K_VIF(vif)->macid;
1622 return mwl8k_post_cmd(hw, cmd);
1623}
1624
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001625/*
Lennert Buytenhek1349ad22010-01-12 13:48:17 +01001626 * Setup code shared between STA and AP firmware images.
1627 */
1628static void mwl8k_setup_2ghz_band(struct ieee80211_hw *hw)
1629{
1630 struct mwl8k_priv *priv = hw->priv;
1631
1632 BUILD_BUG_ON(sizeof(priv->channels_24) != sizeof(mwl8k_channels_24));
1633 memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24));
1634
1635 BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24));
1636 memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24));
1637
1638 priv->band_24.band = IEEE80211_BAND_2GHZ;
1639 priv->band_24.channels = priv->channels_24;
1640 priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24);
1641 priv->band_24.bitrates = priv->rates_24;
1642 priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24);
1643
1644 hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band_24;
1645}
1646
Lennert Buytenhek4eae9ed2010-01-12 13:48:32 +01001647static void mwl8k_setup_5ghz_band(struct ieee80211_hw *hw)
1648{
1649 struct mwl8k_priv *priv = hw->priv;
1650
1651 BUILD_BUG_ON(sizeof(priv->channels_50) != sizeof(mwl8k_channels_50));
1652 memcpy(priv->channels_50, mwl8k_channels_50, sizeof(mwl8k_channels_50));
1653
1654 BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl8k_rates_50));
1655 memcpy(priv->rates_50, mwl8k_rates_50, sizeof(mwl8k_rates_50));
1656
1657 priv->band_50.band = IEEE80211_BAND_5GHZ;
1658 priv->band_50.channels = priv->channels_50;
1659 priv->band_50.n_channels = ARRAY_SIZE(mwl8k_channels_50);
1660 priv->band_50.bitrates = priv->rates_50;
1661 priv->band_50.n_bitrates = ARRAY_SIZE(mwl8k_rates_50);
1662
1663 hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &priv->band_50;
1664}
1665
Lennert Buytenhek1349ad22010-01-12 13:48:17 +01001666/*
Lennert Buytenhek04b147b2009-10-22 20:21:05 +02001667 * CMD_GET_HW_SPEC (STA version).
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001668 */
Lennert Buytenhek04b147b2009-10-22 20:21:05 +02001669struct mwl8k_cmd_get_hw_spec_sta {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001670 struct mwl8k_cmd_pkt header;
1671 __u8 hw_rev;
1672 __u8 host_interface;
1673 __le16 num_mcaddrs;
Lennert Buytenhekd89173f2009-07-16 09:54:27 +02001674 __u8 perm_addr[ETH_ALEN];
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001675 __le16 region_code;
1676 __le32 fw_rev;
1677 __le32 ps_cookie;
1678 __le32 caps;
1679 __u8 mcs_bitmap[16];
1680 __le32 rx_queue_ptr;
1681 __le32 num_tx_queues;
1682 __le32 tx_queue_ptrs[MWL8K_TX_QUEUES];
1683 __le32 caps2;
1684 __le32 num_tx_desc_per_queue;
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001685 __le32 total_rxd;
Eric Dumazetba2d3582010-06-02 18:10:09 +00001686} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001687
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001688#define MWL8K_CAP_MAX_AMSDU 0x20000000
1689#define MWL8K_CAP_GREENFIELD 0x08000000
1690#define MWL8K_CAP_AMPDU 0x04000000
1691#define MWL8K_CAP_RX_STBC 0x01000000
1692#define MWL8K_CAP_TX_STBC 0x00800000
1693#define MWL8K_CAP_SHORTGI_40MHZ 0x00400000
1694#define MWL8K_CAP_SHORTGI_20MHZ 0x00200000
1695#define MWL8K_CAP_RX_ANTENNA_MASK 0x000e0000
1696#define MWL8K_CAP_TX_ANTENNA_MASK 0x0001c000
1697#define MWL8K_CAP_DELAY_BA 0x00003000
1698#define MWL8K_CAP_MIMO 0x00000200
1699#define MWL8K_CAP_40MHZ 0x00000100
Lennert Buytenhek06953232010-01-12 13:49:41 +01001700#define MWL8K_CAP_BAND_MASK 0x00000007
1701#define MWL8K_CAP_5GHZ 0x00000004
1702#define MWL8K_CAP_2GHZ4 0x00000001
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001703
Lennert Buytenhek06953232010-01-12 13:49:41 +01001704static void
1705mwl8k_set_ht_caps(struct ieee80211_hw *hw,
1706 struct ieee80211_supported_band *band, u32 cap)
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001707{
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001708 int rx_streams;
1709 int tx_streams;
1710
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001711 band->ht_cap.ht_supported = 1;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001712
1713 if (cap & MWL8K_CAP_MAX_AMSDU)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001714 band->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001715 if (cap & MWL8K_CAP_GREENFIELD)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001716 band->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001717 if (cap & MWL8K_CAP_AMPDU) {
1718 hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001719 band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
1720 band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001721 }
1722 if (cap & MWL8K_CAP_RX_STBC)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001723 band->ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001724 if (cap & MWL8K_CAP_TX_STBC)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001725 band->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001726 if (cap & MWL8K_CAP_SHORTGI_40MHZ)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001727 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001728 if (cap & MWL8K_CAP_SHORTGI_20MHZ)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001729 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001730 if (cap & MWL8K_CAP_DELAY_BA)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001731 band->ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001732 if (cap & MWL8K_CAP_40MHZ)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001733 band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001734
1735 rx_streams = hweight32(cap & MWL8K_CAP_RX_ANTENNA_MASK);
1736 tx_streams = hweight32(cap & MWL8K_CAP_TX_ANTENNA_MASK);
1737
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001738 band->ht_cap.mcs.rx_mask[0] = 0xff;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001739 if (rx_streams >= 2)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001740 band->ht_cap.mcs.rx_mask[1] = 0xff;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001741 if (rx_streams >= 3)
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001742 band->ht_cap.mcs.rx_mask[2] = 0xff;
1743 band->ht_cap.mcs.rx_mask[4] = 0x01;
1744 band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001745
1746 if (rx_streams != tx_streams) {
Lennert Buytenhek777ad372010-01-12 13:48:04 +01001747 band->ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
1748 band->ht_cap.mcs.tx_params |= (tx_streams - 1) <<
Lennert Buytenhek341c9792010-01-04 21:58:40 +01001749 IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
1750 }
1751}
1752
Lennert Buytenhek06953232010-01-12 13:49:41 +01001753static void
1754mwl8k_set_caps(struct ieee80211_hw *hw, u32 caps)
1755{
1756 struct mwl8k_priv *priv = hw->priv;
1757
1758 if ((caps & MWL8K_CAP_2GHZ4) || !(caps & MWL8K_CAP_BAND_MASK)) {
1759 mwl8k_setup_2ghz_band(hw);
1760 if (caps & MWL8K_CAP_MIMO)
1761 mwl8k_set_ht_caps(hw, &priv->band_24, caps);
1762 }
1763
1764 if (caps & MWL8K_CAP_5GHZ) {
1765 mwl8k_setup_5ghz_band(hw);
1766 if (caps & MWL8K_CAP_MIMO)
1767 mwl8k_set_ht_caps(hw, &priv->band_50, caps);
1768 }
1769}
1770
Lennert Buytenhek04b147b2009-10-22 20:21:05 +02001771static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001772{
1773 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenhek04b147b2009-10-22 20:21:05 +02001774 struct mwl8k_cmd_get_hw_spec_sta *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001775 int rc;
1776 int i;
1777
1778 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
1779 if (cmd == NULL)
1780 return -ENOMEM;
1781
1782 cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC);
1783 cmd->header.length = cpu_to_le16(sizeof(*cmd));
1784
1785 memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr));
1786 cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001787 cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma);
Lennert Buytenhek4ff64322009-08-03 21:58:39 +02001788 cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001789 for (i = 0; i < MWL8K_TX_QUEUES; i++)
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001790 cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma);
Lennert Buytenhek4ff64322009-08-03 21:58:39 +02001791 cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);
Lennert Buytenhek45eb4002009-10-22 20:20:40 +02001792 cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001793
1794 rc = mwl8k_post_cmd(hw, &cmd->header);
1795
1796 if (!rc) {
1797 SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr);
1798 priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
Lennert Buytenhek4ff64322009-08-03 21:58:39 +02001799 priv->fw_rev = le32_to_cpu(cmd->fw_rev);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001800 priv->hw_rev = cmd->hw_rev;
Lennert Buytenhek06953232010-01-12 13:49:41 +01001801 mwl8k_set_caps(hw, le32_to_cpu(cmd->caps));
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01001802 priv->ap_macids_supported = 0x00000000;
1803 priv->sta_macids_supported = 0x00000001;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001804 }
1805
1806 kfree(cmd);
1807 return rc;
1808}
1809
1810/*
Lennert Buytenhek42fba212009-10-22 20:21:30 +02001811 * CMD_GET_HW_SPEC (AP version).
1812 */
1813struct mwl8k_cmd_get_hw_spec_ap {
1814 struct mwl8k_cmd_pkt header;
1815 __u8 hw_rev;
1816 __u8 host_interface;
1817 __le16 num_wcb;
1818 __le16 num_mcaddrs;
1819 __u8 perm_addr[ETH_ALEN];
1820 __le16 region_code;
1821 __le16 num_antenna;
1822 __le32 fw_rev;
1823 __le32 wcbbase0;
1824 __le32 rxwrptr;
1825 __le32 rxrdptr;
1826 __le32 ps_cookie;
1827 __le32 wcbbase1;
1828 __le32 wcbbase2;
1829 __le32 wcbbase3;
Eric Dumazetba2d3582010-06-02 18:10:09 +00001830} __packed;
Lennert Buytenhek42fba212009-10-22 20:21:30 +02001831
1832static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw)
1833{
1834 struct mwl8k_priv *priv = hw->priv;
1835 struct mwl8k_cmd_get_hw_spec_ap *cmd;
1836 int rc;
1837
1838 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
1839 if (cmd == NULL)
1840 return -ENOMEM;
1841
1842 cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC);
1843 cmd->header.length = cpu_to_le16(sizeof(*cmd));
1844
1845 memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr));
1846 cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
1847
1848 rc = mwl8k_post_cmd(hw, &cmd->header);
1849
1850 if (!rc) {
1851 int off;
1852
1853 SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr);
1854 priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
1855 priv->fw_rev = le32_to_cpu(cmd->fw_rev);
1856 priv->hw_rev = cmd->hw_rev;
Lennert Buytenhek1349ad22010-01-12 13:48:17 +01001857 mwl8k_setup_2ghz_band(hw);
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01001858 priv->ap_macids_supported = 0x000000ff;
1859 priv->sta_macids_supported = 0x00000000;
Lennert Buytenhek42fba212009-10-22 20:21:30 +02001860
1861 off = le32_to_cpu(cmd->wcbbase0) & 0xffff;
John W. Linvilleb6037422010-07-20 13:55:00 -04001862 iowrite32(priv->txq[0].txd_dma, priv->sram + off);
Lennert Buytenhek42fba212009-10-22 20:21:30 +02001863
1864 off = le32_to_cpu(cmd->rxwrptr) & 0xffff;
John W. Linvilleb6037422010-07-20 13:55:00 -04001865 iowrite32(priv->rxq[0].rxd_dma, priv->sram + off);
Lennert Buytenhek42fba212009-10-22 20:21:30 +02001866
1867 off = le32_to_cpu(cmd->rxrdptr) & 0xffff;
John W. Linvilleb6037422010-07-20 13:55:00 -04001868 iowrite32(priv->rxq[0].rxd_dma, priv->sram + off);
Lennert Buytenhek42fba212009-10-22 20:21:30 +02001869
1870 off = le32_to_cpu(cmd->wcbbase1) & 0xffff;
John W. Linvilleb6037422010-07-20 13:55:00 -04001871 iowrite32(priv->txq[1].txd_dma, priv->sram + off);
Lennert Buytenhek42fba212009-10-22 20:21:30 +02001872
1873 off = le32_to_cpu(cmd->wcbbase2) & 0xffff;
John W. Linvilleb6037422010-07-20 13:55:00 -04001874 iowrite32(priv->txq[2].txd_dma, priv->sram + off);
Lennert Buytenhek42fba212009-10-22 20:21:30 +02001875
1876 off = le32_to_cpu(cmd->wcbbase3) & 0xffff;
John W. Linvilleb6037422010-07-20 13:55:00 -04001877 iowrite32(priv->txq[3].txd_dma, priv->sram + off);
Lennert Buytenhek42fba212009-10-22 20:21:30 +02001878 }
1879
1880 kfree(cmd);
1881 return rc;
1882}
1883
1884/*
1885 * CMD_SET_HW_SPEC.
1886 */
1887struct mwl8k_cmd_set_hw_spec {
1888 struct mwl8k_cmd_pkt header;
1889 __u8 hw_rev;
1890 __u8 host_interface;
1891 __le16 num_mcaddrs;
1892 __u8 perm_addr[ETH_ALEN];
1893 __le16 region_code;
1894 __le32 fw_rev;
1895 __le32 ps_cookie;
1896 __le32 caps;
1897 __le32 rx_queue_ptr;
1898 __le32 num_tx_queues;
1899 __le32 tx_queue_ptrs[MWL8K_TX_QUEUES];
1900 __le32 flags;
1901 __le32 num_tx_desc_per_queue;
1902 __le32 total_rxd;
Eric Dumazetba2d3582010-06-02 18:10:09 +00001903} __packed;
Lennert Buytenhek42fba212009-10-22 20:21:30 +02001904
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01001905#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080
1906#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP 0x00000020
1907#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON 0x00000010
Lennert Buytenhek42fba212009-10-22 20:21:30 +02001908
1909static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw)
1910{
1911 struct mwl8k_priv *priv = hw->priv;
1912 struct mwl8k_cmd_set_hw_spec *cmd;
1913 int rc;
1914 int i;
1915
1916 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
1917 if (cmd == NULL)
1918 return -ENOMEM;
1919
1920 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_HW_SPEC);
1921 cmd->header.length = cpu_to_le16(sizeof(*cmd));
1922
1923 cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
1924 cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma);
1925 cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES);
1926 for (i = 0; i < MWL8K_TX_QUEUES; i++)
1927 cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma);
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01001928 cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT |
1929 MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP |
1930 MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON);
Lennert Buytenhek42fba212009-10-22 20:21:30 +02001931 cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);
1932 cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS);
1933
1934 rc = mwl8k_post_cmd(hw, &cmd->header);
1935 kfree(cmd);
1936
1937 return rc;
1938}
1939
1940/*
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001941 * CMD_MAC_MULTICAST_ADR.
1942 */
1943struct mwl8k_cmd_mac_multicast_adr {
1944 struct mwl8k_cmd_pkt header;
1945 __le16 action;
1946 __le16 numaddr;
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02001947 __u8 addr[0][ETH_ALEN];
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001948};
1949
Lennert Buytenhekd5e30842009-10-22 20:19:41 +02001950#define MWL8K_ENABLE_RX_DIRECTED 0x0001
1951#define MWL8K_ENABLE_RX_MULTICAST 0x0002
1952#define MWL8K_ENABLE_RX_ALL_MULTICAST 0x0004
1953#define MWL8K_ENABLE_RX_BROADCAST 0x0008
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02001954
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02001955static struct mwl8k_cmd_pkt *
Lennert Buytenhek447ced02009-10-22 20:19:50 +02001956__mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti,
Jiri Pirko22bedad2010-04-01 21:22:57 +00001957 struct netdev_hw_addr_list *mc_list)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001958{
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02001959 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001960 struct mwl8k_cmd_mac_multicast_adr *cmd;
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02001961 int size;
Jiri Pirko22bedad2010-04-01 21:22:57 +00001962 int mc_count = 0;
1963
1964 if (mc_list)
1965 mc_count = netdev_hw_addr_list_count(mc_list);
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02001966
Lennert Buytenhek447ced02009-10-22 20:19:50 +02001967 if (allmulti || mc_count > priv->num_mcaddrs) {
Lennert Buytenhekd5e30842009-10-22 20:19:41 +02001968 allmulti = 1;
1969 mc_count = 0;
1970 }
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02001971
1972 size = sizeof(*cmd) + mc_count * ETH_ALEN;
1973
1974 cmd = kzalloc(size, GFP_ATOMIC);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001975 if (cmd == NULL)
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02001976 return NULL;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001977
1978 cmd->header.code = cpu_to_le16(MWL8K_CMD_MAC_MULTICAST_ADR);
1979 cmd->header.length = cpu_to_le16(size);
Lennert Buytenhekd5e30842009-10-22 20:19:41 +02001980 cmd->action = cpu_to_le16(MWL8K_ENABLE_RX_DIRECTED |
1981 MWL8K_ENABLE_RX_BROADCAST);
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02001982
Lennert Buytenhekd5e30842009-10-22 20:19:41 +02001983 if (allmulti) {
1984 cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_ALL_MULTICAST);
1985 } else if (mc_count) {
Jiri Pirko22bedad2010-04-01 21:22:57 +00001986 struct netdev_hw_addr *ha;
1987 int i = 0;
Lennert Buytenhekd5e30842009-10-22 20:19:41 +02001988
1989 cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST);
1990 cmd->numaddr = cpu_to_le16(mc_count);
Jiri Pirko22bedad2010-04-01 21:22:57 +00001991 netdev_hw_addr_list_for_each(ha, mc_list) {
1992 memcpy(cmd->addr[i], ha->addr, ETH_ALEN);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001993 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001994 }
1995
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02001996 return &cmd->header;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01001997}
1998
1999/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002000 * CMD_GET_STAT.
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002001 */
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002002struct mwl8k_cmd_get_stat {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002003 struct mwl8k_cmd_pkt header;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002004 __le32 stats[64];
Eric Dumazetba2d3582010-06-02 18:10:09 +00002005} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002006
2007#define MWL8K_STAT_ACK_FAILURE 9
2008#define MWL8K_STAT_RTS_FAILURE 12
2009#define MWL8K_STAT_FCS_ERROR 24
2010#define MWL8K_STAT_RTS_SUCCESS 11
2011
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002012static int mwl8k_cmd_get_stat(struct ieee80211_hw *hw,
2013 struct ieee80211_low_level_stats *stats)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002014{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002015 struct mwl8k_cmd_get_stat *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002016 int rc;
2017
2018 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2019 if (cmd == NULL)
2020 return -ENOMEM;
2021
2022 cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_STAT);
2023 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002024
2025 rc = mwl8k_post_cmd(hw, &cmd->header);
2026 if (!rc) {
2027 stats->dot11ACKFailureCount =
2028 le32_to_cpu(cmd->stats[MWL8K_STAT_ACK_FAILURE]);
2029 stats->dot11RTSFailureCount =
2030 le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_FAILURE]);
2031 stats->dot11FCSErrorCount =
2032 le32_to_cpu(cmd->stats[MWL8K_STAT_FCS_ERROR]);
2033 stats->dot11RTSSuccessCount =
2034 le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_SUCCESS]);
2035 }
2036 kfree(cmd);
2037
2038 return rc;
2039}
2040
2041/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002042 * CMD_RADIO_CONTROL.
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002043 */
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002044struct mwl8k_cmd_radio_control {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002045 struct mwl8k_cmd_pkt header;
2046 __le16 action;
2047 __le16 control;
2048 __le16 radio_on;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002049} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002050
Lennert Buytenhekc46563b2009-07-16 12:14:58 +02002051static int
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002052mwl8k_cmd_radio_control(struct ieee80211_hw *hw, bool enable, bool force)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002053{
2054 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002055 struct mwl8k_cmd_radio_control *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002056 int rc;
2057
Lennert Buytenhekc46563b2009-07-16 12:14:58 +02002058 if (enable == priv->radio_on && !force)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002059 return 0;
2060
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002061 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2062 if (cmd == NULL)
2063 return -ENOMEM;
2064
2065 cmd->header.code = cpu_to_le16(MWL8K_CMD_RADIO_CONTROL);
2066 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2067 cmd->action = cpu_to_le16(MWL8K_CMD_SET);
Lennert Buytenhek68ce3882009-07-16 12:26:57 +02002068 cmd->control = cpu_to_le16(priv->radio_short_preamble ? 3 : 1);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002069 cmd->radio_on = cpu_to_le16(enable ? 0x0001 : 0x0000);
2070
2071 rc = mwl8k_post_cmd(hw, &cmd->header);
2072 kfree(cmd);
2073
2074 if (!rc)
Lennert Buytenhekc46563b2009-07-16 12:14:58 +02002075 priv->radio_on = enable;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002076
2077 return rc;
2078}
2079
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002080static int mwl8k_cmd_radio_disable(struct ieee80211_hw *hw)
Lennert Buytenhekc46563b2009-07-16 12:14:58 +02002081{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002082 return mwl8k_cmd_radio_control(hw, 0, 0);
Lennert Buytenhekc46563b2009-07-16 12:14:58 +02002083}
2084
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002085static int mwl8k_cmd_radio_enable(struct ieee80211_hw *hw)
Lennert Buytenhekc46563b2009-07-16 12:14:58 +02002086{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002087 return mwl8k_cmd_radio_control(hw, 1, 0);
Lennert Buytenhekc46563b2009-07-16 12:14:58 +02002088}
2089
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002090static int
2091mwl8k_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble)
2092{
Lennert Buytenhek99200a992009-11-30 18:31:40 +01002093 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002094
Lennert Buytenhek68ce3882009-07-16 12:26:57 +02002095 priv->radio_short_preamble = short_preamble;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002096
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002097 return mwl8k_cmd_radio_control(hw, 1, 1);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002098}
2099
2100/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002101 * CMD_RF_TX_POWER.
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002102 */
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -08002103#define MWL8K_RF_TX_POWER_LEVEL_TOTAL 8
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002104
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002105struct mwl8k_cmd_rf_tx_power {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002106 struct mwl8k_cmd_pkt header;
2107 __le16 action;
2108 __le16 support_level;
2109 __le16 current_level;
2110 __le16 reserved;
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -08002111 __le16 power_level_list[MWL8K_RF_TX_POWER_LEVEL_TOTAL];
Eric Dumazetba2d3582010-06-02 18:10:09 +00002112} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002113
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002114static int mwl8k_cmd_rf_tx_power(struct ieee80211_hw *hw, int dBm)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002115{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002116 struct mwl8k_cmd_rf_tx_power *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002117 int rc;
2118
2119 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2120 if (cmd == NULL)
2121 return -ENOMEM;
2122
2123 cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_TX_POWER);
2124 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2125 cmd->action = cpu_to_le16(MWL8K_CMD_SET);
2126 cmd->support_level = cpu_to_le16(dBm);
2127
2128 rc = mwl8k_post_cmd(hw, &cmd->header);
2129 kfree(cmd);
2130
2131 return rc;
2132}
2133
2134/*
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -08002135 * CMD_TX_POWER.
2136 */
2137#define MWL8K_TX_POWER_LEVEL_TOTAL 12
2138
2139struct mwl8k_cmd_tx_power {
2140 struct mwl8k_cmd_pkt header;
2141 __le16 action;
2142 __le16 band;
2143 __le16 channel;
2144 __le16 bw;
2145 __le16 sub_ch;
2146 __le16 power_level_list[MWL8K_TX_POWER_LEVEL_TOTAL];
2147} __attribute__((packed));
2148
2149static int mwl8k_cmd_tx_power(struct ieee80211_hw *hw,
2150 struct ieee80211_conf *conf,
2151 unsigned short pwr)
2152{
2153 struct ieee80211_channel *channel = conf->channel;
2154 struct mwl8k_cmd_tx_power *cmd;
2155 int rc;
2156 int i;
2157
2158 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2159 if (cmd == NULL)
2160 return -ENOMEM;
2161
2162 cmd->header.code = cpu_to_le16(MWL8K_CMD_TX_POWER);
2163 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2164 cmd->action = cpu_to_le16(MWL8K_CMD_SET_LIST);
2165
2166 if (channel->band == IEEE80211_BAND_2GHZ)
2167 cmd->band = cpu_to_le16(0x1);
2168 else if (channel->band == IEEE80211_BAND_5GHZ)
2169 cmd->band = cpu_to_le16(0x4);
2170
2171 cmd->channel = channel->hw_value;
2172
2173 if (conf->channel_type == NL80211_CHAN_NO_HT ||
2174 conf->channel_type == NL80211_CHAN_HT20) {
2175 cmd->bw = cpu_to_le16(0x2);
2176 } else {
2177 cmd->bw = cpu_to_le16(0x4);
2178 if (conf->channel_type == NL80211_CHAN_HT40MINUS)
2179 cmd->sub_ch = cpu_to_le16(0x3);
2180 else if (conf->channel_type == NL80211_CHAN_HT40PLUS)
2181 cmd->sub_ch = cpu_to_le16(0x1);
2182 }
2183
2184 for (i = 0; i < MWL8K_TX_POWER_LEVEL_TOTAL; i++)
2185 cmd->power_level_list[i] = cpu_to_le16(pwr);
2186
2187 rc = mwl8k_post_cmd(hw, &cmd->header);
2188 kfree(cmd);
2189
2190 return rc;
2191}
2192
2193/*
Lennert Buytenhek08b06342009-10-22 20:21:33 +02002194 * CMD_RF_ANTENNA.
2195 */
2196struct mwl8k_cmd_rf_antenna {
2197 struct mwl8k_cmd_pkt header;
2198 __le16 antenna;
2199 __le16 mode;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002200} __packed;
Lennert Buytenhek08b06342009-10-22 20:21:33 +02002201
2202#define MWL8K_RF_ANTENNA_RX 1
2203#define MWL8K_RF_ANTENNA_TX 2
2204
2205static int
2206mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask)
2207{
2208 struct mwl8k_cmd_rf_antenna *cmd;
2209 int rc;
2210
2211 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2212 if (cmd == NULL)
2213 return -ENOMEM;
2214
2215 cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_ANTENNA);
2216 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2217 cmd->antenna = cpu_to_le16(antenna);
2218 cmd->mode = cpu_to_le16(mask);
2219
2220 rc = mwl8k_post_cmd(hw, &cmd->header);
2221 kfree(cmd);
2222
2223 return rc;
2224}
2225
2226/*
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01002227 * CMD_SET_BEACON.
2228 */
2229struct mwl8k_cmd_set_beacon {
2230 struct mwl8k_cmd_pkt header;
2231 __le16 beacon_len;
2232 __u8 beacon[0];
2233};
2234
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01002235static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw,
2236 struct ieee80211_vif *vif, u8 *beacon, int len)
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01002237{
2238 struct mwl8k_cmd_set_beacon *cmd;
2239 int rc;
2240
2241 cmd = kzalloc(sizeof(*cmd) + len, GFP_KERNEL);
2242 if (cmd == NULL)
2243 return -ENOMEM;
2244
2245 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_BEACON);
2246 cmd->header.length = cpu_to_le16(sizeof(*cmd) + len);
2247 cmd->beacon_len = cpu_to_le16(len);
2248 memcpy(cmd->beacon, beacon, len);
2249
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01002250 rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01002251 kfree(cmd);
2252
2253 return rc;
2254}
2255
2256/*
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002257 * CMD_SET_PRE_SCAN.
2258 */
2259struct mwl8k_cmd_set_pre_scan {
2260 struct mwl8k_cmd_pkt header;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002261} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002262
2263static int mwl8k_cmd_set_pre_scan(struct ieee80211_hw *hw)
2264{
2265 struct mwl8k_cmd_set_pre_scan *cmd;
2266 int rc;
2267
2268 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2269 if (cmd == NULL)
2270 return -ENOMEM;
2271
2272 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_PRE_SCAN);
2273 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2274
2275 rc = mwl8k_post_cmd(hw, &cmd->header);
2276 kfree(cmd);
2277
2278 return rc;
2279}
2280
2281/*
2282 * CMD_SET_POST_SCAN.
2283 */
2284struct mwl8k_cmd_set_post_scan {
2285 struct mwl8k_cmd_pkt header;
2286 __le32 isibss;
Lennert Buytenhekd89173f2009-07-16 09:54:27 +02002287 __u8 bssid[ETH_ALEN];
Eric Dumazetba2d3582010-06-02 18:10:09 +00002288} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002289
2290static int
Lennert Buytenhek0a11dfc2010-01-04 21:55:21 +01002291mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002292{
2293 struct mwl8k_cmd_set_post_scan *cmd;
2294 int rc;
2295
2296 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2297 if (cmd == NULL)
2298 return -ENOMEM;
2299
2300 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_POST_SCAN);
2301 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2302 cmd->isibss = 0;
Lennert Buytenhekd89173f2009-07-16 09:54:27 +02002303 memcpy(cmd->bssid, mac, ETH_ALEN);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002304
2305 rc = mwl8k_post_cmd(hw, &cmd->header);
2306 kfree(cmd);
2307
2308 return rc;
2309}
2310
2311/*
2312 * CMD_SET_RF_CHANNEL.
2313 */
2314struct mwl8k_cmd_set_rf_channel {
2315 struct mwl8k_cmd_pkt header;
2316 __le16 action;
2317 __u8 current_channel;
2318 __le32 channel_flags;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002319} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002320
2321static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw,
Lennert Buytenhek610677d2010-01-04 21:56:46 +01002322 struct ieee80211_conf *conf)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002323{
Lennert Buytenhek610677d2010-01-04 21:56:46 +01002324 struct ieee80211_channel *channel = conf->channel;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002325 struct mwl8k_cmd_set_rf_channel *cmd;
2326 int rc;
2327
2328 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2329 if (cmd == NULL)
2330 return -ENOMEM;
2331
2332 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RF_CHANNEL);
2333 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2334 cmd->action = cpu_to_le16(MWL8K_CMD_SET);
2335 cmd->current_channel = channel->hw_value;
Lennert Buytenhek610677d2010-01-04 21:56:46 +01002336
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002337 if (channel->band == IEEE80211_BAND_2GHZ)
Lennert Buytenhek610677d2010-01-04 21:56:46 +01002338 cmd->channel_flags |= cpu_to_le32(0x00000001);
Lennert Buytenhek42574ea2010-01-12 13:49:18 +01002339 else if (channel->band == IEEE80211_BAND_5GHZ)
2340 cmd->channel_flags |= cpu_to_le32(0x00000004);
Lennert Buytenhek610677d2010-01-04 21:56:46 +01002341
2342 if (conf->channel_type == NL80211_CHAN_NO_HT ||
2343 conf->channel_type == NL80211_CHAN_HT20)
2344 cmd->channel_flags |= cpu_to_le32(0x00000080);
2345 else if (conf->channel_type == NL80211_CHAN_HT40MINUS)
2346 cmd->channel_flags |= cpu_to_le32(0x000001900);
2347 else if (conf->channel_type == NL80211_CHAN_HT40PLUS)
2348 cmd->channel_flags |= cpu_to_le32(0x000000900);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002349
2350 rc = mwl8k_post_cmd(hw, &cmd->header);
2351 kfree(cmd);
2352
2353 return rc;
2354}
2355
2356/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002357 * CMD_SET_AID.
2358 */
2359#define MWL8K_FRAME_PROT_DISABLED 0x00
2360#define MWL8K_FRAME_PROT_11G 0x07
2361#define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY 0x02
2362#define MWL8K_FRAME_PROT_11N_HT_ALL 0x06
2363
2364struct mwl8k_cmd_update_set_aid {
2365 struct mwl8k_cmd_pkt header;
2366 __le16 aid;
2367
2368 /* AP's MAC address (BSSID) */
2369 __u8 bssid[ETH_ALEN];
2370 __le16 protection_mode;
2371 __u8 supp_rates[14];
Eric Dumazetba2d3582010-06-02 18:10:09 +00002372} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002373
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01002374static void legacy_rate_mask_to_array(u8 *rates, u32 mask)
2375{
2376 int i;
2377 int j;
2378
2379 /*
2380 * Clear nonstandard rates 4 and 13.
2381 */
2382 mask &= 0x1fef;
2383
2384 for (i = 0, j = 0; i < 14; i++) {
2385 if (mask & (1 << i))
Lennert Buytenhek777ad372010-01-12 13:48:04 +01002386 rates[j++] = mwl8k_rates_24[i].hw_value;
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01002387 }
2388}
2389
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002390static int
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01002391mwl8k_cmd_set_aid(struct ieee80211_hw *hw,
2392 struct ieee80211_vif *vif, u32 legacy_rate_mask)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002393{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002394 struct mwl8k_cmd_update_set_aid *cmd;
2395 u16 prot_mode;
2396 int rc;
2397
2398 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2399 if (cmd == NULL)
2400 return -ENOMEM;
2401
2402 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID);
2403 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenhek7dc6a7a2009-11-30 18:33:09 +01002404 cmd->aid = cpu_to_le16(vif->bss_conf.aid);
Lennert Buytenhek0a11dfc2010-01-04 21:55:21 +01002405 memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002406
Lennert Buytenhek7dc6a7a2009-11-30 18:33:09 +01002407 if (vif->bss_conf.use_cts_prot) {
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002408 prot_mode = MWL8K_FRAME_PROT_11G;
2409 } else {
Lennert Buytenhek7dc6a7a2009-11-30 18:33:09 +01002410 switch (vif->bss_conf.ht_operation_mode &
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002411 IEEE80211_HT_OP_MODE_PROTECTION) {
2412 case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
2413 prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY;
2414 break;
2415 case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
2416 prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL;
2417 break;
2418 default:
2419 prot_mode = MWL8K_FRAME_PROT_DISABLED;
2420 break;
2421 }
2422 }
2423 cmd->protection_mode = cpu_to_le16(prot_mode);
2424
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01002425 legacy_rate_mask_to_array(cmd->supp_rates, legacy_rate_mask);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002426
2427 rc = mwl8k_post_cmd(hw, &cmd->header);
2428 kfree(cmd);
2429
2430 return rc;
2431}
2432
2433/*
2434 * CMD_SET_RATE.
2435 */
2436struct mwl8k_cmd_set_rate {
2437 struct mwl8k_cmd_pkt header;
2438 __u8 legacy_rates[14];
2439
2440 /* Bitmap for supported MCS codes. */
2441 __u8 mcs_set[16];
2442 __u8 reserved[16];
Eric Dumazetba2d3582010-06-02 18:10:09 +00002443} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002444
2445static int
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01002446mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
Lennert Buytenhek13935e22010-01-04 21:57:59 +01002447 u32 legacy_rate_mask, u8 *mcs_rates)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002448{
2449 struct mwl8k_cmd_set_rate *cmd;
2450 int rc;
2451
2452 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2453 if (cmd == NULL)
2454 return -ENOMEM;
2455
2456 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE);
2457 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01002458 legacy_rate_mask_to_array(cmd->legacy_rates, legacy_rate_mask);
Lennert Buytenhek13935e22010-01-04 21:57:59 +01002459 memcpy(cmd->mcs_set, mcs_rates, 16);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002460
2461 rc = mwl8k_post_cmd(hw, &cmd->header);
2462 kfree(cmd);
2463
2464 return rc;
2465}
2466
2467/*
2468 * CMD_FINALIZE_JOIN.
2469 */
2470#define MWL8K_FJ_BEACON_MAXLEN 128
2471
2472struct mwl8k_cmd_finalize_join {
2473 struct mwl8k_cmd_pkt header;
2474 __le32 sleep_interval; /* Number of beacon periods to sleep */
2475 __u8 beacon_data[MWL8K_FJ_BEACON_MAXLEN];
Eric Dumazetba2d3582010-06-02 18:10:09 +00002476} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002477
2478static int mwl8k_cmd_finalize_join(struct ieee80211_hw *hw, void *frame,
2479 int framelen, int dtim)
2480{
2481 struct mwl8k_cmd_finalize_join *cmd;
2482 struct ieee80211_mgmt *payload = frame;
2483 int payload_len;
2484 int rc;
2485
2486 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2487 if (cmd == NULL)
2488 return -ENOMEM;
2489
2490 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN);
2491 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2492 cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1);
2493
2494 payload_len = framelen - ieee80211_hdrlen(payload->frame_control);
2495 if (payload_len < 0)
2496 payload_len = 0;
2497 else if (payload_len > MWL8K_FJ_BEACON_MAXLEN)
2498 payload_len = MWL8K_FJ_BEACON_MAXLEN;
2499
2500 memcpy(cmd->beacon_data, &payload->u.beacon, payload_len);
2501
2502 rc = mwl8k_post_cmd(hw, &cmd->header);
2503 kfree(cmd);
2504
2505 return rc;
2506}
2507
2508/*
2509 * CMD_SET_RTS_THRESHOLD.
2510 */
2511struct mwl8k_cmd_set_rts_threshold {
2512 struct mwl8k_cmd_pkt header;
2513 __le16 action;
2514 __le16 threshold;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002515} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002516
Lennert Buytenhekc2c2b122010-01-08 18:27:59 +01002517static int
2518mwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, int rts_thresh)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002519{
2520 struct mwl8k_cmd_set_rts_threshold *cmd;
2521 int rc;
2522
2523 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2524 if (cmd == NULL)
2525 return -ENOMEM;
2526
2527 cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD);
2528 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenhekc2c2b122010-01-08 18:27:59 +01002529 cmd->action = cpu_to_le16(MWL8K_CMD_SET);
2530 cmd->threshold = cpu_to_le16(rts_thresh);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002531
2532 rc = mwl8k_post_cmd(hw, &cmd->header);
2533 kfree(cmd);
2534
2535 return rc;
2536}
2537
2538/*
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002539 * CMD_SET_SLOT.
2540 */
2541struct mwl8k_cmd_set_slot {
2542 struct mwl8k_cmd_pkt header;
2543 __le16 action;
2544 __u8 short_slot;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002545} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002546
Lennert Buytenhek5539bb52009-07-16 16:06:53 +02002547static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002548{
2549 struct mwl8k_cmd_set_slot *cmd;
2550 int rc;
2551
2552 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2553 if (cmd == NULL)
2554 return -ENOMEM;
2555
2556 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT);
2557 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2558 cmd->action = cpu_to_le16(MWL8K_CMD_SET);
Lennert Buytenhek5539bb52009-07-16 16:06:53 +02002559 cmd->short_slot = short_slot_time;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002560
2561 rc = mwl8k_post_cmd(hw, &cmd->header);
2562 kfree(cmd);
2563
2564 return rc;
2565}
2566
2567/*
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002568 * CMD_SET_EDCA_PARAMS.
2569 */
2570struct mwl8k_cmd_set_edca_params {
2571 struct mwl8k_cmd_pkt header;
2572
2573 /* See MWL8K_SET_EDCA_XXX below */
2574 __le16 action;
2575
2576 /* TX opportunity in units of 32 us */
2577 __le16 txop;
2578
Lennert Buytenhek2e484c82009-10-22 20:21:43 +02002579 union {
2580 struct {
2581 /* Log exponent of max contention period: 0...15 */
2582 __le32 log_cw_max;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002583
Lennert Buytenhek2e484c82009-10-22 20:21:43 +02002584 /* Log exponent of min contention period: 0...15 */
2585 __le32 log_cw_min;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002586
Lennert Buytenhek2e484c82009-10-22 20:21:43 +02002587 /* Adaptive interframe spacing in units of 32us */
2588 __u8 aifs;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002589
Lennert Buytenhek2e484c82009-10-22 20:21:43 +02002590 /* TX queue to configure */
2591 __u8 txq;
2592 } ap;
2593 struct {
2594 /* Log exponent of max contention period: 0...15 */
2595 __u8 log_cw_max;
2596
2597 /* Log exponent of min contention period: 0...15 */
2598 __u8 log_cw_min;
2599
2600 /* Adaptive interframe spacing in units of 32us */
2601 __u8 aifs;
2602
2603 /* TX queue to configure */
2604 __u8 txq;
2605 } sta;
2606 };
Eric Dumazetba2d3582010-06-02 18:10:09 +00002607} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002608
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002609#define MWL8K_SET_EDCA_CW 0x01
2610#define MWL8K_SET_EDCA_TXOP 0x02
2611#define MWL8K_SET_EDCA_AIFS 0x04
2612
2613#define MWL8K_SET_EDCA_ALL (MWL8K_SET_EDCA_CW | \
2614 MWL8K_SET_EDCA_TXOP | \
2615 MWL8K_SET_EDCA_AIFS)
2616
2617static int
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002618mwl8k_cmd_set_edca_params(struct ieee80211_hw *hw, __u8 qnum,
2619 __u16 cw_min, __u16 cw_max,
2620 __u8 aifs, __u16 txop)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002621{
Lennert Buytenhek2e484c82009-10-22 20:21:43 +02002622 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002623 struct mwl8k_cmd_set_edca_params *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002624 int rc;
2625
2626 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2627 if (cmd == NULL)
2628 return -ENOMEM;
2629
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002630 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS);
2631 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002632 cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL);
2633 cmd->txop = cpu_to_le16(txop);
Lennert Buytenhek2e484c82009-10-22 20:21:43 +02002634 if (priv->ap_fw) {
2635 cmd->ap.log_cw_max = cpu_to_le32(ilog2(cw_max + 1));
2636 cmd->ap.log_cw_min = cpu_to_le32(ilog2(cw_min + 1));
2637 cmd->ap.aifs = aifs;
2638 cmd->ap.txq = qnum;
2639 } else {
2640 cmd->sta.log_cw_max = (u8)ilog2(cw_max + 1);
2641 cmd->sta.log_cw_min = (u8)ilog2(cw_min + 1);
2642 cmd->sta.aifs = aifs;
2643 cmd->sta.txq = qnum;
2644 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002645
2646 rc = mwl8k_post_cmd(hw, &cmd->header);
2647 kfree(cmd);
2648
2649 return rc;
2650}
2651
2652/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002653 * CMD_SET_WMM_MODE.
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002654 */
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002655struct mwl8k_cmd_set_wmm_mode {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002656 struct mwl8k_cmd_pkt header;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002657 __le16 action;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002658} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002659
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002660static int mwl8k_cmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002661{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002662 struct mwl8k_priv *priv = hw->priv;
2663 struct mwl8k_cmd_set_wmm_mode *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002664 int rc;
2665
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002666 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2667 if (cmd == NULL)
2668 return -ENOMEM;
2669
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002670 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_WMM_MODE);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002671 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002672 cmd->action = cpu_to_le16(!!enable);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002673
2674 rc = mwl8k_post_cmd(hw, &cmd->header);
2675 kfree(cmd);
Lennert Buytenhek16cec432009-11-30 18:14:23 +01002676
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002677 if (!rc)
2678 priv->wmm_enabled = enable;
2679
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002680 return rc;
2681}
2682
2683/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002684 * CMD_MIMO_CONFIG.
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002685 */
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002686struct mwl8k_cmd_mimo_config {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002687 struct mwl8k_cmd_pkt header;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002688 __le32 action;
2689 __u8 rx_antenna_map;
2690 __u8 tx_antenna_map;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002691} __packed;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002692
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002693static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002694{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002695 struct mwl8k_cmd_mimo_config *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002696 int rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002697
2698 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2699 if (cmd == NULL)
2700 return -ENOMEM;
2701
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002702 cmd->header.code = cpu_to_le16(MWL8K_CMD_MIMO_CONFIG);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002703 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002704 cmd->action = cpu_to_le32((u32)MWL8K_CMD_SET);
2705 cmd->rx_antenna_map = rx;
2706 cmd->tx_antenna_map = tx;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002707
2708 rc = mwl8k_post_cmd(hw, &cmd->header);
2709 kfree(cmd);
2710
2711 return rc;
2712}
2713
2714/*
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01002715 * CMD_USE_FIXED_RATE (STA version).
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002716 */
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01002717struct mwl8k_cmd_use_fixed_rate_sta {
2718 struct mwl8k_cmd_pkt header;
2719 __le32 action;
2720 __le32 allow_rate_drop;
2721 __le32 num_rates;
2722 struct {
2723 __le32 is_ht_rate;
2724 __le32 enable_retry;
2725 __le32 rate;
2726 __le32 retry_count;
2727 } rate_entry[8];
2728 __le32 rate_type;
2729 __le32 reserved1;
2730 __le32 reserved2;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002731} __packed;
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01002732
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002733#define MWL8K_USE_AUTO_RATE 0x0002
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01002734#define MWL8K_UCAST_RATE 0
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002735
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01002736static int mwl8k_cmd_use_fixed_rate_sta(struct ieee80211_hw *hw)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002737{
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01002738 struct mwl8k_cmd_use_fixed_rate_sta *cmd;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002739 int rc;
2740
2741 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2742 if (cmd == NULL)
2743 return -ENOMEM;
2744
2745 cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE);
2746 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01002747 cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE);
2748 cmd->rate_type = cpu_to_le32(MWL8K_UCAST_RATE);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01002749
2750 rc = mwl8k_post_cmd(hw, &cmd->header);
2751 kfree(cmd);
2752
2753 return rc;
2754}
2755
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002756/*
Lennert Buytenhek088aab82010-01-08 18:30:36 +01002757 * CMD_USE_FIXED_RATE (AP version).
2758 */
2759struct mwl8k_cmd_use_fixed_rate_ap {
2760 struct mwl8k_cmd_pkt header;
2761 __le32 action;
2762 __le32 allow_rate_drop;
2763 __le32 num_rates;
2764 struct mwl8k_rate_entry_ap {
2765 __le32 is_ht_rate;
2766 __le32 enable_retry;
2767 __le32 rate;
2768 __le32 retry_count;
2769 } rate_entry[4];
2770 u8 multicast_rate;
2771 u8 multicast_rate_type;
2772 u8 management_rate;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002773} __packed;
Lennert Buytenhek088aab82010-01-08 18:30:36 +01002774
2775static int
2776mwl8k_cmd_use_fixed_rate_ap(struct ieee80211_hw *hw, int mcast, int mgmt)
2777{
2778 struct mwl8k_cmd_use_fixed_rate_ap *cmd;
2779 int rc;
2780
2781 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2782 if (cmd == NULL)
2783 return -ENOMEM;
2784
2785 cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE);
2786 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2787 cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE);
2788 cmd->multicast_rate = mcast;
2789 cmd->management_rate = mgmt;
2790
2791 rc = mwl8k_post_cmd(hw, &cmd->header);
2792 kfree(cmd);
2793
2794 return rc;
2795}
2796
2797/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002798 * CMD_ENABLE_SNIFFER.
2799 */
2800struct mwl8k_cmd_enable_sniffer {
2801 struct mwl8k_cmd_pkt header;
2802 __le32 action;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002803} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002804
2805static int mwl8k_cmd_enable_sniffer(struct ieee80211_hw *hw, bool enable)
2806{
2807 struct mwl8k_cmd_enable_sniffer *cmd;
2808 int rc;
2809
2810 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2811 if (cmd == NULL)
2812 return -ENOMEM;
2813
2814 cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER);
2815 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2816 cmd->action = cpu_to_le32(!!enable);
2817
2818 rc = mwl8k_post_cmd(hw, &cmd->header);
2819 kfree(cmd);
2820
2821 return rc;
2822}
2823
2824/*
2825 * CMD_SET_MAC_ADDR.
2826 */
2827struct mwl8k_cmd_set_mac_addr {
2828 struct mwl8k_cmd_pkt header;
2829 union {
2830 struct {
2831 __le16 mac_type;
2832 __u8 mac_addr[ETH_ALEN];
2833 } mbss;
2834 __u8 mac_addr[ETH_ALEN];
2835 };
Eric Dumazetba2d3582010-06-02 18:10:09 +00002836} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002837
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01002838#define MWL8K_MAC_TYPE_PRIMARY_CLIENT 0
2839#define MWL8K_MAC_TYPE_SECONDARY_CLIENT 1
2840#define MWL8K_MAC_TYPE_PRIMARY_AP 2
2841#define MWL8K_MAC_TYPE_SECONDARY_AP 3
Lennert Buytenheka9e00b12010-01-08 18:31:30 +01002842
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01002843static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw,
2844 struct ieee80211_vif *vif, u8 *mac)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002845{
2846 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01002847 struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002848 struct mwl8k_cmd_set_mac_addr *cmd;
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01002849 int mac_type;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002850 int rc;
2851
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01002852 mac_type = MWL8K_MAC_TYPE_PRIMARY_AP;
2853 if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) {
2854 if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported))
2855 mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT;
2856 else
2857 mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT;
2858 } else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) {
2859 if (mwl8k_vif->macid + 1 == ffs(priv->ap_macids_supported))
2860 mac_type = MWL8K_MAC_TYPE_PRIMARY_AP;
2861 else
2862 mac_type = MWL8K_MAC_TYPE_SECONDARY_AP;
2863 }
2864
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002865 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2866 if (cmd == NULL)
2867 return -ENOMEM;
2868
2869 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR);
2870 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2871 if (priv->ap_fw) {
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01002872 cmd->mbss.mac_type = cpu_to_le16(mac_type);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002873 memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN);
2874 } else {
2875 memcpy(cmd->mac_addr, mac, ETH_ALEN);
2876 }
2877
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01002878 rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002879 kfree(cmd);
2880
2881 return rc;
2882}
2883
2884/*
2885 * CMD_SET_RATEADAPT_MODE.
2886 */
2887struct mwl8k_cmd_set_rate_adapt_mode {
2888 struct mwl8k_cmd_pkt header;
2889 __le16 action;
2890 __le16 mode;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002891} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01002892
2893static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode)
2894{
2895 struct mwl8k_cmd_set_rate_adapt_mode *cmd;
2896 int rc;
2897
2898 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2899 if (cmd == NULL)
2900 return -ENOMEM;
2901
2902 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATEADAPT_MODE);
2903 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2904 cmd->action = cpu_to_le16(MWL8K_CMD_SET);
2905 cmd->mode = cpu_to_le16(mode);
2906
2907 rc = mwl8k_post_cmd(hw, &cmd->header);
2908 kfree(cmd);
2909
2910 return rc;
2911}
2912
2913/*
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01002914 * CMD_BSS_START.
2915 */
2916struct mwl8k_cmd_bss_start {
2917 struct mwl8k_cmd_pkt header;
2918 __le32 enable;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002919} __packed;
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01002920
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01002921static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw,
2922 struct ieee80211_vif *vif, int enable)
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01002923{
2924 struct mwl8k_cmd_bss_start *cmd;
2925 int rc;
2926
2927 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2928 if (cmd == NULL)
2929 return -ENOMEM;
2930
2931 cmd->header.code = cpu_to_le16(MWL8K_CMD_BSS_START);
2932 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2933 cmd->enable = cpu_to_le32(enable);
2934
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01002935 rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01002936 kfree(cmd);
2937
2938 return rc;
2939}
2940
2941/*
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01002942 * CMD_SET_NEW_STN.
2943 */
2944struct mwl8k_cmd_set_new_stn {
2945 struct mwl8k_cmd_pkt header;
2946 __le16 aid;
2947 __u8 mac_addr[6];
2948 __le16 stn_id;
2949 __le16 action;
2950 __le16 rsvd;
2951 __le32 legacy_rates;
2952 __u8 ht_rates[4];
2953 __le16 cap_info;
2954 __le16 ht_capabilities_info;
2955 __u8 mac_ht_param_info;
2956 __u8 rev;
2957 __u8 control_channel;
2958 __u8 add_channel;
2959 __le16 op_mode;
2960 __le16 stbc;
2961 __u8 add_qos_info;
2962 __u8 is_qos_sta;
2963 __le32 fw_sta_ptr;
Eric Dumazetba2d3582010-06-02 18:10:09 +00002964} __packed;
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01002965
2966#define MWL8K_STA_ACTION_ADD 0
2967#define MWL8K_STA_ACTION_REMOVE 2
2968
2969static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw,
2970 struct ieee80211_vif *vif,
2971 struct ieee80211_sta *sta)
2972{
2973 struct mwl8k_cmd_set_new_stn *cmd;
Lennert Buytenhek8707d022010-01-12 13:49:15 +01002974 u32 rates;
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01002975 int rc;
2976
2977 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2978 if (cmd == NULL)
2979 return -ENOMEM;
2980
2981 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
2982 cmd->header.length = cpu_to_le16(sizeof(*cmd));
2983 cmd->aid = cpu_to_le16(sta->aid);
2984 memcpy(cmd->mac_addr, sta->addr, ETH_ALEN);
2985 cmd->stn_id = cpu_to_le16(sta->aid);
2986 cmd->action = cpu_to_le16(MWL8K_STA_ACTION_ADD);
Lennert Buytenhek8707d022010-01-12 13:49:15 +01002987 if (hw->conf.channel->band == IEEE80211_BAND_2GHZ)
2988 rates = sta->supp_rates[IEEE80211_BAND_2GHZ];
2989 else
2990 rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5;
2991 cmd->legacy_rates = cpu_to_le32(rates);
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01002992 if (sta->ht_cap.ht_supported) {
2993 cmd->ht_rates[0] = sta->ht_cap.mcs.rx_mask[0];
2994 cmd->ht_rates[1] = sta->ht_cap.mcs.rx_mask[1];
2995 cmd->ht_rates[2] = sta->ht_cap.mcs.rx_mask[2];
2996 cmd->ht_rates[3] = sta->ht_cap.mcs.rx_mask[3];
2997 cmd->ht_capabilities_info = cpu_to_le16(sta->ht_cap.cap);
2998 cmd->mac_ht_param_info = (sta->ht_cap.ampdu_factor & 3) |
2999 ((sta->ht_cap.ampdu_density & 7) << 2);
3000 cmd->is_qos_sta = 1;
3001 }
3002
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01003003 rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003004 kfree(cmd);
3005
3006 return rc;
3007}
3008
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01003009static int mwl8k_cmd_set_new_stn_add_self(struct ieee80211_hw *hw,
3010 struct ieee80211_vif *vif)
3011{
3012 struct mwl8k_cmd_set_new_stn *cmd;
3013 int rc;
3014
3015 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3016 if (cmd == NULL)
3017 return -ENOMEM;
3018
3019 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
3020 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3021 memcpy(cmd->mac_addr, vif->addr, ETH_ALEN);
3022
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01003023 rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01003024 kfree(cmd);
3025
3026 return rc;
3027}
3028
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003029static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw,
3030 struct ieee80211_vif *vif, u8 *addr)
3031{
3032 struct mwl8k_cmd_set_new_stn *cmd;
3033 int rc;
3034
3035 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3036 if (cmd == NULL)
3037 return -ENOMEM;
3038
3039 cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
3040 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3041 memcpy(cmd->mac_addr, addr, ETH_ALEN);
3042 cmd->action = cpu_to_le16(MWL8K_STA_ACTION_REMOVE);
3043
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01003044 rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003045 kfree(cmd);
3046
3047 return rc;
3048}
3049
3050/*
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003051 * CMD_UPDATE_STADB.
3052 */
Lennert Buytenhek25d81b12010-01-04 21:54:41 +01003053struct ewc_ht_info {
3054 __le16 control1;
3055 __le16 control2;
3056 __le16 control3;
Eric Dumazetba2d3582010-06-02 18:10:09 +00003057} __packed;
Lennert Buytenhek25d81b12010-01-04 21:54:41 +01003058
3059struct peer_capability_info {
3060 /* Peer type - AP vs. STA. */
3061 __u8 peer_type;
3062
3063 /* Basic 802.11 capabilities from assoc resp. */
3064 __le16 basic_caps;
3065
3066 /* Set if peer supports 802.11n high throughput (HT). */
3067 __u8 ht_support;
3068
3069 /* Valid if HT is supported. */
3070 __le16 ht_caps;
3071 __u8 extended_ht_caps;
3072 struct ewc_ht_info ewc_info;
3073
3074 /* Legacy rate table. Intersection of our rates and peer rates. */
3075 __u8 legacy_rates[12];
3076
3077 /* HT rate table. Intersection of our rates and peer rates. */
3078 __u8 ht_rates[16];
3079 __u8 pad[16];
3080
3081 /* If set, interoperability mode, no proprietary extensions. */
3082 __u8 interop;
3083 __u8 pad2;
3084 __u8 station_id;
3085 __le16 amsdu_enabled;
Eric Dumazetba2d3582010-06-02 18:10:09 +00003086} __packed;
Lennert Buytenhek25d81b12010-01-04 21:54:41 +01003087
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003088struct mwl8k_cmd_update_stadb {
3089 struct mwl8k_cmd_pkt header;
3090
3091 /* See STADB_ACTION_TYPE */
3092 __le32 action;
3093
3094 /* Peer MAC address */
3095 __u8 peer_addr[ETH_ALEN];
3096
3097 __le32 reserved;
3098
3099 /* Peer info - valid during add/update. */
3100 struct peer_capability_info peer_info;
Eric Dumazetba2d3582010-06-02 18:10:09 +00003101} __packed;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003102
Lennert Buytenheka6804002010-01-04 21:55:42 +01003103#define MWL8K_STA_DB_MODIFY_ENTRY 1
3104#define MWL8K_STA_DB_DEL_ENTRY 2
3105
3106/* Peer Entry flags - used to define the type of the peer node */
3107#define MWL8K_PEER_TYPE_ACCESSPOINT 2
3108
3109static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw,
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01003110 struct ieee80211_vif *vif,
Lennert Buytenhek13935e22010-01-04 21:57:59 +01003111 struct ieee80211_sta *sta)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003112{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003113 struct mwl8k_cmd_update_stadb *cmd;
Lennert Buytenheka6804002010-01-04 21:55:42 +01003114 struct peer_capability_info *p;
Lennert Buytenhek8707d022010-01-12 13:49:15 +01003115 u32 rates;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003116 int rc;
3117
3118 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3119 if (cmd == NULL)
3120 return -ENOMEM;
3121
3122 cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB);
3123 cmd->header.length = cpu_to_le16(sizeof(*cmd));
Lennert Buytenheka6804002010-01-04 21:55:42 +01003124 cmd->action = cpu_to_le32(MWL8K_STA_DB_MODIFY_ENTRY);
Lennert Buytenhek13935e22010-01-04 21:57:59 +01003125 memcpy(cmd->peer_addr, sta->addr, ETH_ALEN);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003126
Lennert Buytenheka6804002010-01-04 21:55:42 +01003127 p = &cmd->peer_info;
3128 p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT;
3129 p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability);
Lennert Buytenhek13935e22010-01-04 21:57:59 +01003130 p->ht_support = sta->ht_cap.ht_supported;
John W. Linvilleb6037422010-07-20 13:55:00 -04003131 p->ht_caps = cpu_to_le16(sta->ht_cap.cap);
Lennert Buytenhek13935e22010-01-04 21:57:59 +01003132 p->extended_ht_caps = (sta->ht_cap.ampdu_factor & 3) |
3133 ((sta->ht_cap.ampdu_density & 7) << 2);
Lennert Buytenhek8707d022010-01-12 13:49:15 +01003134 if (hw->conf.channel->band == IEEE80211_BAND_2GHZ)
3135 rates = sta->supp_rates[IEEE80211_BAND_2GHZ];
3136 else
3137 rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5;
3138 legacy_rate_mask_to_array(p->legacy_rates, rates);
Lennert Buytenhek13935e22010-01-04 21:57:59 +01003139 memcpy(p->ht_rates, sta->ht_cap.mcs.rx_mask, 16);
Lennert Buytenheka6804002010-01-04 21:55:42 +01003140 p->interop = 1;
3141 p->amsdu_enabled = 0;
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003142
Lennert Buytenheka6804002010-01-04 21:55:42 +01003143 rc = mwl8k_post_cmd(hw, &cmd->header);
3144 kfree(cmd);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003145
Lennert Buytenheka6804002010-01-04 21:55:42 +01003146 return rc ? rc : p->station_id;
3147}
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003148
Lennert Buytenheka6804002010-01-04 21:55:42 +01003149static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw,
3150 struct ieee80211_vif *vif, u8 *addr)
3151{
3152 struct mwl8k_cmd_update_stadb *cmd;
3153 int rc;
3154
3155 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3156 if (cmd == NULL)
3157 return -ENOMEM;
3158
3159 cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB);
3160 cmd->header.length = cpu_to_le16(sizeof(*cmd));
3161 cmd->action = cpu_to_le32(MWL8K_STA_DB_DEL_ENTRY);
3162 memcpy(cmd->peer_addr, addr, ETH_ALEN);
3163
3164 rc = mwl8k_post_cmd(hw, &cmd->header);
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003165 kfree(cmd);
3166
3167 return rc;
3168}
3169
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003170
3171/*
3172 * Interrupt handling.
3173 */
3174static irqreturn_t mwl8k_interrupt(int irq, void *dev_id)
3175{
3176 struct ieee80211_hw *hw = dev_id;
3177 struct mwl8k_priv *priv = hw->priv;
3178 u32 status;
3179
3180 status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003181 if (!status)
3182 return IRQ_NONE;
3183
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +01003184 if (status & MWL8K_A2H_INT_TX_DONE) {
3185 status &= ~MWL8K_A2H_INT_TX_DONE;
3186 tasklet_schedule(&priv->poll_tx_task);
3187 }
3188
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01003189 if (status & MWL8K_A2H_INT_RX_READY) {
3190 status &= ~MWL8K_A2H_INT_RX_READY;
3191 tasklet_schedule(&priv->poll_rx_task);
3192 }
3193
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +01003194 if (status)
3195 iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003196
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003197 if (status & MWL8K_A2H_INT_OPC_DONE) {
Lennert Buytenhek618952a2009-08-18 03:18:01 +02003198 if (priv->hostcmd_wait != NULL)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003199 complete(priv->hostcmd_wait);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003200 }
3201
3202 if (status & MWL8K_A2H_INT_QUEUE_EMPTY) {
Lennert Buytenhek618952a2009-08-18 03:18:01 +02003203 if (!mutex_is_locked(&priv->fw_mutex) &&
Lennert Buytenhek88de754a2009-10-22 20:19:37 +02003204 priv->radio_on && priv->pending_tx_pkts)
Lennert Buytenhek618952a2009-08-18 03:18:01 +02003205 mwl8k_tx_start(priv);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003206 }
3207
3208 return IRQ_HANDLED;
3209}
3210
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +01003211static void mwl8k_tx_poll(unsigned long data)
3212{
3213 struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
3214 struct mwl8k_priv *priv = hw->priv;
3215 int limit;
3216 int i;
3217
3218 limit = 32;
3219
3220 spin_lock_bh(&priv->tx_lock);
3221
3222 for (i = 0; i < MWL8K_TX_QUEUES; i++)
3223 limit -= mwl8k_txq_reclaim(hw, i, limit, 0);
3224
3225 if (!priv->pending_tx_pkts && priv->tx_wait != NULL) {
3226 complete(priv->tx_wait);
3227 priv->tx_wait = NULL;
3228 }
3229
3230 spin_unlock_bh(&priv->tx_lock);
3231
3232 if (limit) {
3233 writel(~MWL8K_A2H_INT_TX_DONE,
3234 priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
3235 } else {
3236 tasklet_schedule(&priv->poll_tx_task);
3237 }
3238}
3239
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01003240static void mwl8k_rx_poll(unsigned long data)
3241{
3242 struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
3243 struct mwl8k_priv *priv = hw->priv;
3244 int limit;
3245
3246 limit = 32;
3247 limit -= rxq_process(hw, 0, limit);
3248 limit -= rxq_refill(hw, 0, limit);
3249
3250 if (limit) {
3251 writel(~MWL8K_A2H_INT_RX_READY,
3252 priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
3253 } else {
3254 tasklet_schedule(&priv->poll_rx_task);
3255 }
3256}
3257
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003258
3259/*
3260 * Core driver operations.
3261 */
3262static int mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
3263{
3264 struct mwl8k_priv *priv = hw->priv;
3265 int index = skb_get_queue_mapping(skb);
3266 int rc;
3267
Lennert Buytenhek9189c102010-01-12 13:47:32 +01003268 if (!priv->radio_on) {
Joe Perchesc96c31e2010-07-26 14:39:58 -07003269 wiphy_debug(hw->wiphy,
3270 "dropped TX frame since radio disabled\n");
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003271 dev_kfree_skb(skb);
3272 return NETDEV_TX_OK;
3273 }
3274
3275 rc = mwl8k_txq_xmit(hw, index, skb);
3276
3277 return rc;
3278}
3279
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003280static int mwl8k_start(struct ieee80211_hw *hw)
3281{
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003282 struct mwl8k_priv *priv = hw->priv;
3283 int rc;
3284
Joe Perchesa0607fd2009-11-18 23:29:17 -08003285 rc = request_irq(priv->pdev->irq, mwl8k_interrupt,
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003286 IRQF_SHARED, MWL8K_NAME, hw);
3287 if (rc) {
Joe Perches5db55842010-08-11 19:11:19 -07003288 wiphy_err(hw->wiphy, "failed to register IRQ handler\n");
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003289 return -EIO;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003290 }
3291
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01003292 /* Enable TX reclaim and RX tasklets. */
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +01003293 tasklet_enable(&priv->poll_tx_task);
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01003294 tasklet_enable(&priv->poll_rx_task);
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003295
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003296 /* Enable interrupts */
Lennert Buytenhekc23b5a62009-07-16 13:49:55 +02003297 iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003298
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003299 rc = mwl8k_fw_lock(hw);
3300 if (!rc) {
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003301 rc = mwl8k_cmd_radio_enable(hw);
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003302
Lennert Buytenhek5e4cf162009-10-22 20:21:38 +02003303 if (!priv->ap_fw) {
3304 if (!rc)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003305 rc = mwl8k_cmd_enable_sniffer(hw, 0);
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003306
Lennert Buytenhek5e4cf162009-10-22 20:21:38 +02003307 if (!rc)
3308 rc = mwl8k_cmd_set_pre_scan(hw);
3309
3310 if (!rc)
3311 rc = mwl8k_cmd_set_post_scan(hw,
3312 "\x00\x00\x00\x00\x00\x00");
3313 }
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003314
3315 if (!rc)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003316 rc = mwl8k_cmd_set_rateadapt_mode(hw, 0);
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003317
3318 if (!rc)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003319 rc = mwl8k_cmd_set_wmm_mode(hw, 0);
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003320
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003321 mwl8k_fw_unlock(hw);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003322 }
3323
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003324 if (rc) {
3325 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
3326 free_irq(priv->pdev->irq, hw);
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +01003327 tasklet_disable(&priv->poll_tx_task);
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01003328 tasklet_disable(&priv->poll_rx_task);
Lennert Buytenhek2ec610c2009-07-17 07:11:37 +02003329 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003330
3331 return rc;
3332}
3333
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003334static void mwl8k_stop(struct ieee80211_hw *hw)
3335{
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003336 struct mwl8k_priv *priv = hw->priv;
3337 int i;
3338
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003339 mwl8k_cmd_radio_disable(hw);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003340
3341 ieee80211_stop_queues(hw);
3342
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003343 /* Disable interrupts */
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003344 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003345 free_irq(priv->pdev->irq, hw);
3346
3347 /* Stop finalize join worker */
3348 cancel_work_sync(&priv->finalize_join_worker);
3349 if (priv->beacon_skb != NULL)
3350 dev_kfree_skb(priv->beacon_skb);
3351
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01003352 /* Stop TX reclaim and RX tasklets. */
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +01003353 tasklet_disable(&priv->poll_tx_task);
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01003354 tasklet_disable(&priv->poll_rx_task);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003355
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003356 /* Return all skbs to mac80211 */
3357 for (i = 0; i < MWL8K_TX_QUEUES; i++)
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01003358 mwl8k_txq_reclaim(hw, i, INT_MAX, 1);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003359}
3360
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08003361static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image);
3362
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003363static int mwl8k_add_interface(struct ieee80211_hw *hw,
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01003364 struct ieee80211_vif *vif)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003365{
3366 struct mwl8k_priv *priv = hw->priv;
3367 struct mwl8k_vif *mwl8k_vif;
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01003368 u32 macids_supported;
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08003369 int macid, rc;
3370 struct mwl8k_device_info *di;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003371
3372 /*
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02003373 * Reject interface creation if sniffer mode is active, as
3374 * STA operation is mutually exclusive with hardware sniffer
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01003375 * mode. (Sniffer mode is only used on STA firmware.)
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02003376 */
3377 if (priv->sniffer_enabled) {
Joe Perchesc96c31e2010-07-26 14:39:58 -07003378 wiphy_info(hw->wiphy,
3379 "unable to create STA interface because sniffer mode is enabled\n");
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02003380 return -EINVAL;
3381 }
3382
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08003383 di = priv->device_info;
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01003384 switch (vif->type) {
3385 case NL80211_IFTYPE_AP:
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08003386 if (!priv->ap_fw && di->fw_image_ap) {
3387 /* we must load the ap fw to meet this request */
3388 if (!list_empty(&priv->vif_list))
3389 return -EBUSY;
3390 rc = mwl8k_reload_firmware(hw, di->fw_image_ap);
3391 if (rc)
3392 return rc;
3393 }
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01003394 macids_supported = priv->ap_macids_supported;
3395 break;
3396 case NL80211_IFTYPE_STATION:
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08003397 if (priv->ap_fw && di->fw_image_sta) {
3398 /* we must load the sta fw to meet this request */
3399 if (!list_empty(&priv->vif_list))
3400 return -EBUSY;
3401 rc = mwl8k_reload_firmware(hw, di->fw_image_sta);
3402 if (rc)
3403 return rc;
3404 }
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01003405 macids_supported = priv->sta_macids_supported;
3406 break;
3407 default:
3408 return -EINVAL;
3409 }
3410
3411 macid = ffs(macids_supported & ~priv->macids_used);
3412 if (!macid--)
3413 return -EBUSY;
3414
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01003415 /* Setup driver private area. */
Johannes Berg1ed32e42009-12-23 13:15:45 +01003416 mwl8k_vif = MWL8K_VIF(vif);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003417 memset(mwl8k_vif, 0, sizeof(*mwl8k_vif));
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01003418 mwl8k_vif->vif = vif;
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01003419 mwl8k_vif->macid = macid;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003420 mwl8k_vif->seqno = 0;
3421
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01003422 /* Set the mac address. */
3423 mwl8k_cmd_set_mac_addr(hw, vif, vif->addr);
3424
3425 if (priv->ap_fw)
3426 mwl8k_cmd_set_new_stn_add_self(hw, vif);
3427
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01003428 priv->macids_used |= 1 << mwl8k_vif->macid;
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01003429 list_add_tail(&mwl8k_vif->list, &priv->vif_list);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003430
3431 return 0;
3432}
3433
3434static void mwl8k_remove_interface(struct ieee80211_hw *hw,
Johannes Berg1ed32e42009-12-23 13:15:45 +01003435 struct ieee80211_vif *vif)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003436{
3437 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01003438 struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003439
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01003440 if (priv->ap_fw)
3441 mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr);
3442
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01003443 mwl8k_cmd_set_mac_addr(hw, vif, "\x00\x00\x00\x00\x00\x00");
Lennert Buytenhek32060e12009-10-22 20:20:04 +02003444
Lennert Buytenhekee0ddf12010-01-12 13:51:30 +01003445 priv->macids_used &= ~(1 << mwl8k_vif->macid);
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01003446 list_del(&mwl8k_vif->list);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003447}
3448
Lennert Buytenhekee03a932009-07-17 07:19:37 +02003449static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003450{
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003451 struct ieee80211_conf *conf = &hw->conf;
3452 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenhekee03a932009-07-17 07:19:37 +02003453 int rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003454
Lennert Buytenhek7595d672009-08-17 23:59:40 +02003455 if (conf->flags & IEEE80211_CONF_IDLE) {
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003456 mwl8k_cmd_radio_disable(hw);
Lennert Buytenhekee03a932009-07-17 07:19:37 +02003457 return 0;
Lennert Buytenhek7595d672009-08-17 23:59:40 +02003458 }
3459
Lennert Buytenhekee03a932009-07-17 07:19:37 +02003460 rc = mwl8k_fw_lock(hw);
3461 if (rc)
3462 return rc;
3463
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003464 rc = mwl8k_cmd_radio_enable(hw);
Lennert Buytenhekee03a932009-07-17 07:19:37 +02003465 if (rc)
3466 goto out;
3467
Lennert Buytenhek610677d2010-01-04 21:56:46 +01003468 rc = mwl8k_cmd_set_rf_channel(hw, conf);
Lennert Buytenhekee03a932009-07-17 07:19:37 +02003469 if (rc)
3470 goto out;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003471
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003472 if (conf->power_level > 18)
3473 conf->power_level = 18;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003474
Lennert Buytenhek08b06342009-10-22 20:21:33 +02003475 if (priv->ap_fw) {
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -08003476 rc = mwl8k_cmd_tx_power(hw, conf, conf->power_level);
3477 if (rc)
3478 goto out;
3479
Lennert Buytenhek08b06342009-10-22 20:21:33 +02003480 rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x7);
3481 if (!rc)
3482 rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7);
3483 } else {
Nishant Sarmukadam41fdf092010-11-12 17:23:48 -08003484 rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level);
3485 if (rc)
3486 goto out;
Lennert Buytenhek08b06342009-10-22 20:21:33 +02003487 rc = mwl8k_cmd_mimo_config(hw, 0x7, 0x7);
3488 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003489
Lennert Buytenhekee03a932009-07-17 07:19:37 +02003490out:
3491 mwl8k_fw_unlock(hw);
3492
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003493 return rc;
3494}
3495
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01003496static void
3497mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
3498 struct ieee80211_bss_conf *info, u32 changed)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003499{
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003500 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003501 u32 ap_legacy_rates;
Lennert Buytenhek13935e22010-01-04 21:57:59 +01003502 u8 ap_mcs_rates[16];
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02003503 int rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003504
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003505 if (mwl8k_fw_lock(hw))
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02003506 return;
3507
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003508 /*
3509 * No need to capture a beacon if we're no longer associated.
3510 */
3511 if ((changed & BSS_CHANGED_ASSOC) && !vif->bss_conf.assoc)
3512 priv->capture_beacon = false;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003513
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003514 /*
Lennert Buytenhek13935e22010-01-04 21:57:59 +01003515 * Get the AP's legacy and MCS rates.
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003516 */
Lennert Buytenhek7dc6a7a2009-11-30 18:33:09 +01003517 if (vif->bss_conf.assoc) {
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01003518 struct ieee80211_sta *ap;
Lennert Buytenhekc97470d2010-01-12 13:47:37 +01003519
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01003520 rcu_read_lock();
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003521
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01003522 ap = ieee80211_find_sta(vif, vif->bss_conf.bssid);
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003523 if (ap == NULL) {
3524 rcu_read_unlock();
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01003525 goto out;
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003526 }
Lennert Buytenhekc6e96012010-01-04 21:55:52 +01003527
Lennert Buytenhek8707d022010-01-12 13:49:15 +01003528 if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) {
3529 ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ];
3530 } else {
3531 ap_legacy_rates =
3532 ap->supp_rates[IEEE80211_BAND_5GHZ] << 5;
3533 }
Lennert Buytenhek13935e22010-01-04 21:57:59 +01003534 memcpy(ap_mcs_rates, ap->ht_cap.mcs.rx_mask, 16);
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003535
3536 rcu_read_unlock();
3537 }
3538
3539 if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) {
Lennert Buytenhek13935e22010-01-04 21:57:59 +01003540 rc = mwl8k_cmd_set_rate(hw, vif, ap_legacy_rates, ap_mcs_rates);
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02003541 if (rc)
3542 goto out;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003543
Lennert Buytenhekb71ed2c2010-01-08 18:30:16 +01003544 rc = mwl8k_cmd_use_fixed_rate_sta(hw);
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02003545 if (rc)
3546 goto out;
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003547 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003548
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003549 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
Lennert Buytenhek7dc6a7a2009-11-30 18:33:09 +01003550 rc = mwl8k_set_radio_preamble(hw,
3551 vif->bss_conf.use_short_preamble);
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02003552 if (rc)
3553 goto out;
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003554 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003555
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003556 if (changed & BSS_CHANGED_ERP_SLOT) {
Lennert Buytenhek7dc6a7a2009-11-30 18:33:09 +01003557 rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot);
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02003558 if (rc)
3559 goto out;
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003560 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003561
Lennert Buytenhekc97470d2010-01-12 13:47:37 +01003562 if (vif->bss_conf.assoc &&
3563 (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT |
3564 BSS_CHANGED_HT))) {
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003565 rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates);
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02003566 if (rc)
3567 goto out;
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003568 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003569
Lennert Buytenhekc3cbbe82010-01-04 21:56:07 +01003570 if (vif->bss_conf.assoc &&
3571 (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INT))) {
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003572 /*
3573 * Finalize the join. Tell rx handler to process
3574 * next beacon from our BSSID.
3575 */
Lennert Buytenhek0a11dfc2010-01-04 21:55:21 +01003576 memcpy(priv->capture_bssid, vif->bss_conf.bssid, ETH_ALEN);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003577 priv->capture_beacon = true;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003578 }
3579
Lennert Buytenhek3a980d02009-07-17 07:21:46 +02003580out:
3581 mwl8k_fw_unlock(hw);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003582}
3583
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01003584static void
3585mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
3586 struct ieee80211_bss_conf *info, u32 changed)
3587{
3588 int rc;
3589
3590 if (mwl8k_fw_lock(hw))
3591 return;
3592
3593 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
3594 rc = mwl8k_set_radio_preamble(hw,
3595 vif->bss_conf.use_short_preamble);
3596 if (rc)
3597 goto out;
3598 }
3599
3600 if (changed & BSS_CHANGED_BASIC_RATES) {
3601 int idx;
3602 int rate;
3603
3604 /*
3605 * Use lowest supported basic rate for multicasts
3606 * and management frames (such as probe responses --
3607 * beacons will always go out at 1 Mb/s).
3608 */
3609 idx = ffs(vif->bss_conf.basic_rates);
Lennert Buytenhek8707d022010-01-12 13:49:15 +01003610 if (idx)
3611 idx--;
3612
3613 if (hw->conf.channel->band == IEEE80211_BAND_2GHZ)
3614 rate = mwl8k_rates_24[idx].hw_value;
3615 else
3616 rate = mwl8k_rates_50[idx].hw_value;
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01003617
3618 mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate);
3619 }
3620
3621 if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) {
3622 struct sk_buff *skb;
3623
3624 skb = ieee80211_beacon_get(hw, vif);
3625 if (skb != NULL) {
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01003626 mwl8k_cmd_set_beacon(hw, vif, skb->data, skb->len);
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01003627 kfree_skb(skb);
3628 }
3629 }
3630
3631 if (changed & BSS_CHANGED_BEACON_ENABLED)
Lennert Buytenhekaa21d0f2010-01-12 13:50:36 +01003632 mwl8k_cmd_bss_start(hw, vif, info->enable_beacon);
Lennert Buytenhekb64fe612010-01-08 18:31:39 +01003633
3634out:
3635 mwl8k_fw_unlock(hw);
3636}
3637
3638static void
3639mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
3640 struct ieee80211_bss_conf *info, u32 changed)
3641{
3642 struct mwl8k_priv *priv = hw->priv;
3643
3644 if (!priv->ap_fw)
3645 mwl8k_bss_info_changed_sta(hw, vif, info, changed);
3646 else
3647 mwl8k_bss_info_changed_ap(hw, vif, info, changed);
3648}
3649
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02003650static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw,
Jiri Pirko22bedad2010-04-01 21:22:57 +00003651 struct netdev_hw_addr_list *mc_list)
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02003652{
3653 struct mwl8k_cmd_pkt *cmd;
3654
Lennert Buytenhek447ced02009-10-22 20:19:50 +02003655 /*
3656 * Synthesize and return a command packet that programs the
3657 * hardware multicast address filter. At this point we don't
3658 * know whether FIF_ALLMULTI is being requested, but if it is,
3659 * we'll end up throwing this packet away and creating a new
3660 * one in mwl8k_configure_filter().
3661 */
Jiri Pirko22bedad2010-04-01 21:22:57 +00003662 cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_list);
Lennert Buytenheke81cd2d2009-08-18 03:55:42 +02003663
3664 return (unsigned long)cmd;
3665}
3666
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02003667static int
3668mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw,
3669 unsigned int changed_flags,
3670 unsigned int *total_flags)
3671{
3672 struct mwl8k_priv *priv = hw->priv;
3673
3674 /*
3675 * Hardware sniffer mode is mutually exclusive with STA
3676 * operation, so refuse to enable sniffer mode if a STA
3677 * interface is active.
3678 */
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01003679 if (!list_empty(&priv->vif_list)) {
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02003680 if (net_ratelimit())
Joe Perchesc96c31e2010-07-26 14:39:58 -07003681 wiphy_info(hw->wiphy,
3682 "not enabling sniffer mode because STA interface is active\n");
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02003683 return 0;
3684 }
3685
3686 if (!priv->sniffer_enabled) {
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003687 if (mwl8k_cmd_enable_sniffer(hw, 1))
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02003688 return 0;
3689 priv->sniffer_enabled = true;
3690 }
3691
3692 *total_flags &= FIF_PROMISC_IN_BSS | FIF_ALLMULTI |
3693 FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL |
3694 FIF_OTHER_BSS;
3695
3696 return 1;
3697}
3698
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01003699static struct mwl8k_vif *mwl8k_first_vif(struct mwl8k_priv *priv)
3700{
3701 if (!list_empty(&priv->vif_list))
3702 return list_entry(priv->vif_list.next, struct mwl8k_vif, list);
3703
3704 return NULL;
3705}
3706
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02003707static void mwl8k_configure_filter(struct ieee80211_hw *hw,
3708 unsigned int changed_flags,
3709 unsigned int *total_flags,
3710 u64 multicast)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003711{
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003712 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02003713 struct mwl8k_cmd_pkt *cmd = (void *)(unsigned long)multicast;
3714
3715 /*
Lennert Buytenhekc0adae22009-10-22 20:21:36 +02003716 * AP firmware doesn't allow fine-grained control over
3717 * the receive filter.
3718 */
3719 if (priv->ap_fw) {
3720 *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC;
3721 kfree(cmd);
3722 return;
3723 }
3724
3725 /*
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02003726 * Enable hardware sniffer mode if FIF_CONTROL or
3727 * FIF_OTHER_BSS is requested.
3728 */
3729 if (*total_flags & (FIF_CONTROL | FIF_OTHER_BSS) &&
3730 mwl8k_configure_filter_sniffer(hw, changed_flags, total_flags)) {
3731 kfree(cmd);
3732 return;
3733 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003734
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02003735 /* Clear unsupported feature flags */
Lennert Buytenhek447ced02009-10-22 20:19:50 +02003736 *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC;
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02003737
Lennert Buytenhek90852f72010-01-02 10:31:42 +01003738 if (mwl8k_fw_lock(hw)) {
3739 kfree(cmd);
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02003740 return;
Lennert Buytenhek90852f72010-01-02 10:31:42 +01003741 }
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02003742
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02003743 if (priv->sniffer_enabled) {
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003744 mwl8k_cmd_enable_sniffer(hw, 0);
Lennert Buytenheka43c49a2009-10-22 20:20:32 +02003745 priv->sniffer_enabled = false;
3746 }
3747
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02003748 if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
Lennert Buytenhek77165d82009-10-22 20:19:53 +02003749 if (*total_flags & FIF_BCN_PRBRESP_PROMISC) {
3750 /*
3751 * Disable the BSS filter.
3752 */
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02003753 mwl8k_cmd_set_pre_scan(hw);
Lennert Buytenhek77165d82009-10-22 20:19:53 +02003754 } else {
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01003755 struct mwl8k_vif *mwl8k_vif;
Lennert Buytenhek0a11dfc2010-01-04 21:55:21 +01003756 const u8 *bssid;
Lennert Buytenheka94cc972009-08-03 21:58:57 +02003757
Lennert Buytenhek77165d82009-10-22 20:19:53 +02003758 /*
3759 * Enable the BSS filter.
3760 *
3761 * If there is an active STA interface, use that
3762 * interface's BSSID, otherwise use a dummy one
3763 * (where the OUI part needs to be nonzero for
3764 * the BSSID to be accepted by POST_SCAN).
3765 */
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01003766 mwl8k_vif = mwl8k_first_vif(priv);
3767 if (mwl8k_vif != NULL)
3768 bssid = mwl8k_vif->vif->bss_conf.bssid;
3769 else
3770 bssid = "\x01\x00\x00\x00\x00\x00";
Lennert Buytenheka94cc972009-08-03 21:58:57 +02003771
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02003772 mwl8k_cmd_set_post_scan(hw, bssid);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003773 }
3774 }
3775
Lennert Buytenhek447ced02009-10-22 20:19:50 +02003776 /*
3777 * If FIF_ALLMULTI is being requested, throw away the command
3778 * packet that ->prepare_multicast() built and replace it with
3779 * a command packet that enables reception of all multicast
3780 * packets.
3781 */
3782 if (*total_flags & FIF_ALLMULTI) {
3783 kfree(cmd);
Jiri Pirko22bedad2010-04-01 21:22:57 +00003784 cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, NULL);
Lennert Buytenhek447ced02009-10-22 20:19:50 +02003785 }
3786
3787 if (cmd != NULL) {
3788 mwl8k_post_cmd(hw, cmd);
3789 kfree(cmd);
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02003790 }
Lennert Buytenhekce9e2e12009-07-16 14:00:45 +02003791
Lennert Buytenheke6935ea2009-08-18 04:06:20 +02003792 mwl8k_fw_unlock(hw);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003793}
3794
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003795static int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
3796{
Lennert Buytenhekc2c2b122010-01-08 18:27:59 +01003797 return mwl8k_cmd_set_rts_threshold(hw, value);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003798}
3799
Johannes Berg4a6967b2010-02-19 19:18:37 +01003800static int mwl8k_sta_remove(struct ieee80211_hw *hw,
3801 struct ieee80211_vif *vif,
3802 struct ieee80211_sta *sta)
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003803{
3804 struct mwl8k_priv *priv = hw->priv;
3805
Johannes Berg4a6967b2010-02-19 19:18:37 +01003806 if (priv->ap_fw)
3807 return mwl8k_cmd_set_new_stn_del(hw, vif, sta->addr);
3808 else
3809 return mwl8k_cmd_update_stadb_del(hw, vif, sta->addr);
3810}
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003811
Johannes Berg4a6967b2010-02-19 19:18:37 +01003812static int mwl8k_sta_add(struct ieee80211_hw *hw,
3813 struct ieee80211_vif *vif,
3814 struct ieee80211_sta *sta)
3815{
3816 struct mwl8k_priv *priv = hw->priv;
3817 int ret;
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003818
Johannes Berg4a6967b2010-02-19 19:18:37 +01003819 if (!priv->ap_fw) {
3820 ret = mwl8k_cmd_update_stadb_add(hw, vif, sta);
3821 if (ret >= 0) {
3822 MWL8K_STA(sta)->peer_id = ret;
3823 return 0;
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003824 }
Johannes Berg4a6967b2010-02-19 19:18:37 +01003825
3826 return ret;
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003827 }
Lennert Buytenhek3f5610f2010-01-08 18:30:58 +01003828
Johannes Berg4a6967b2010-02-19 19:18:37 +01003829 return mwl8k_cmd_set_new_stn_add(hw, vif, sta);
Lennert Buytenhekbbfd9122010-01-04 21:55:12 +01003830}
3831
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003832static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue,
3833 const struct ieee80211_tx_queue_params *params)
3834{
Lennert Buytenhek3e4f5422009-07-17 07:25:59 +02003835 struct mwl8k_priv *priv = hw->priv;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003836 int rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003837
Lennert Buytenhek3e4f5422009-07-17 07:25:59 +02003838 rc = mwl8k_fw_lock(hw);
3839 if (!rc) {
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08003840 BUG_ON(queue > MWL8K_TX_QUEUES - 1);
3841 memcpy(&priv->wmm_params[queue], params, sizeof(*params));
3842
Lennert Buytenhek3e4f5422009-07-17 07:25:59 +02003843 if (!priv->wmm_enabled)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003844 rc = mwl8k_cmd_set_wmm_mode(hw, 1);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003845
Lennert Buytenhek3e4f5422009-07-17 07:25:59 +02003846 if (!rc)
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003847 rc = mwl8k_cmd_set_edca_params(hw, queue,
3848 params->cw_min,
3849 params->cw_max,
3850 params->aifs,
3851 params->txop);
Lennert Buytenhek3e4f5422009-07-17 07:25:59 +02003852
3853 mwl8k_fw_unlock(hw);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003854 }
Lennert Buytenhek3e4f5422009-07-17 07:25:59 +02003855
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003856 return rc;
3857}
3858
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003859static int mwl8k_get_stats(struct ieee80211_hw *hw,
3860 struct ieee80211_low_level_stats *stats)
3861{
Lennert Buytenhek55489b62009-11-30 18:31:33 +01003862 return mwl8k_cmd_get_stat(hw, stats);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003863}
3864
John W. Linville0d462bb2010-07-28 14:04:24 -04003865static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx,
3866 struct survey_info *survey)
3867{
3868 struct mwl8k_priv *priv = hw->priv;
3869 struct ieee80211_conf *conf = &hw->conf;
3870
3871 if (idx != 0)
3872 return -ENOENT;
3873
3874 survey->channel = conf->channel;
3875 survey->filled = SURVEY_INFO_NOISE_DBM;
3876 survey->noise = priv->noise;
3877
3878 return 0;
3879}
3880
Lennert Buytenheka2292d82010-01-04 21:58:12 +01003881static int
3882mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
3883 enum ieee80211_ampdu_mlme_action action,
3884 struct ieee80211_sta *sta, u16 tid, u16 *ssn)
3885{
3886 switch (action) {
3887 case IEEE80211_AMPDU_RX_START:
3888 case IEEE80211_AMPDU_RX_STOP:
3889 if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION))
3890 return -ENOTSUPP;
3891 return 0;
3892 default:
3893 return -ENOTSUPP;
3894 }
3895}
3896
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003897static const struct ieee80211_ops mwl8k_ops = {
3898 .tx = mwl8k_tx,
3899 .start = mwl8k_start,
3900 .stop = mwl8k_stop,
3901 .add_interface = mwl8k_add_interface,
3902 .remove_interface = mwl8k_remove_interface,
3903 .config = mwl8k_config,
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003904 .bss_info_changed = mwl8k_bss_info_changed,
Johannes Berg3ac64be2009-08-17 16:16:53 +02003905 .prepare_multicast = mwl8k_prepare_multicast,
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003906 .configure_filter = mwl8k_configure_filter,
3907 .set_rts_threshold = mwl8k_set_rts_threshold,
Johannes Berg4a6967b2010-02-19 19:18:37 +01003908 .sta_add = mwl8k_sta_add,
3909 .sta_remove = mwl8k_sta_remove,
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003910 .conf_tx = mwl8k_conf_tx,
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003911 .get_stats = mwl8k_get_stats,
John W. Linville0d462bb2010-07-28 14:04:24 -04003912 .get_survey = mwl8k_get_survey,
Lennert Buytenheka2292d82010-01-04 21:58:12 +01003913 .ampdu_action = mwl8k_ampdu_action,
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003914};
3915
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003916static void mwl8k_finalize_join_worker(struct work_struct *work)
3917{
3918 struct mwl8k_priv *priv =
3919 container_of(work, struct mwl8k_priv, finalize_join_worker);
3920 struct sk_buff *skb = priv->beacon_skb;
Johannes Berg56007a02010-01-26 14:19:52 +01003921 struct ieee80211_mgmt *mgmt = (void *)skb->data;
3922 int len = skb->len - offsetof(struct ieee80211_mgmt, u.beacon.variable);
3923 const u8 *tim = cfg80211_find_ie(WLAN_EID_TIM,
3924 mgmt->u.beacon.variable, len);
3925 int dtim_period = 1;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003926
Johannes Berg56007a02010-01-26 14:19:52 +01003927 if (tim && tim[1] >= 2)
3928 dtim_period = tim[3];
3929
3930 mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len, dtim_period);
Lennert Buytenhekf5bb87c2010-01-12 13:49:51 +01003931
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003932 dev_kfree_skb(skb);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01003933 priv->beacon_skb = NULL;
3934}
3935
John W. Linvillebcb628d2009-11-06 16:40:16 -05003936enum {
Lennert Buytenhek9e1b17e2010-01-04 21:56:19 +01003937 MWL8363 = 0,
3938 MWL8687,
John W. Linvillebcb628d2009-11-06 16:40:16 -05003939 MWL8366,
Lennert Buytenhek6f6d1e92009-10-22 20:21:48 +02003940};
3941
John W. Linvillebcb628d2009-11-06 16:40:16 -05003942static struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = {
Lennert Buytenhek9e1b17e2010-01-04 21:56:19 +01003943 [MWL8363] = {
3944 .part_name = "88w8363",
3945 .helper_image = "mwl8k/helper_8363.fw",
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08003946 .fw_image_sta = "mwl8k/fmimage_8363.fw",
Lennert Buytenhek9e1b17e2010-01-04 21:56:19 +01003947 },
Lennert Buytenhek49eb6912009-11-30 18:32:13 +01003948 [MWL8687] = {
John W. Linvillebcb628d2009-11-06 16:40:16 -05003949 .part_name = "88w8687",
3950 .helper_image = "mwl8k/helper_8687.fw",
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08003951 .fw_image_sta = "mwl8k/fmimage_8687.fw",
John W. Linvillebcb628d2009-11-06 16:40:16 -05003952 },
Lennert Buytenhek49eb6912009-11-30 18:32:13 +01003953 [MWL8366] = {
John W. Linvillebcb628d2009-11-06 16:40:16 -05003954 .part_name = "88w8366",
3955 .helper_image = "mwl8k/helper_8366.fw",
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08003956 .fw_image_sta = "mwl8k/fmimage_8366.fw",
3957 .fw_image_ap = "mwl8k/fmimage_8366_ap-1.fw",
Lennert Buytenhek89a91f42009-11-30 18:32:54 +01003958 .ap_rxd_ops = &rxd_8366_ap_ops,
John W. Linvillebcb628d2009-11-06 16:40:16 -05003959 },
Lennert Buytenhek45a390d2009-10-22 20:20:47 +02003960};
3961
Lennert Buytenhekc92d4ed2010-01-12 13:47:22 +01003962MODULE_FIRMWARE("mwl8k/helper_8363.fw");
3963MODULE_FIRMWARE("mwl8k/fmimage_8363.fw");
3964MODULE_FIRMWARE("mwl8k/helper_8687.fw");
3965MODULE_FIRMWARE("mwl8k/fmimage_8687.fw");
3966MODULE_FIRMWARE("mwl8k/helper_8366.fw");
3967MODULE_FIRMWARE("mwl8k/fmimage_8366.fw");
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08003968MODULE_FIRMWARE("mwl8k/fmimage_8366_ap-1.fw");
Lennert Buytenhekc92d4ed2010-01-12 13:47:22 +01003969
Lennert Buytenhek45a390d2009-10-22 20:20:47 +02003970static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = {
Benjamin Larssone5868ba2010-03-19 01:46:10 +01003971 { PCI_VDEVICE(MARVELL, 0x2a0a), .driver_data = MWL8363, },
Lennert Buytenhek9e1b17e2010-01-04 21:56:19 +01003972 { PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, },
3973 { PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, },
John W. Linvillebcb628d2009-11-06 16:40:16 -05003974 { PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, },
3975 { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, },
3976 { PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, },
Lennert Buytenhekca665272010-01-12 13:47:53 +01003977 { PCI_VDEVICE(MARVELL, 0x2a43), .driver_data = MWL8366, },
John W. Linvillebcb628d2009-11-06 16:40:16 -05003978 { },
Lennert Buytenhek45a390d2009-10-22 20:20:47 +02003979};
3980MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table);
3981
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08003982static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image)
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08003983{
3984 struct mwl8k_priv *priv = hw->priv;
3985 int rc;
3986
3987 /* Reset firmware and hardware */
3988 mwl8k_hw_reset(priv);
3989
3990 /* Ask userland hotplug daemon for the device firmware */
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08003991 rc = mwl8k_request_firmware(priv, fw_image);
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08003992 if (rc) {
3993 wiphy_err(hw->wiphy, "Firmware files not found\n");
3994 return rc;
3995 }
3996
3997 /* Load firmware into hardware */
3998 rc = mwl8k_load_firmware(hw);
3999 if (rc)
4000 wiphy_err(hw->wiphy, "Cannot start firmware\n");
4001
4002 /* Reclaim memory once firmware is successfully loaded */
4003 mwl8k_release_firmware(priv);
4004
4005 return rc;
4006}
4007
4008/* initialize hw after successfully loading a firmware image */
4009static int mwl8k_probe_hw(struct ieee80211_hw *hw)
4010{
4011 struct mwl8k_priv *priv = hw->priv;
4012 int rc = 0;
4013 int i;
4014
4015 if (priv->ap_fw) {
4016 priv->rxd_ops = priv->device_info->ap_rxd_ops;
4017 if (priv->rxd_ops == NULL) {
4018 wiphy_err(hw->wiphy,
4019 "Driver does not have AP firmware image support for this hardware\n");
4020 goto err_stop_firmware;
4021 }
4022 } else {
4023 priv->rxd_ops = &rxd_sta_ops;
4024 }
4025
4026 priv->sniffer_enabled = false;
4027 priv->wmm_enabled = false;
4028 priv->pending_tx_pkts = 0;
4029
4030 rc = mwl8k_rxq_init(hw, 0);
4031 if (rc)
4032 goto err_stop_firmware;
4033 rxq_refill(hw, 0, INT_MAX);
4034
4035 for (i = 0; i < MWL8K_TX_QUEUES; i++) {
4036 rc = mwl8k_txq_init(hw, i);
4037 if (rc)
4038 goto err_free_queues;
4039 }
4040
4041 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
4042 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
4043 iowrite32(MWL8K_A2H_INT_TX_DONE | MWL8K_A2H_INT_RX_READY,
4044 priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL);
4045 iowrite32(0xffffffff, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
4046
4047 rc = request_irq(priv->pdev->irq, mwl8k_interrupt,
4048 IRQF_SHARED, MWL8K_NAME, hw);
4049 if (rc) {
4050 wiphy_err(hw->wiphy, "failed to register IRQ handler\n");
4051 goto err_free_queues;
4052 }
4053
4054 /*
4055 * Temporarily enable interrupts. Initial firmware host
4056 * commands use interrupts and avoid polling. Disable
4057 * interrupts when done.
4058 */
4059 iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
4060
4061 /* Get config data, mac addrs etc */
4062 if (priv->ap_fw) {
4063 rc = mwl8k_cmd_get_hw_spec_ap(hw);
4064 if (!rc)
4065 rc = mwl8k_cmd_set_hw_spec(hw);
4066 } else {
4067 rc = mwl8k_cmd_get_hw_spec_sta(hw);
4068 }
4069 if (rc) {
4070 wiphy_err(hw->wiphy, "Cannot initialise firmware\n");
4071 goto err_free_irq;
4072 }
4073
4074 /* Turn radio off */
4075 rc = mwl8k_cmd_radio_disable(hw);
4076 if (rc) {
4077 wiphy_err(hw->wiphy, "Cannot disable\n");
4078 goto err_free_irq;
4079 }
4080
4081 /* Clear MAC address */
4082 rc = mwl8k_cmd_set_mac_addr(hw, NULL, "\x00\x00\x00\x00\x00\x00");
4083 if (rc) {
4084 wiphy_err(hw->wiphy, "Cannot clear MAC address\n");
4085 goto err_free_irq;
4086 }
4087
4088 /* Disable interrupts */
4089 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
4090 free_irq(priv->pdev->irq, hw);
4091
4092 wiphy_info(hw->wiphy, "%s v%d, %pm, %s firmware %u.%u.%u.%u\n",
4093 priv->device_info->part_name,
4094 priv->hw_rev, hw->wiphy->perm_addr,
4095 priv->ap_fw ? "AP" : "STA",
4096 (priv->fw_rev >> 24) & 0xff, (priv->fw_rev >> 16) & 0xff,
4097 (priv->fw_rev >> 8) & 0xff, priv->fw_rev & 0xff);
4098
4099 return 0;
4100
4101err_free_irq:
4102 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
4103 free_irq(priv->pdev->irq, hw);
4104
4105err_free_queues:
4106 for (i = 0; i < MWL8K_TX_QUEUES; i++)
4107 mwl8k_txq_deinit(hw, i);
4108 mwl8k_rxq_deinit(hw, 0);
4109
4110err_stop_firmware:
4111 mwl8k_hw_reset(priv);
4112
4113 return rc;
4114}
4115
4116/*
4117 * invoke mwl8k_reload_firmware to change the firmware image after the device
4118 * has already been registered
4119 */
4120static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)
4121{
4122 int i, rc = 0;
4123 struct mwl8k_priv *priv = hw->priv;
4124
4125 mwl8k_stop(hw);
4126 mwl8k_rxq_deinit(hw, 0);
4127
4128 for (i = 0; i < MWL8K_TX_QUEUES; i++)
4129 mwl8k_txq_deinit(hw, i);
4130
4131 rc = mwl8k_init_firmware(hw, fw_image);
4132 if (rc)
4133 goto fail;
4134
4135 rc = mwl8k_probe_hw(hw);
4136 if (rc)
4137 goto fail;
4138
4139 rc = mwl8k_start(hw);
4140 if (rc)
4141 goto fail;
4142
4143 rc = mwl8k_config(hw, ~0);
4144 if (rc)
4145 goto fail;
4146
4147 for (i = 0; i < MWL8K_TX_QUEUES; i++) {
4148 rc = mwl8k_conf_tx(hw, i, &priv->wmm_params[i]);
4149 if (rc)
4150 goto fail;
4151 }
4152
4153 return rc;
4154
4155fail:
4156 printk(KERN_WARNING "mwl8k: Failed to reload firmware image.\n");
4157 return rc;
4158}
4159
4160static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
4161{
4162 struct ieee80211_hw *hw = priv->hw;
4163 int i, rc;
4164
4165 /*
4166 * Extra headroom is the size of the required DMA header
4167 * minus the size of the smallest 802.11 frame (CTS frame).
4168 */
4169 hw->extra_tx_headroom =
4170 sizeof(struct mwl8k_dma_data) - sizeof(struct ieee80211_cts);
4171
4172 hw->channel_change_time = 10;
4173
4174 hw->queues = MWL8K_TX_QUEUES;
4175
4176 /* Set rssi values to dBm */
4177 hw->flags |= IEEE80211_HW_SIGNAL_DBM;
4178 hw->vif_data_size = sizeof(struct mwl8k_vif);
4179 hw->sta_data_size = sizeof(struct mwl8k_sta);
4180
4181 priv->macids_used = 0;
4182 INIT_LIST_HEAD(&priv->vif_list);
4183
4184 /* Set default radio state and preamble */
4185 priv->radio_on = 0;
4186 priv->radio_short_preamble = 0;
4187
4188 /* Finalize join worker */
4189 INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker);
4190
4191 /* TX reclaim and RX tasklets. */
4192 tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw);
4193 tasklet_disable(&priv->poll_tx_task);
4194 tasklet_init(&priv->poll_rx_task, mwl8k_rx_poll, (unsigned long)hw);
4195 tasklet_disable(&priv->poll_rx_task);
4196
4197 /* Power management cookie */
4198 priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma);
4199 if (priv->cookie == NULL)
4200 return -ENOMEM;
4201
4202 mutex_init(&priv->fw_mutex);
4203 priv->fw_mutex_owner = NULL;
4204 priv->fw_mutex_depth = 0;
4205 priv->hostcmd_wait = NULL;
4206
4207 spin_lock_init(&priv->tx_lock);
4208
4209 priv->tx_wait = NULL;
4210
4211 rc = mwl8k_probe_hw(hw);
4212 if (rc)
4213 goto err_free_cookie;
4214
4215 hw->wiphy->interface_modes = 0;
4216 if (priv->ap_macids_supported || priv->device_info->fw_image_ap)
4217 hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP);
4218 if (priv->sta_macids_supported || priv->device_info->fw_image_sta)
4219 hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
4220
4221 rc = ieee80211_register_hw(hw);
4222 if (rc) {
4223 wiphy_err(hw->wiphy, "Cannot register device\n");
4224 goto err_unprobe_hw;
4225 }
4226
4227 return 0;
4228
4229err_unprobe_hw:
4230 for (i = 0; i < MWL8K_TX_QUEUES; i++)
4231 mwl8k_txq_deinit(hw, i);
4232 mwl8k_rxq_deinit(hw, 0);
4233
4234err_free_cookie:
4235 if (priv->cookie != NULL)
4236 pci_free_consistent(priv->pdev, 4,
4237 priv->cookie, priv->cookie_dma);
4238
4239 return rc;
4240}
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004241static int __devinit mwl8k_probe(struct pci_dev *pdev,
4242 const struct pci_device_id *id)
4243{
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004244 static int printed_version;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004245 struct ieee80211_hw *hw;
4246 struct mwl8k_priv *priv;
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08004247 struct mwl8k_device_info *di;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004248 int rc;
Lennert Buytenhek2aa7b012009-08-24 15:48:07 +02004249
4250 if (!printed_version) {
4251 printk(KERN_INFO "%s version %s\n", MWL8K_DESC, MWL8K_VERSION);
4252 printed_version = 1;
4253 }
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004254
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +01004255
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004256 rc = pci_enable_device(pdev);
4257 if (rc) {
4258 printk(KERN_ERR "%s: Cannot enable new PCI device\n",
4259 MWL8K_NAME);
4260 return rc;
4261 }
4262
4263 rc = pci_request_regions(pdev, MWL8K_NAME);
4264 if (rc) {
4265 printk(KERN_ERR "%s: Cannot obtain PCI resources\n",
4266 MWL8K_NAME);
Lennert Buytenhek3db95e52009-11-30 18:13:34 +01004267 goto err_disable_device;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004268 }
4269
4270 pci_set_master(pdev);
4271
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +01004272
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004273 hw = ieee80211_alloc_hw(sizeof(*priv), &mwl8k_ops);
4274 if (hw == NULL) {
4275 printk(KERN_ERR "%s: ieee80211 alloc failed\n", MWL8K_NAME);
4276 rc = -ENOMEM;
4277 goto err_free_reg;
4278 }
4279
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +01004280 SET_IEEE80211_DEV(hw, &pdev->dev);
4281 pci_set_drvdata(pdev, hw);
4282
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004283 priv = hw->priv;
4284 priv->hw = hw;
4285 priv->pdev = pdev;
John W. Linvillebcb628d2009-11-06 16:40:16 -05004286 priv->device_info = &mwl8k_info_tbl[id->driver_data];
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004287
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004288
Lennert Buytenhek5b9482d2009-10-22 20:20:43 +02004289 priv->sram = pci_iomap(pdev, 0, 0x10000);
4290 if (priv->sram == NULL) {
Joe Perches5db55842010-08-11 19:11:19 -07004291 wiphy_err(hw->wiphy, "Cannot map device SRAM\n");
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004292 goto err_iounmap;
4293 }
4294
Lennert Buytenhek5b9482d2009-10-22 20:20:43 +02004295 /*
4296 * If BAR0 is a 32 bit BAR, the register BAR will be BAR1.
4297 * If BAR0 is a 64 bit BAR, the register BAR will be BAR2.
4298 */
4299 priv->regs = pci_iomap(pdev, 1, 0x10000);
4300 if (priv->regs == NULL) {
4301 priv->regs = pci_iomap(pdev, 2, 0x10000);
4302 if (priv->regs == NULL) {
Joe Perches5db55842010-08-11 19:11:19 -07004303 wiphy_err(hw->wiphy, "Cannot map device registers\n");
Lennert Buytenhek5b9482d2009-10-22 20:20:43 +02004304 goto err_iounmap;
4305 }
4306 }
4307
Brian Cavagnolo0863ade2010-11-12 17:23:50 -08004308 /*
4309 * Choose the initial fw image depending on user input and availability
4310 * of images.
4311 */
4312 di = priv->device_info;
4313 if (ap_mode_default && di->fw_image_ap)
4314 rc = mwl8k_init_firmware(hw, di->fw_image_ap);
4315 else if (!ap_mode_default && di->fw_image_sta)
4316 rc = mwl8k_init_firmware(hw, di->fw_image_sta);
4317 else if (ap_mode_default && !di->fw_image_ap && di->fw_image_sta) {
4318 printk(KERN_WARNING "AP fw is unavailable. Using STA fw.");
4319 rc = mwl8k_init_firmware(hw, di->fw_image_sta);
4320 } else if (!ap_mode_default && !di->fw_image_sta && di->fw_image_ap) {
4321 printk(KERN_WARNING "STA fw is unavailable. Using AP fw.");
4322 rc = mwl8k_init_firmware(hw, di->fw_image_ap);
4323 } else
4324 rc = mwl8k_init_firmware(hw, di->fw_image_sta);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004325 if (rc)
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004326 goto err_stop_firmware;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004327
Brian Cavagnolo3cc77722010-11-12 17:23:49 -08004328 rc = mwl8k_firmware_load_success(priv);
4329 if (!rc)
4330 return rc;
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004331
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +01004332err_stop_firmware:
4333 mwl8k_hw_reset(priv);
Lennert Buytenhekbe695fc2009-11-30 18:32:46 +01004334
4335err_iounmap:
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004336 if (priv->regs != NULL)
4337 pci_iounmap(pdev, priv->regs);
4338
Lennert Buytenhek5b9482d2009-10-22 20:20:43 +02004339 if (priv->sram != NULL)
4340 pci_iounmap(pdev, priv->sram);
4341
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004342 pci_set_drvdata(pdev, NULL);
4343 ieee80211_free_hw(hw);
4344
4345err_free_reg:
4346 pci_release_regions(pdev);
Lennert Buytenhek3db95e52009-11-30 18:13:34 +01004347
4348err_disable_device:
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004349 pci_disable_device(pdev);
4350
4351 return rc;
4352}
4353
Joerg Albert230f7af2009-04-18 02:10:45 +02004354static void __devexit mwl8k_shutdown(struct pci_dev *pdev)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004355{
4356 printk(KERN_ERR "===>%s(%u)\n", __func__, __LINE__);
4357}
4358
Joerg Albert230f7af2009-04-18 02:10:45 +02004359static void __devexit mwl8k_remove(struct pci_dev *pdev)
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004360{
4361 struct ieee80211_hw *hw = pci_get_drvdata(pdev);
4362 struct mwl8k_priv *priv;
4363 int i;
4364
4365 if (hw == NULL)
4366 return;
4367 priv = hw->priv;
4368
4369 ieee80211_stop_queues(hw);
4370
Lennert Buytenhek60aa5692009-08-03 21:59:09 +02004371 ieee80211_unregister_hw(hw);
4372
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01004373 /* Remove TX reclaim and RX tasklets. */
Lennert Buytenhek1e9f9de32010-01-08 18:32:01 +01004374 tasklet_kill(&priv->poll_tx_task);
Lennert Buytenhek67e2eb22010-01-08 18:32:18 +01004375 tasklet_kill(&priv->poll_rx_task);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004376
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004377 /* Stop hardware */
4378 mwl8k_hw_reset(priv);
4379
4380 /* Return all skbs to mac80211 */
4381 for (i = 0; i < MWL8K_TX_QUEUES; i++)
Lennert Buytenhekefb7c492010-01-08 18:31:47 +01004382 mwl8k_txq_reclaim(hw, i, INT_MAX, 1);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004383
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004384 for (i = 0; i < MWL8K_TX_QUEUES; i++)
4385 mwl8k_txq_deinit(hw, i);
4386
4387 mwl8k_rxq_deinit(hw, 0);
4388
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +02004389 pci_free_consistent(priv->pdev, 4, priv->cookie, priv->cookie_dma);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004390
4391 pci_iounmap(pdev, priv->regs);
Lennert Buytenhek5b9482d2009-10-22 20:20:43 +02004392 pci_iounmap(pdev, priv->sram);
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004393 pci_set_drvdata(pdev, NULL);
4394 ieee80211_free_hw(hw);
4395 pci_release_regions(pdev);
4396 pci_disable_device(pdev);
4397}
4398
4399static struct pci_driver mwl8k_driver = {
4400 .name = MWL8K_NAME,
Lennert Buytenhek45a390d2009-10-22 20:20:47 +02004401 .id_table = mwl8k_pci_id_table,
Lennert Buytenheka66098d2009-03-10 10:13:33 +01004402 .probe = mwl8k_probe,
4403 .remove = __devexit_p(mwl8k_remove),
4404 .shutdown = __devexit_p(mwl8k_shutdown),
4405};
4406
4407static int __init mwl8k_init(void)
4408{
4409 return pci_register_driver(&mwl8k_driver);
4410}
4411
4412static void __exit mwl8k_exit(void)
4413{
4414 pci_unregister_driver(&mwl8k_driver);
4415}
4416
4417module_init(mwl8k_init);
4418module_exit(mwl8k_exit);
Lennert Buytenhekc2c357c2009-10-22 20:19:33 +02004419
4420MODULE_DESCRIPTION(MWL8K_DESC);
4421MODULE_VERSION(MWL8K_VERSION);
4422MODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com>");
4423MODULE_LICENSE("GPL");