mwl8k.c revision 0bf22c3751d19f9be20205c0e7112723618a4858
1a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
2ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek * drivers/net/wireless/mwl8k.c
3ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek * Driver for Marvell TOPDOG 802.11 Wireless cards
4a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *
5a5fb297d634ba20bd53a7d6fecd611bbfd342e78Lennert Buytenhek * Copyright (C) 2008, 2009, 2010 Marvell Semiconductor Inc.
6a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *
7a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * This file is licensed under the terms of the GNU General Public
8a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * License version 2.  This program is licensed "as is" without any
9a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * warranty of any kind, whether express or implied.
10a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
11a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
12a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#include <linux/init.h>
13a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#include <linux/module.h>
14a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#include <linux/kernel.h>
153d76e82c9538d8104e578ca460d35f214bfddfd3Lennert Buytenhek#include <linux/sched.h>
16a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#include <linux/spinlock.h>
17a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#include <linux/list.h>
18a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#include <linux/pci.h>
19a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#include <linux/delay.h>
20a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#include <linux/completion.h>
21a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#include <linux/etherdevice.h>
225a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
23a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#include <net/mac80211.h>
24a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#include <linux/moduleparam.h>
25a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#include <linux/firmware.h>
26a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#include <linux/workqueue.h>
27a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
28a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_DESC	"Marvell TOPDOG(R) 802.11 Wireless Network Driver"
29a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_NAME	KBUILD_MODNAME
30a5fb297d634ba20bd53a7d6fecd611bbfd342e78Lennert Buytenhek#define MWL8K_VERSION	"0.12"
31a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
320863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo/* Module parameters */
330863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolostatic unsigned ap_mode_default;
340863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolomodule_param(ap_mode_default, bool, 0);
350863ade8d6bde1d151f75720d999ff27f9fe3533Brian CavagnoloMODULE_PARM_DESC(ap_mode_default,
360863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo		 "Set to 1 to make ap mode the default instead of sta mode");
370863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo
38a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/* Register definitions */
39a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_HIU_GEN_PTR			0x00000c10
40ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_MODE_STA				 0x0000005a
41ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_MODE_AP				 0x000000a5
42a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_HIU_INT_CODE			0x00000c14
43ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_FWSTA_READY			 0xf0f1f2f4
44ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_FWAP_READY			 0xf1f2f4a5
45ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_INT_CODE_CMD_FINISHED		 0x00000005
46a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_HIU_SCRATCH			0x00000c40
47a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
48a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/* Host->device communications */
49a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_HIU_H2A_INTERRUPT_EVENTS		0x00000c18
50a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_HIU_H2A_INTERRUPT_STATUS		0x00000c1c
51a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_HIU_H2A_INTERRUPT_MASK		0x00000c20
52a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_HIU_H2A_INTERRUPT_CLEAR_SEL	0x00000c24
53a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_HIU_H2A_INTERRUPT_STATUS_MASK	0x00000c28
54ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_H2A_INT_DUMMY			 (1 << 20)
55ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_H2A_INT_RESET			 (1 << 15)
56ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_H2A_INT_DOORBELL			 (1 << 1)
57ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_H2A_INT_PPA_READY		 (1 << 0)
58a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
59a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/* Device->host communications */
60a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_HIU_A2H_INTERRUPT_EVENTS		0x00000c2c
61a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_HIU_A2H_INTERRUPT_STATUS		0x00000c30
62a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_HIU_A2H_INTERRUPT_MASK		0x00000c34
63a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL	0x00000c38
64a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK	0x00000c3c
65ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_A2H_INT_DUMMY			 (1 << 20)
66ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_A2H_INT_CHNL_SWITCHED		 (1 << 11)
67ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_A2H_INT_QUEUE_EMPTY		 (1 << 10)
68ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_A2H_INT_RADAR_DETECT		 (1 << 7)
69ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_A2H_INT_RADIO_ON			 (1 << 6)
70ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_A2H_INT_RADIO_OFF		 (1 << 5)
71ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_A2H_INT_MAC_EVENT		 (1 << 3)
72ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_A2H_INT_OPC_DONE			 (1 << 2)
73ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_A2H_INT_RX_READY			 (1 << 1)
74ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#define  MWL8K_A2H_INT_TX_DONE			 (1 << 0)
75a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
76a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_A2H_EVENTS	(MWL8K_A2H_INT_DUMMY | \
77a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				 MWL8K_A2H_INT_CHNL_SWITCHED | \
78a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				 MWL8K_A2H_INT_QUEUE_EMPTY | \
79a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				 MWL8K_A2H_INT_RADAR_DETECT | \
80a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				 MWL8K_A2H_INT_RADIO_ON | \
81a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				 MWL8K_A2H_INT_RADIO_OFF | \
82a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				 MWL8K_A2H_INT_MAC_EVENT | \
83a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				 MWL8K_A2H_INT_OPC_DONE | \
84a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				 MWL8K_A2H_INT_RX_READY | \
85a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				 MWL8K_A2H_INT_TX_DONE)
86a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
87a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_RX_QUEUES		1
88a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_TX_QUEUES		4
89a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
9054bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhekstruct rxd_ops {
9154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	int rxd_size;
9254bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr);
9354bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	void (*rxd_refill)(void *rxd, dma_addr_t addr, int len);
9420f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	int (*rxd_process)(void *rxd, struct ieee80211_rx_status *status,
950d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville			   __le16 *qos, s8 *noise);
9654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek};
9754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
9845a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhekstruct mwl8k_device_info {
99a74b295edb3e2b39d6c03255b24dca862a843c59Lennert Buytenhek	char *part_name;
100a74b295edb3e2b39d6c03255b24dca862a843c59Lennert Buytenhek	char *helper_image;
1010863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	char *fw_image_sta;
1020863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	char *fw_image_ap;
10389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	struct rxd_ops *ap_rxd_ops;
104952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo	u32 fw_api_ap;
10545a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhek};
10645a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhek
107a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstruct mwl8k_rx_queue {
10845eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	int rxd_count;
109a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
110a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* hw receives here */
11145eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	int head;
112a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
113a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* refill descs here */
11445eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	int tail;
115a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
11654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	void *rxd;
11745eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	dma_addr_t rxd_dma;
118788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek	struct {
119788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek		struct sk_buff *skb;
12053b1b3e1f0feaa57b82d3dc344d887fe3eecc90bFUJITA Tomonori		DEFINE_DMA_UNMAP_ADDR(dma);
121788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek	} *buf;
122a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek};
123a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
124a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstruct mwl8k_tx_queue {
125a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* hw transmits here */
12645eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	int head;
127a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
128a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* sw appends here */
12945eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	int tail;
130a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1318ccbc3b8b0c919e8609560ca56cd777ece8d2c41Kalle Valo	unsigned int len;
13245eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	struct mwl8k_tx_desc *txd;
13345eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	dma_addr_t txd_dma;
13445eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	struct sk_buff **skb;
135a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek};
136a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
137a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstruct mwl8k_priv {
138a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct ieee80211_hw *hw;
139a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct pci_dev *pdev;
140a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
14145a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhek	struct mwl8k_device_info *device_info;
14245a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhek
143be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	void __iomem *sram;
144be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	void __iomem *regs;
145be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek
146be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	/* firmware */
147d1f9e41d1d739cd4393840d35e7554f4a439a4f1Brian Cavagnolo	const struct firmware *fw_helper;
148d1f9e41d1d739cd4393840d35e7554f4a439a4f1Brian Cavagnolo	const struct firmware *fw_ucode;
149a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
150be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	/* hardware/firmware parameters */
151be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	bool ap_fw;
152be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	struct rxd_ops *rxd_ops;
153777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek	struct ieee80211_supported_band band_24;
154777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek	struct ieee80211_channel channels_24[14];
155777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek	struct ieee80211_rate rates_24[14];
1564eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	struct ieee80211_supported_band band_50;
1574eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	struct ieee80211_channel channels_50[4];
1584eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	struct ieee80211_rate rates_50[9];
159ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	u32 ap_macids_supported;
160ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	u32 sta_macids_supported;
161be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek
162618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	/* firmware access */
163618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	struct mutex fw_mutex;
164618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	struct task_struct *fw_mutex_owner;
165618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	int fw_mutex_depth;
166618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	struct completion *hostcmd_wait;
167618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
168a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* lock held over TX and TX reap */
169a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	spinlock_t tx_lock;
170a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
17188de754ad59025eba797e7a8375807755577f450Lennert Buytenhek	/* TX quiesce completion, protected by fw_mutex and tx_lock */
17288de754ad59025eba797e7a8375807755577f450Lennert Buytenhek	struct completion *tx_wait;
17388de754ad59025eba797e7a8375807755577f450Lennert Buytenhek
174f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek	/* List of interfaces.  */
175ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	u32 macids_used;
176f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek	struct list_head vif_list;
177a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
178a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* power management status cookie from firmware */
179a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	u32 *cookie;
180a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dma_addr_t cookie_dma;
181a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
182a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	u16 num_mcaddrs;
183a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	u8 hw_rev;
1842aa7b01fe4f2d0978115bfd40364f52d86003606Lennert Buytenhek	u32 fw_rev;
185a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
186a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/*
187a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 * Running count of TX packets in flight, to avoid
188a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 * iterating over the transmit rings each time.
189a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 */
190a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int pending_tx_pkts;
191a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
192a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_rx_queue rxq[MWL8K_RX_QUEUES];
193a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_tx_queue txq[MWL8K_TX_QUEUES];
194a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
195c46563b714b09d44eec4d1fd8a0f53e79ddaa3aaLennert Buytenhek	bool radio_on;
19668ce38845c23443b15e374fb7362916c1231278eLennert Buytenhek	bool radio_short_preamble;
197a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	bool sniffer_enabled;
1980439b1f55646ea944b0d58337f5065b79a1c1be0Lennert Buytenhek	bool wmm_enabled;
199a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
200a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* XXX need to convert this to handle multiple interfaces */
201a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	bool capture_beacon;
202d89173f25228b8795af2d4b53e985cc44c729332Lennert Buytenhek	u8 capture_bssid[ETH_ALEN];
203a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct sk_buff *beacon_skb;
204a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
205a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/*
206a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 * This FJ worker has to be global as it is scheduled from the
207a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 * RX handler.  At this point we don't know which interface it
208a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 * belongs to until the list of bssids waiting to complete join
209a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 * is checked.
210a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 */
211a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct work_struct finalize_join_worker;
212a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2131e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	/* Tasklet to perform TX reclaim.  */
2141e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	struct tasklet_struct poll_tx_task;
21567e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek
21667e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	/* Tasklet to perform RX.  */
21767e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	struct tasklet_struct poll_rx_task;
2180d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville
2190d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville	/* Most recently reported noise in dBm */
2200d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville	s8 noise;
2210863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo
2220863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	/*
2230863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	 * preserve the queue configurations so they can be restored if/when
2240863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	 * the firmware image is swapped.
2250863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	 */
2260863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_QUEUES];
22799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
22899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	/* async firmware loading state */
22999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	unsigned fw_state;
23099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	char *fw_pref;
23199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	char *fw_alt;
23299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	struct completion firmware_loading_complete;
233a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek};
234a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
235e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam#define MAX_WEP_KEY_LEN         13
236e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam#define NUM_WEP_KEYS            4
237e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
238a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/* Per interface specific private data */
239a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstruct mwl8k_vif {
240f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek	struct list_head list;
241f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek	struct ieee80211_vif *vif;
242f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek
243f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek	/* Firmware macid for this vif.  */
244f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek	int macid;
245f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek
246c2c2b12a8b6cd23d4abbc086642647c656bf406cLennert Buytenhek	/* Non AMPDU sequence number assigned by driver.  */
247a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	u16 seqno;
248e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
249e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	/* Saved WEP keys */
250e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	struct {
251e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		u8 enabled;
252e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		u8 key[sizeof(struct ieee80211_key_conf) + MAX_WEP_KEY_LEN];
253e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	} wep_key_conf[NUM_WEP_KEYS];
254d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
255d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	/* BSSID */
256d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	u8 bssid[ETH_ALEN];
257d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
258d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	/* A flag to indicate is HW crypto is enabled for this bssid */
259d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	bool is_hw_crypto_enabled;
260a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek};
261a94cc97e14c5750ec2b50b2e4ecdfb0f369ed0f4Lennert Buytenhek#define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv))
262fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam#define IEEE80211_KEY_CONF(_u8) ((struct ieee80211_key_conf *)(_u8))
263a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
264a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhekstruct mwl8k_sta {
265a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	/* Index into station database. Returned by UPDATE_STADB.  */
266a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	u8 peer_id;
267a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek};
268a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek#define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv))
269a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek
270777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhekstatic const struct ieee80211_channel mwl8k_channels_24[] = {
271a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .center_freq = 2412, .hw_value = 1, },
272a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .center_freq = 2417, .hw_value = 2, },
273a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .center_freq = 2422, .hw_value = 3, },
274a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .center_freq = 2427, .hw_value = 4, },
275a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .center_freq = 2432, .hw_value = 5, },
276a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .center_freq = 2437, .hw_value = 6, },
277a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .center_freq = 2442, .hw_value = 7, },
278a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .center_freq = 2447, .hw_value = 8, },
279a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .center_freq = 2452, .hw_value = 9, },
280a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .center_freq = 2457, .hw_value = 10, },
281a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .center_freq = 2462, .hw_value = 11, },
282647ca6b01a5289948e970ea7c1f656f9d90b0a27Lennert Buytenhek	{ .center_freq = 2467, .hw_value = 12, },
283647ca6b01a5289948e970ea7c1f656f9d90b0a27Lennert Buytenhek	{ .center_freq = 2472, .hw_value = 13, },
284647ca6b01a5289948e970ea7c1f656f9d90b0a27Lennert Buytenhek	{ .center_freq = 2484, .hw_value = 14, },
285a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek};
286a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
287777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhekstatic const struct ieee80211_rate mwl8k_rates_24[] = {
288a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .bitrate = 10, .hw_value = 2, },
289a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .bitrate = 20, .hw_value = 4, },
290a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .bitrate = 55, .hw_value = 11, },
2915dfd3e2c6fb69cf4295ec139107f4ebd3f7fbff0Lennert Buytenhek	{ .bitrate = 110, .hw_value = 22, },
2925dfd3e2c6fb69cf4295ec139107f4ebd3f7fbff0Lennert Buytenhek	{ .bitrate = 220, .hw_value = 44, },
293a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .bitrate = 60, .hw_value = 12, },
294a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .bitrate = 90, .hw_value = 18, },
295a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .bitrate = 120, .hw_value = 24, },
296a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .bitrate = 180, .hw_value = 36, },
297a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .bitrate = 240, .hw_value = 48, },
298a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .bitrate = 360, .hw_value = 72, },
299a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .bitrate = 480, .hw_value = 96, },
300a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	{ .bitrate = 540, .hw_value = 108, },
301140eb5e2c1978622d7cd979d59a1c0586fe3bbdbLennert Buytenhek	{ .bitrate = 720, .hw_value = 144, },
302140eb5e2c1978622d7cd979d59a1c0586fe3bbdbLennert Buytenhek};
303140eb5e2c1978622d7cd979d59a1c0586fe3bbdbLennert Buytenhek
3044eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhekstatic const struct ieee80211_channel mwl8k_channels_50[] = {
3054eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	{ .center_freq = 5180, .hw_value = 36, },
3064eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	{ .center_freq = 5200, .hw_value = 40, },
3074eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	{ .center_freq = 5220, .hw_value = 44, },
3084eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	{ .center_freq = 5240, .hw_value = 48, },
3094eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek};
3104eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek
3114eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhekstatic const struct ieee80211_rate mwl8k_rates_50[] = {
3124eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	{ .bitrate = 60, .hw_value = 12, },
3134eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	{ .bitrate = 90, .hw_value = 18, },
3144eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	{ .bitrate = 120, .hw_value = 24, },
3154eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	{ .bitrate = 180, .hw_value = 36, },
3164eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	{ .bitrate = 240, .hw_value = 48, },
3174eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	{ .bitrate = 360, .hw_value = 72, },
3184eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	{ .bitrate = 480, .hw_value = 96, },
3194eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	{ .bitrate = 540, .hw_value = 108, },
3204eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	{ .bitrate = 720, .hw_value = 144, },
3214eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek};
3224eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek
323a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/* Set or get info from Firmware */
324a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_CMD_GET			0x0000
32541fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam#define MWL8K_CMD_SET			0x0001
32641fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam#define MWL8K_CMD_SET_LIST		0x0002
327a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
328a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/* Firmware command codes */
329a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_CMD_CODE_DNLD		0x0001
330a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_CMD_GET_HW_SPEC		0x0003
33142fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek#define MWL8K_CMD_SET_HW_SPEC		0x0004
332a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_CMD_MAC_MULTICAST_ADR	0x0010
333a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_CMD_GET_STAT		0x0014
334ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek#define MWL8K_CMD_RADIO_CONTROL		0x001c
335ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek#define MWL8K_CMD_RF_TX_POWER		0x001e
33641fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam#define MWL8K_CMD_TX_POWER		0x001f
33708b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek#define MWL8K_CMD_RF_ANTENNA		0x0020
338aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek#define MWL8K_CMD_SET_BEACON		0x0100		/* per-vif */
339a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_CMD_SET_PRE_SCAN		0x0107
340a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_CMD_SET_POST_SCAN		0x0108
341ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek#define MWL8K_CMD_SET_RF_CHANNEL	0x010a
342ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek#define MWL8K_CMD_SET_AID		0x010d
343ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek#define MWL8K_CMD_SET_RATE		0x0110
344ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek#define MWL8K_CMD_SET_FINALIZE_JOIN	0x0111
345ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek#define MWL8K_CMD_RTS_THRESHOLD		0x0113
346a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_CMD_SET_SLOT		0x0114
347ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek#define MWL8K_CMD_SET_EDCA_PARAMS	0x0115
348ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek#define MWL8K_CMD_SET_WMM_MODE		0x0123
349a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_CMD_MIMO_CONFIG		0x0125
350ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek#define MWL8K_CMD_USE_FIXED_RATE	0x0126
351a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_CMD_ENABLE_SNIFFER	0x0150
352aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek#define MWL8K_CMD_SET_MAC_ADDR		0x0202		/* per-vif */
353a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_CMD_SET_RATEADAPT_MODE	0x0203
354aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek#define MWL8K_CMD_BSS_START		0x1100		/* per-vif */
355aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek#define MWL8K_CMD_SET_NEW_STN		0x1111		/* per-vif */
356fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam#define MWL8K_CMD_UPDATE_ENCRYPTION	0x1122		/* per-vif */
357ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek#define MWL8K_CMD_UPDATE_STADB		0x1123
358a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
359b603742f49c3ec922522602e18ac22e8f6835132John W. Linvillestatic const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize)
360a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
361b603742f49c3ec922522602e18ac22e8f6835132John W. Linville	u16 command = le16_to_cpu(cmd);
362b603742f49c3ec922522602e18ac22e8f6835132John W. Linville
363a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_CMDNAME(x)	case MWL8K_CMD_##x: do {\
364a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek					snprintf(buf, bufsize, "%s", #x);\
365a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek					return buf;\
366a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek					} while (0)
367b603742f49c3ec922522602e18ac22e8f6835132John W. Linville	switch (command & ~0x8000) {
368a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		MWL8K_CMDNAME(CODE_DNLD);
369a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		MWL8K_CMDNAME(GET_HW_SPEC);
37042fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		MWL8K_CMDNAME(SET_HW_SPEC);
371a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		MWL8K_CMDNAME(MAC_MULTICAST_ADR);
372a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		MWL8K_CMDNAME(GET_STAT);
373a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		MWL8K_CMDNAME(RADIO_CONTROL);
374a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		MWL8K_CMDNAME(RF_TX_POWER);
37541fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam		MWL8K_CMDNAME(TX_POWER);
37608b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek		MWL8K_CMDNAME(RF_ANTENNA);
377b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		MWL8K_CMDNAME(SET_BEACON);
378a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		MWL8K_CMDNAME(SET_PRE_SCAN);
379a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		MWL8K_CMDNAME(SET_POST_SCAN);
380a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		MWL8K_CMDNAME(SET_RF_CHANNEL);
381ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek		MWL8K_CMDNAME(SET_AID);
382ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek		MWL8K_CMDNAME(SET_RATE);
383ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek		MWL8K_CMDNAME(SET_FINALIZE_JOIN);
384ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek		MWL8K_CMDNAME(RTS_THRESHOLD);
385a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		MWL8K_CMDNAME(SET_SLOT);
386ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek		MWL8K_CMDNAME(SET_EDCA_PARAMS);
387ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek		MWL8K_CMDNAME(SET_WMM_MODE);
388a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		MWL8K_CMDNAME(MIMO_CONFIG);
389ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek		MWL8K_CMDNAME(USE_FIXED_RATE);
390a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		MWL8K_CMDNAME(ENABLE_SNIFFER);
39132060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek		MWL8K_CMDNAME(SET_MAC_ADDR);
392a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		MWL8K_CMDNAME(SET_RATEADAPT_MODE);
393b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		MWL8K_CMDNAME(BSS_START);
3943f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek		MWL8K_CMDNAME(SET_NEW_STN);
395fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		MWL8K_CMDNAME(UPDATE_ENCRYPTION);
396ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek		MWL8K_CMDNAME(UPDATE_STADB);
397a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	default:
398a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		snprintf(buf, bufsize, "0x%x", cmd);
399a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
400a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#undef MWL8K_CMDNAME
401a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
402a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return buf;
403a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
404a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
405a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/* Hardware and firmware reset */
406a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void mwl8k_hw_reset(struct mwl8k_priv *priv)
407a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
408a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(MWL8K_H2A_INT_RESET,
409a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
410a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(MWL8K_H2A_INT_RESET,
411a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
412a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	msleep(20);
413a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
414a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
415a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/* Release fw image */
416d1f9e41d1d739cd4393840d35e7554f4a439a4f1Brian Cavagnolostatic void mwl8k_release_fw(const struct firmware **fw)
417a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
418a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (*fw == NULL)
419a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return;
420a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	release_firmware(*fw);
421a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	*fw = NULL;
422a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
423a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
424a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void mwl8k_release_firmware(struct mwl8k_priv *priv)
425a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
42622be40d9c53faa10d03a679160e0854ad115b610Lennert Buytenhek	mwl8k_release_fw(&priv->fw_ucode);
42722be40d9c53faa10d03a679160e0854ad115b610Lennert Buytenhek	mwl8k_release_fw(&priv->fw_helper);
428a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
429a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
43099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo/* states for asynchronous f/w loading */
43199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolostatic void mwl8k_fw_state_machine(const struct firmware *fw, void *context);
43299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnoloenum {
43399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	FW_STATE_INIT = 0,
43499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	FW_STATE_LOADING_PREF,
43599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	FW_STATE_LOADING_ALT,
43699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	FW_STATE_ERROR,
43799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo};
43899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
439a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/* Request fw image */
440a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int mwl8k_request_fw(struct mwl8k_priv *priv,
441d1f9e41d1d739cd4393840d35e7554f4a439a4f1Brian Cavagnolo			    const char *fname, const struct firmware **fw,
44299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			    bool nowait)
443a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
444a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* release current image */
445a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (*fw != NULL)
446a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		mwl8k_release_fw(fw);
447a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
44899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	if (nowait)
44999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		return request_firmware_nowait(THIS_MODULE, 1, fname,
45099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo					       &priv->pdev->dev, GFP_KERNEL,
45199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo					       priv, mwl8k_fw_state_machine);
45299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	else
453d1f9e41d1d739cd4393840d35e7554f4a439a4f1Brian Cavagnolo		return request_firmware(fw, fname, &priv->pdev->dev);
454a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
455a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
45699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolostatic int mwl8k_request_firmware(struct mwl8k_priv *priv, char *fw_image,
45799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo				  bool nowait)
458a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
459a74b295edb3e2b39d6c03255b24dca862a843c59Lennert Buytenhek	struct mwl8k_device_info *di = priv->device_info;
460a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
461a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
462a74b295edb3e2b39d6c03255b24dca862a843c59Lennert Buytenhek	if (di->helper_image != NULL) {
46399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		if (nowait)
46499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			rc = mwl8k_request_fw(priv, di->helper_image,
46599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo					      &priv->fw_helper, true);
46699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		else
46799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			rc = mwl8k_request_fw(priv, di->helper_image,
46899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo					      &priv->fw_helper, false);
46999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		if (rc)
47099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			printk(KERN_ERR "%s: Error requesting helper fw %s\n",
47199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			       pci_name(priv->pdev), di->helper_image);
47299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
47399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		if (rc || nowait)
474a74b295edb3e2b39d6c03255b24dca862a843c59Lennert Buytenhek			return rc;
475a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
476a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
47799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	if (nowait) {
47899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		/*
47999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		 * if we get here, no helper image is needed.  Skip the
48099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		 * FW_STATE_INIT state.
48199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		 */
48299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		priv->fw_state = FW_STATE_LOADING_PREF;
48399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		rc = mwl8k_request_fw(priv, fw_image,
48499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo				      &priv->fw_ucode,
48599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo				      true);
48699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	} else
48799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		rc = mwl8k_request_fw(priv, fw_image,
48899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo				      &priv->fw_ucode, false);
489a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (rc) {
490c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		printk(KERN_ERR "%s: Error requesting firmware file %s\n",
4910863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo		       pci_name(priv->pdev), fw_image);
49222be40d9c53faa10d03a679160e0854ad115b610Lennert Buytenhek		mwl8k_release_fw(&priv->fw_helper);
493a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return rc;
494a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
495a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
496a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return 0;
497a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
498a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
499a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstruct mwl8k_cmd_pkt {
500a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16	code;
501a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16	length;
502f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek	__u8	seq_num;
503f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek	__u8	macid;
504a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16	result;
505a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	char	payload[0];
506ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
507a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
508a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
509a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * Firmware loading.
510a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
511a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int
512a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekmwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length)
513a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
514a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	void __iomem *regs = priv->regs;
515a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dma_addr_t dma_addr;
516a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int loops;
517a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
518a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dma_addr = pci_map_single(priv->pdev, data, length, PCI_DMA_TODEVICE);
519a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (pci_dma_mapping_error(priv->pdev, dma_addr))
520a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
521a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
522a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR);
523a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(0, regs + MWL8K_HIU_INT_CODE);
524a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(MWL8K_H2A_INT_DOORBELL,
525a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
526a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(MWL8K_H2A_INT_DUMMY,
527a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
528a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
529a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	loops = 1000;
530a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	do {
531a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		u32 int_code;
532a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
533a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		int_code = ioread32(regs + MWL8K_HIU_INT_CODE);
534a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (int_code == MWL8K_INT_CODE_CMD_FINISHED) {
535a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			iowrite32(0, regs + MWL8K_HIU_INT_CODE);
536a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			break;
537a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		}
538a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
5393d76e82c9538d8104e578ca460d35f214bfddfd3Lennert Buytenhek		cond_resched();
540a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		udelay(1);
541a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	} while (--loops);
542a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
543a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	pci_unmap_single(priv->pdev, dma_addr, length, PCI_DMA_TODEVICE);
544a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
545d4b7057052236e81ab0788cc8df306dc02b0e7beLennert Buytenhek	return loops ? 0 : -ETIMEDOUT;
546a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
547a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
548a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int mwl8k_load_fw_image(struct mwl8k_priv *priv,
549a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				const u8 *data, size_t length)
550a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
551a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_pkt *cmd;
552a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int done;
553a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc = 0;
554a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
555a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kmalloc(sizeof(*cmd) + 256, GFP_KERNEL);
556a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
557a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
558a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
559a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->code = cpu_to_le16(MWL8K_CMD_CODE_DNLD);
560a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->seq_num = 0;
561f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek	cmd->macid = 0;
562a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->result = 0;
563a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
564a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	done = 0;
565a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	while (length) {
566a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		int block_size = length > 256 ? 256 : length;
567a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
568a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		memcpy(cmd->payload, data + done, block_size);
569a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		cmd->length = cpu_to_le16(block_size);
570a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
571a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		rc = mwl8k_send_fw_load_cmd(priv, cmd,
572a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek						sizeof(*cmd) + block_size);
573a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (rc)
574a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			break;
575a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
576a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		done += block_size;
577a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		length -= block_size;
578a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
579a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
580a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (!rc) {
581a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		cmd->length = 0;
582a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		rc = mwl8k_send_fw_load_cmd(priv, cmd, sizeof(*cmd));
583a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
584a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
585a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
586a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
587a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
588a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
589a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
590a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int mwl8k_feed_fw_image(struct mwl8k_priv *priv,
591a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				const u8 *data, size_t length)
592a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
593a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	unsigned char *buffer;
594a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int may_continue, rc = 0;
595a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	u32 done, prev_block_size;
596a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
597a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	buffer = kmalloc(1024, GFP_KERNEL);
598a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (buffer == NULL)
599a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
600a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
601a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	done = 0;
602a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	prev_block_size = 0;
603a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	may_continue = 1000;
604a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	while (may_continue > 0) {
605a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		u32 block_size;
606a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
607a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		block_size = ioread32(priv->regs + MWL8K_HIU_SCRATCH);
608a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (block_size & 1) {
609a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			block_size &= ~1;
610a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			may_continue--;
611a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		} else {
612a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			done += prev_block_size;
613a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			length -= prev_block_size;
614a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		}
615a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
616a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (block_size > 1024 || block_size > length) {
617a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			rc = -EOVERFLOW;
618a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			break;
619a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		}
620a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
621a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (length == 0) {
622a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			rc = 0;
623a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			break;
624a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		}
625a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
626a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (block_size == 0) {
627a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			rc = -EPROTO;
628a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			may_continue--;
629a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			udelay(1);
630a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			continue;
631a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		}
632a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
633a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		prev_block_size = block_size;
634a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		memcpy(buffer, data + done, block_size);
635a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
636a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		rc = mwl8k_send_fw_load_cmd(priv, buffer, block_size);
637a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (rc)
638a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			break;
639a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
640a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
641a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (!rc && length != 0)
642a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		rc = -EREMOTEIO;
643a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
644a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(buffer);
645a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
646a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
647a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
648a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
649c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhekstatic int mwl8k_load_firmware(struct ieee80211_hw *hw)
650a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
651c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
652d1f9e41d1d739cd4393840d35e7554f4a439a4f1Brian Cavagnolo	const struct firmware *fw = priv->fw_ucode;
653c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek	int rc;
654c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek	int loops;
655c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek
656c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek	if (!memcmp(fw->data, "\x01\x00\x00\x00", 4)) {
657d1f9e41d1d739cd4393840d35e7554f4a439a4f1Brian Cavagnolo		const struct firmware *helper = priv->fw_helper;
658a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
659c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		if (helper == NULL) {
660c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek			printk(KERN_ERR "%s: helper image needed but none "
661c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek			       "given\n", pci_name(priv->pdev));
662c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek			return -EINVAL;
663c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		}
664a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
665c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		rc = mwl8k_load_fw_image(priv, helper->data, helper->size);
666a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (rc) {
667a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			printk(KERN_ERR "%s: unable to load firmware "
668c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek			       "helper image\n", pci_name(priv->pdev));
669a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			return rc;
670a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		}
67189b872e2e476833cde8aaac658c75817f67e8f81Lennert Buytenhek		msleep(5);
672a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
673c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		rc = mwl8k_feed_fw_image(priv, fw->data, fw->size);
674a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	} else {
675c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		rc = mwl8k_load_fw_image(priv, fw->data, fw->size);
676a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
677a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
678a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (rc) {
679c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		printk(KERN_ERR "%s: unable to load firmware image\n",
680c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		       pci_name(priv->pdev));
681a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return rc;
682a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
683a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
68489a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR);
685a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
68689b872e2e476833cde8aaac658c75817f67e8f81Lennert Buytenhek	loops = 500000;
687a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	do {
688eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek		u32 ready_code;
689eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek
690eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek		ready_code = ioread32(priv->regs + MWL8K_HIU_INT_CODE);
691eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek		if (ready_code == MWL8K_FWAP_READY) {
692eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek			priv->ap_fw = 1;
693eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek			break;
694eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek		} else if (ready_code == MWL8K_FWSTA_READY) {
695eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek			priv->ap_fw = 0;
696a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			break;
697eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek		}
698eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek
699eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek		cond_resched();
700a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		udelay(1);
701a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	} while (--loops);
702a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
703a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return loops ? 0 : -ETIMEDOUT;
704a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
705a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
706a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
707a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/* DMA header used by firmware and hardware.  */
708a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstruct mwl8k_dma_data {
709a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 fwlen;
710a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct ieee80211_hdr wh;
71120f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	char data[0];
712ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
713a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
714a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/* Routines to add/remove DMA header from skb.  */
71520f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhekstatic inline void mwl8k_remove_dma_header(struct sk_buff *skb, __le16 qos)
716a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
71720f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	struct mwl8k_dma_data *tr;
71820f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	int hdrlen;
71920f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek
72020f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	tr = (struct mwl8k_dma_data *)skb->data;
72120f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	hdrlen = ieee80211_hdrlen(tr->wh.frame_control);
72220f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek
72320f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	if (hdrlen != sizeof(tr->wh)) {
72420f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek		if (ieee80211_is_data_qos(tr->wh.frame_control)) {
72520f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek			memmove(tr->data - hdrlen, &tr->wh, hdrlen - 2);
72620f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek			*((__le16 *)(tr->data - 2)) = qos;
72720f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek		} else {
72820f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek			memmove(tr->data - hdrlen, &tr->wh, hdrlen);
72920f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek		}
730a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
73120f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek
73220f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	if (hdrlen != sizeof(*tr))
73320f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek		skb_pull(skb, sizeof(*tr) - hdrlen);
734a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
735a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
736252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadamstatic void
737252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadammwl8k_add_dma_header(struct sk_buff *skb, int tail_pad)
738a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
739a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct ieee80211_hdr *wh;
740ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek	int hdrlen;
741252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadam	int reqd_hdrlen;
742a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_dma_data *tr;
743a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
744ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek	/*
745ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek	 * Add a firmware DMA header; the firmware requires that we
746ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek	 * present a 2-byte payload length followed by a 4-address
747ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek	 * header (without QoS field), followed (optionally) by any
748ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek	 * WEP/ExtIV header (but only filled in for CCMP).
749ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek	 */
750a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	wh = (struct ieee80211_hdr *)skb->data;
751ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek
752a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	hdrlen = ieee80211_hdrlen(wh->frame_control);
753252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadam	reqd_hdrlen = sizeof(*tr);
754252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadam
755252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadam	if (hdrlen != reqd_hdrlen)
756252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadam		skb_push(skb, reqd_hdrlen - hdrlen);
757a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
758ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek	if (ieee80211_is_data_qos(wh->frame_control))
759252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadam		hdrlen -= IEEE80211_QOS_CTL_LEN;
760a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
761a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	tr = (struct mwl8k_dma_data *)skb->data;
762a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (wh != &tr->wh)
763a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		memmove(&tr->wh, wh, hdrlen);
764ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek	if (hdrlen != sizeof(tr->wh))
765ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek		memset(((void *)&tr->wh) + hdrlen, 0, sizeof(tr->wh) - hdrlen);
766a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
767a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/*
768a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 * Firmware length is the length of the fully formed "802.11
769a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 * payload".  That is, everything except for the 802.11 header.
770a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 * This includes all crypto material including the MIC.
771a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 */
772252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadam	tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr) + tail_pad);
773a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
774a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
775e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadamstatic void mwl8k_encapsulate_tx_frame(struct sk_buff *skb)
776e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam{
777e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	struct ieee80211_hdr *wh;
778e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	struct ieee80211_tx_info *tx_info;
779e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	struct ieee80211_key_conf *key_conf;
780e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	int data_pad;
781e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
782e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	wh = (struct ieee80211_hdr *)skb->data;
783e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
784e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	tx_info = IEEE80211_SKB_CB(skb);
785e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
786e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	key_conf = NULL;
787e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	if (ieee80211_is_data(wh->frame_control))
788e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		key_conf = tx_info->control.hw_key;
789e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
790e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	/*
791e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	 * Make sure the packet header is in the DMA header format (4-address
792e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	 * without QoS), the necessary crypto padding between the header and the
793e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	 * payload has already been provided by mac80211, but it doesn't add tail
794e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	 * padding when HW crypto is enabled.
795e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	 *
796e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	 * We have the following trailer padding requirements:
797e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	 * - WEP: 4 trailer bytes (ICV)
798e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	 * - TKIP: 12 trailer bytes (8 MIC + 4 ICV)
799e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	 * - CCMP: 8 trailer bytes (MIC)
800e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	 */
801e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	data_pad = 0;
802e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	if (key_conf != NULL) {
803e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		switch (key_conf->cipher) {
804e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		case WLAN_CIPHER_SUITE_WEP40:
805e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		case WLAN_CIPHER_SUITE_WEP104:
806e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam			data_pad = 4;
807e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam			break;
808e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		case WLAN_CIPHER_SUITE_TKIP:
809e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam			data_pad = 12;
810e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam			break;
811e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		case WLAN_CIPHER_SUITE_CCMP:
812e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam			data_pad = 8;
813e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam			break;
814e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		}
815e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	}
816e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	mwl8k_add_dma_header(skb, data_pad);
817e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam}
818a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
819a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
82089a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek * Packet reception for 88w8366 AP firmware.
8216f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek */
82289a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhekstruct mwl8k_rxd_8366_ap {
8236f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	__le16 pkt_len;
8246f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	__u8 sq2;
8256f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	__u8 rate;
8266f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	__le32 pkt_phys_addr;
8276f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	__le32 next_rxd_phys_addr;
8286f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	__le16 qos_control;
8296f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	__le16 htsig2;
8306f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	__le32 hw_rssi_info;
8316f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	__le32 hw_noise_floor_info;
8326f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	__u8 noise_floor;
8336f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	__u8 pad0[3];
8346f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	__u8 rssi;
8356f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	__u8 rx_status;
8366f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	__u8 channel;
8376f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	__u8 rx_ctrl;
838ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
8396f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
84089a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek#define MWL8K_8366_AP_RATE_INFO_MCS_FORMAT	0x80
84189a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek#define MWL8K_8366_AP_RATE_INFO_40MHZ		0x40
84289a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek#define MWL8K_8366_AP_RATE_INFO_RATEID(x)	((x) & 0x3f)
8438e9f33f0ced82a797d285b233e1c956cbd5c7de3Lennert Buytenhek
84489a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek#define MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST	0x80
8456f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
846d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam/* 8366 AP rx_status bits */
847d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam#define MWL8K_8366_AP_RXSTAT_DECRYPT_ERR_MASK		0x80
848d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam#define MWL8K_8366_AP_RXSTAT_GENERAL_DECRYPT_ERR	0xFF
849d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam#define MWL8K_8366_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR	0x02
850d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam#define MWL8K_8366_AP_RXSTAT_WEP_DECRYPT_ICV_ERR	0x04
851d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam#define MWL8K_8366_AP_RXSTAT_TKIP_DECRYPT_ICV_ERR	0x08
852d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
85389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhekstatic void mwl8k_rxd_8366_ap_init(void *_rxd, dma_addr_t next_dma_addr)
8546f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek{
85589a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	struct mwl8k_rxd_8366_ap *rxd = _rxd;
8566f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
8576f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr);
85889a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	rxd->rx_ctrl = MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST;
8596f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek}
8606f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
86189a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhekstatic void mwl8k_rxd_8366_ap_refill(void *_rxd, dma_addr_t addr, int len)
8626f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek{
86389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	struct mwl8k_rxd_8366_ap *rxd = _rxd;
8646f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
8656f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	rxd->pkt_len = cpu_to_le16(len);
8666f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	rxd->pkt_phys_addr = cpu_to_le32(addr);
8676f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	wmb();
8686f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	rxd->rx_ctrl = 0;
8696f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek}
8706f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
8716f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhekstatic int
87289a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhekmwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status,
8730d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville			  __le16 *qos, s8 *noise)
8746f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek{
87589a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	struct mwl8k_rxd_8366_ap *rxd = _rxd;
8766f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
87789a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	if (!(rxd->rx_ctrl & MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST))
8786f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek		return -1;
8796f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	rmb();
8806f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
8816f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	memset(status, 0, sizeof(*status));
8826f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
8836f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	status->signal = -rxd->rssi;
8840d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville	*noise = -rxd->noise_floor;
8856f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
88689a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	if (rxd->rate & MWL8K_8366_AP_RATE_INFO_MCS_FORMAT) {
8876f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek		status->flag |= RX_FLAG_HT;
88889a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek		if (rxd->rate & MWL8K_8366_AP_RATE_INFO_40MHZ)
8898e9f33f0ced82a797d285b233e1c956cbd5c7de3Lennert Buytenhek			status->flag |= RX_FLAG_40MHZ;
89089a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek		status->rate_idx = MWL8K_8366_AP_RATE_INFO_RATEID(rxd->rate);
8916f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	} else {
8926f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek		int i;
8936f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
894777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		for (i = 0; i < ARRAY_SIZE(mwl8k_rates_24); i++) {
895777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek			if (mwl8k_rates_24[i].hw_value == rxd->rate) {
8966f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek				status->rate_idx = i;
8976f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek				break;
8986f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek			}
8996f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek		}
9006f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	}
9016f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
902854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek	if (rxd->channel > 14) {
903854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek		status->band = IEEE80211_BAND_5GHZ;
904854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek		if (!(status->flag & RX_FLAG_HT))
905854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek			status->rate_idx -= 5;
906854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek	} else {
907854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek		status->band = IEEE80211_BAND_2GHZ;
908854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek	}
90959eb21a6504731fc16db4cf9463065dd61093e08Bruno Randolf	status->freq = ieee80211_channel_to_frequency(rxd->channel,
91059eb21a6504731fc16db4cf9463065dd61093e08Bruno Randolf						      status->band);
9116f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
91220f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	*qos = rxd->qos_control;
91320f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek
914d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	if ((rxd->rx_status != MWL8K_8366_AP_RXSTAT_GENERAL_DECRYPT_ERR) &&
915d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	    (rxd->rx_status & MWL8K_8366_AP_RXSTAT_DECRYPT_ERR_MASK) &&
916d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	    (rxd->rx_status & MWL8K_8366_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR))
917d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		status->flag |= RX_FLAG_MMIC_ERROR;
918d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
9196f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	return le16_to_cpu(rxd->pkt_len);
9206f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek}
9216f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
92289a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhekstatic struct rxd_ops rxd_8366_ap_ops = {
92389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	.rxd_size	= sizeof(struct mwl8k_rxd_8366_ap),
92489a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	.rxd_init	= mwl8k_rxd_8366_ap_init,
92589a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	.rxd_refill	= mwl8k_rxd_8366_ap_refill,
92689a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	.rxd_process	= mwl8k_rxd_8366_ap_process,
9276f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek};
9286f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
9296f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek/*
93089a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek * Packet reception for STA firmware.
931a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
93289a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhekstruct mwl8k_rxd_sta {
933a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 pkt_len;
934a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 link_quality;
935a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 noise_level;
936a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 pkt_phys_addr;
93745eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	__le32 next_rxd_phys_addr;
938a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 qos_control;
939a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 rate_info;
940a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 pad0[4];
941a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 rssi;
942a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 channel;
943a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 pad1;
944a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 rx_ctrl;
945a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 rx_status;
946a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 pad2[2];
947ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
948a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
94989a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek#define MWL8K_STA_RATE_INFO_SHORTPRE		0x8000
95089a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek#define MWL8K_STA_RATE_INFO_ANTSELECT(x)	(((x) >> 11) & 0x3)
95189a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek#define MWL8K_STA_RATE_INFO_RATEID(x)		(((x) >> 3) & 0x3f)
95289a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek#define MWL8K_STA_RATE_INFO_40MHZ		0x0004
95389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek#define MWL8K_STA_RATE_INFO_SHORTGI		0x0002
95489a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek#define MWL8K_STA_RATE_INFO_MCS_FORMAT		0x0001
95554bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
95689a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek#define MWL8K_STA_RX_CTRL_OWNED_BY_HOST		0x02
957d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam#define MWL8K_STA_RX_CTRL_DECRYPT_ERROR		0x04
958d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam/* ICV=0 or MIC=1 */
959d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam#define MWL8K_STA_RX_CTRL_DEC_ERR_TYPE		0x08
960d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam/* Key is uploaded only in failure case */
961d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam#define MWL8K_STA_RX_CTRL_KEY_INDEX			0x30
96254bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
96389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhekstatic void mwl8k_rxd_sta_init(void *_rxd, dma_addr_t next_dma_addr)
96454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek{
96589a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	struct mwl8k_rxd_sta *rxd = _rxd;
96654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
96754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr);
96889a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	rxd->rx_ctrl = MWL8K_STA_RX_CTRL_OWNED_BY_HOST;
96954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek}
97054bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
97189a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhekstatic void mwl8k_rxd_sta_refill(void *_rxd, dma_addr_t addr, int len)
97254bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek{
97389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	struct mwl8k_rxd_sta *rxd = _rxd;
97454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
97554bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	rxd->pkt_len = cpu_to_le16(len);
97654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	rxd->pkt_phys_addr = cpu_to_le32(addr);
97754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	wmb();
97854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	rxd->rx_ctrl = 0;
97954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek}
98054bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
98154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhekstatic int
98289a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhekmwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status,
9830d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville		       __le16 *qos, s8 *noise)
98454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek{
98589a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	struct mwl8k_rxd_sta *rxd = _rxd;
98654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	u16 rate_info;
98754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
98889a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	if (!(rxd->rx_ctrl & MWL8K_STA_RX_CTRL_OWNED_BY_HOST))
98954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		return -1;
99054bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	rmb();
99154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
99254bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	rate_info = le16_to_cpu(rxd->rate_info);
99354bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
99454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	memset(status, 0, sizeof(*status));
99554bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
99654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	status->signal = -rxd->rssi;
9970d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville	*noise = -rxd->noise_level;
99889a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	status->antenna = MWL8K_STA_RATE_INFO_ANTSELECT(rate_info);
99989a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	status->rate_idx = MWL8K_STA_RATE_INFO_RATEID(rate_info);
100054bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
100189a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	if (rate_info & MWL8K_STA_RATE_INFO_SHORTPRE)
100254bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		status->flag |= RX_FLAG_SHORTPRE;
100389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	if (rate_info & MWL8K_STA_RATE_INFO_40MHZ)
100454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		status->flag |= RX_FLAG_40MHZ;
100589a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	if (rate_info & MWL8K_STA_RATE_INFO_SHORTGI)
100654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		status->flag |= RX_FLAG_SHORT_GI;
100789a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT)
100854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		status->flag |= RX_FLAG_HT;
100954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
1010854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek	if (rxd->channel > 14) {
1011854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek		status->band = IEEE80211_BAND_5GHZ;
1012854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek		if (!(status->flag & RX_FLAG_HT))
1013854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek			status->rate_idx -= 5;
1014854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek	} else {
1015854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek		status->band = IEEE80211_BAND_2GHZ;
1016854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek	}
101759eb21a6504731fc16db4cf9463065dd61093e08Bruno Randolf	status->freq = ieee80211_channel_to_frequency(rxd->channel,
101859eb21a6504731fc16db4cf9463065dd61093e08Bruno Randolf						      status->band);
101954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
102020f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	*qos = rxd->qos_control;
1021d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	if ((rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DECRYPT_ERROR) &&
1022d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	    (rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DEC_ERR_TYPE))
1023d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		status->flag |= RX_FLAG_MMIC_ERROR;
102420f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek
102554bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	return le16_to_cpu(rxd->pkt_len);
102654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek}
102754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
102889a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhekstatic struct rxd_ops rxd_sta_ops = {
102989a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	.rxd_size	= sizeof(struct mwl8k_rxd_sta),
103089a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	.rxd_init	= mwl8k_rxd_sta_init,
103189a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	.rxd_refill	= mwl8k_rxd_sta_refill,
103289a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	.rxd_process	= mwl8k_rxd_sta_process,
103354bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek};
103454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
103554bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
1036a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_RX_DESCS		256
1037a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_RX_MAXSZ		3800
1038a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1039a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int mwl8k_rxq_init(struct ieee80211_hw *hw, int index)
1040a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1041a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
1042a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_rx_queue *rxq = priv->rxq + index;
1043a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int size;
1044a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int i;
1045a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
104645eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	rxq->rxd_count = 0;
104745eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	rxq->head = 0;
104845eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	rxq->tail = 0;
1049a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
105054bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	size = MWL8K_RX_DESCS * priv->rxd_ops->rxd_size;
1051a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
105245eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	rxq->rxd = pci_alloc_consistent(priv->pdev, size, &rxq->rxd_dma);
105345eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	if (rxq->rxd == NULL) {
10545db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches		wiphy_err(hw->wiphy, "failed to alloc RX descriptors\n");
1055a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
1056a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
105745eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	memset(rxq->rxd, 0, size);
1058a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1059788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek	rxq->buf = kmalloc(MWL8K_RX_DESCS * sizeof(*rxq->buf), GFP_KERNEL);
1060788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek	if (rxq->buf == NULL) {
10615db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches		wiphy_err(hw->wiphy, "failed to alloc RX skbuff list\n");
106245eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		pci_free_consistent(priv->pdev, size, rxq->rxd, rxq->rxd_dma);
1063a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
1064a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
1065788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek	memset(rxq->buf, 0, MWL8K_RX_DESCS * sizeof(*rxq->buf));
1066a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1067a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	for (i = 0; i < MWL8K_RX_DESCS; i++) {
106854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		int desc_size;
106954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		void *rxd;
1070a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		int nexti;
107154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		dma_addr_t next_dma_addr;
107254bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
107354bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		desc_size = priv->rxd_ops->rxd_size;
107454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		rxd = rxq->rxd + (i * priv->rxd_ops->rxd_size);
1075a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
107654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		nexti = i + 1;
107754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		if (nexti == MWL8K_RX_DESCS)
107854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek			nexti = 0;
107954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		next_dma_addr = rxq->rxd_dma + (nexti * desc_size);
1080a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
108154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		priv->rxd_ops->rxd_init(rxd, next_dma_addr);
1082a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
1083a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1084a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return 0;
1085a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1086a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1087a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int rxq_refill(struct ieee80211_hw *hw, int index, int limit)
1088a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1089a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
1090a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_rx_queue *rxq = priv->rxq + index;
1091a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int refilled;
1092a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1093a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	refilled = 0;
109445eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	while (rxq->rxd_count < MWL8K_RX_DESCS && limit--) {
1095a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		struct sk_buff *skb;
1096788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek		dma_addr_t addr;
1097a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		int rx;
109854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		void *rxd;
1099a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1100a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		skb = dev_alloc_skb(MWL8K_RX_MAXSZ);
1101a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (skb == NULL)
1102a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			break;
1103a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1104788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek		addr = pci_map_single(priv->pdev, skb->data,
1105788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek				      MWL8K_RX_MAXSZ, DMA_FROM_DEVICE);
1106a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
110754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		rxq->rxd_count++;
110854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		rx = rxq->tail++;
110954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		if (rxq->tail == MWL8K_RX_DESCS)
111054bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek			rxq->tail = 0;
1111788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek		rxq->buf[rx].skb = skb;
111253b1b3e1f0feaa57b82d3dc344d887fe3eecc90bFUJITA Tomonori		dma_unmap_addr_set(&rxq->buf[rx], dma, addr);
111354bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
111454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		rxd = rxq->rxd + (rx * priv->rxd_ops->rxd_size);
111554bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		priv->rxd_ops->rxd_refill(rxd, addr, MWL8K_RX_MAXSZ);
1116a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1117a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		refilled++;
1118a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
1119a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1120a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return refilled;
1121a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1122a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1123a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/* Must be called only when the card's reception is completely halted */
1124a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void mwl8k_rxq_deinit(struct ieee80211_hw *hw, int index)
1125a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1126a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
1127a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_rx_queue *rxq = priv->rxq + index;
1128a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int i;
1129a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1130a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	for (i = 0; i < MWL8K_RX_DESCS; i++) {
1131788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek		if (rxq->buf[i].skb != NULL) {
1132788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek			pci_unmap_single(priv->pdev,
113353b1b3e1f0feaa57b82d3dc344d887fe3eecc90bFUJITA Tomonori					 dma_unmap_addr(&rxq->buf[i], dma),
1134788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek					 MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE);
113553b1b3e1f0feaa57b82d3dc344d887fe3eecc90bFUJITA Tomonori			dma_unmap_addr_set(&rxq->buf[i], dma, 0);
1136788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek
1137788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek			kfree_skb(rxq->buf[i].skb);
1138788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek			rxq->buf[i].skb = NULL;
1139a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		}
1140a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
1141a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1142788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek	kfree(rxq->buf);
1143788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek	rxq->buf = NULL;
1144a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1145a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	pci_free_consistent(priv->pdev,
114654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek			    MWL8K_RX_DESCS * priv->rxd_ops->rxd_size,
114745eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek			    rxq->rxd, rxq->rxd_dma);
114845eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	rxq->rxd = NULL;
1149a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1150a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1151a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1152a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
1153a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * Scan a list of BSSIDs to process for finalize join.
1154a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * Allows for extension to process multiple BSSIDs.
1155a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
1156a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic inline int
1157a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekmwl8k_capture_bssid(struct mwl8k_priv *priv, struct ieee80211_hdr *wh)
1158a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1159a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return priv->capture_beacon &&
1160a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		ieee80211_is_beacon(wh->frame_control) &&
1161a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		!compare_ether_addr(wh->addr3, priv->capture_bssid);
1162a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1163a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
11643779752d764b86077375510b1fd13d8fb2c7c3a5Lennert Buytenhekstatic inline void mwl8k_save_beacon(struct ieee80211_hw *hw,
11653779752d764b86077375510b1fd13d8fb2c7c3a5Lennert Buytenhek				     struct sk_buff *skb)
1166a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
11673779752d764b86077375510b1fd13d8fb2c7c3a5Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
11683779752d764b86077375510b1fd13d8fb2c7c3a5Lennert Buytenhek
1169a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	priv->capture_beacon = false;
1170d89173f25228b8795af2d4b53e985cc44c729332Lennert Buytenhek	memset(priv->capture_bssid, 0, ETH_ALEN);
1171a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1172a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/*
1173a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 * Use GFP_ATOMIC as rxq_process is called from
1174a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 * the primary interrupt handler, memory allocation call
1175a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 * must not sleep.
1176a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 */
1177a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	priv->beacon_skb = skb_copy(skb, GFP_ATOMIC);
1178a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (priv->beacon_skb != NULL)
11793779752d764b86077375510b1fd13d8fb2c7c3a5Lennert Buytenhek		ieee80211_queue_work(hw, &priv->finalize_join_worker);
1180a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1181a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1182d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadamstatic inline struct mwl8k_vif *mwl8k_find_vif_bss(struct list_head *vif_list,
1183d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam						   u8 *bssid)
1184d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam{
1185d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	struct mwl8k_vif *mwl8k_vif;
1186d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1187d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	list_for_each_entry(mwl8k_vif,
1188d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			    vif_list, list) {
1189d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		if (memcmp(bssid, mwl8k_vif->bssid,
1190d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			   ETH_ALEN) == 0)
1191d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			return mwl8k_vif;
1192d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	}
1193d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1194d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	return NULL;
1195d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam}
1196d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1197a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int rxq_process(struct ieee80211_hw *hw, int index, int limit)
1198a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1199a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
1200d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	struct mwl8k_vif *mwl8k_vif = NULL;
1201a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_rx_queue *rxq = priv->rxq + index;
1202a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int processed;
1203a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1204a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	processed = 0;
120545eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	while (rxq->rxd_count && limit--) {
1206a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		struct sk_buff *skb;
120754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		void *rxd;
120854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		int pkt_len;
1209a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		struct ieee80211_rx_status status;
1210d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		struct ieee80211_hdr *wh;
121120f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek		__le16 qos;
1212a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1213788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek		skb = rxq->buf[rxq->head].skb;
1214d25f9f1357139bbdc79bc960ea84909a7c22ec2bLennert Buytenhek		if (skb == NULL)
1215d25f9f1357139bbdc79bc960ea84909a7c22ec2bLennert Buytenhek			break;
121654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
121754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		rxd = rxq->rxd + (rxq->head * priv->rxd_ops->rxd_size);
121854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
12190d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville		pkt_len = priv->rxd_ops->rxd_process(rxd, &status, &qos,
12200d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville							&priv->noise);
122154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		if (pkt_len < 0)
122254bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek			break;
122354bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
1224788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek		rxq->buf[rxq->head].skb = NULL;
1225788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek
1226788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek		pci_unmap_single(priv->pdev,
122753b1b3e1f0feaa57b82d3dc344d887fe3eecc90bFUJITA Tomonori				 dma_unmap_addr(&rxq->buf[rxq->head], dma),
1228788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek				 MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE);
122953b1b3e1f0feaa57b82d3dc344d887fe3eecc90bFUJITA Tomonori		dma_unmap_addr_set(&rxq->buf[rxq->head], dma, 0);
1230a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
123154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		rxq->head++;
123254bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		if (rxq->head == MWL8K_RX_DESCS)
123354bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek			rxq->head = 0;
123454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
123545eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		rxq->rxd_count--;
1236a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1237d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		wh = &((struct mwl8k_dma_data *)skb->data)->wh;
1238a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1239a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		/*
1240c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		 * Check for a pending join operation.  Save a
1241c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		 * copy of the beacon and schedule a tasklet to
1242c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		 * send a FINALIZE_JOIN command to the firmware.
1243a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		 */
124454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		if (mwl8k_capture_bssid(priv, (void *)skb->data))
12453779752d764b86077375510b1fd13d8fb2c7c3a5Lennert Buytenhek			mwl8k_save_beacon(hw, skb);
1246a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1247d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		if (ieee80211_has_protected(wh->frame_control)) {
1248d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1249d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			/* Check if hw crypto has been enabled for
1250d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			 * this bss. If yes, set the status flags
1251d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			 * accordingly
1252d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			 */
1253d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			mwl8k_vif = mwl8k_find_vif_bss(&priv->vif_list,
1254d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam								wh->addr1);
1255d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1256d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			if (mwl8k_vif != NULL &&
1257d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			    mwl8k_vif->is_hw_crypto_enabled == true) {
1258d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				/*
1259d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				 * When MMIC ERROR is encountered
1260d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				 * by the firmware, payload is
1261d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				 * dropped and only 32 bytes of
1262d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				 * mwl8k Firmware header is sent
1263d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				 * to the host.
1264d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				 *
1265d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				 * We need to add four bytes of
1266d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				 * key information.  In it
1267d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				 * MAC80211 expects keyidx set to
1268d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				 * 0 for triggering Counter
1269d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				 * Measure of MMIC failure.
1270d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				 */
1271d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				if (status.flag & RX_FLAG_MMIC_ERROR) {
1272d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam					struct mwl8k_dma_data *tr;
1273d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam					tr = (struct mwl8k_dma_data *)skb->data;
1274d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam					memset((void *)&(tr->data), 0, 4);
1275d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam					pkt_len += 4;
1276d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				}
1277d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1278d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				if (!ieee80211_is_auth(wh->frame_control))
1279d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam					status.flag |= RX_FLAG_IV_STRIPPED |
1280d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam						       RX_FLAG_DECRYPTED |
1281d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam						       RX_FLAG_MMIC_STRIPPED;
1282d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			}
1283d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		}
1284d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1285d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		skb_put(skb, pkt_len);
1286d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		mwl8k_remove_dma_header(skb, qos);
1287f1d58c2521eb160178b2151d6326d8dc5d7c8560Johannes Berg		memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
1288f1d58c2521eb160178b2151d6326d8dc5d7c8560Johannes Berg		ieee80211_rx_irqsafe(hw, skb);
1289a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1290a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		processed++;
1291a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
1292a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1293a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return processed;
1294a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1295a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1296a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1297a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
1298a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * Packet transmission.
1299a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
1300a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1301a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_TXD_STATUS_OK			0x00000001
1302a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_TXD_STATUS_OK_RETRY		0x00000002
1303a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_TXD_STATUS_OK_MORE_RETRY		0x00000004
1304a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_TXD_STATUS_MULTICAST_TX		0x00000008
1305a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_TXD_STATUS_FW_OWNED		0x80000000
1306a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1307e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhek#define MWL8K_QOS_QLEN_UNSPEC			0xff00
1308e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhek#define MWL8K_QOS_ACK_POLICY_MASK		0x0060
1309e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhek#define MWL8K_QOS_ACK_POLICY_NORMAL		0x0000
1310e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhek#define MWL8K_QOS_ACK_POLICY_BLOCKACK		0x0060
1311e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhek#define MWL8K_QOS_EOSP				0x0010
1312e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhek
1313a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstruct mwl8k_tx_desc {
1314a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 status;
1315a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 data_rate;
1316a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 tx_priority;
1317a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 qos_control;
1318a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 pkt_phys_addr;
1319a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 pkt_len;
1320d89173f25228b8795af2d4b53e985cc44c729332Lennert Buytenhek	__u8 dest_MAC_addr[ETH_ALEN];
132145eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	__le32 next_txd_phys_addr;
1322a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 reserved;
1323a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 rate_info;
1324a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 peer_id;
1325a1fe24b0fd8bf16b4e551ae3fb785bfc574b9ffbBrian Cavagnolo	__u8 tx_frag_cnt;
1326ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
1327a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1328a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_TX_DESCS		128
1329a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1330a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int mwl8k_txq_init(struct ieee80211_hw *hw, int index)
1331a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1332a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
1333a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_tx_queue *txq = priv->txq + index;
1334a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int size;
1335a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int i;
1336a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
13378ccbc3b8b0c919e8609560ca56cd777ece8d2c41Kalle Valo	txq->len = 0;
133845eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	txq->head = 0;
133945eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	txq->tail = 0;
1340a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1341a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	size = MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc);
1342a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
134345eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	txq->txd = pci_alloc_consistent(priv->pdev, size, &txq->txd_dma);
134445eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	if (txq->txd == NULL) {
13455db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches		wiphy_err(hw->wiphy, "failed to alloc TX descriptors\n");
1346a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
1347a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
134845eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	memset(txq->txd, 0, size);
1349a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
135045eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	txq->skb = kmalloc(MWL8K_TX_DESCS * sizeof(*txq->skb), GFP_KERNEL);
135145eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	if (txq->skb == NULL) {
13525db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches		wiphy_err(hw->wiphy, "failed to alloc TX skbuff list\n");
135345eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		pci_free_consistent(priv->pdev, size, txq->txd, txq->txd_dma);
1354a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
1355a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
135645eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	memset(txq->skb, 0, MWL8K_TX_DESCS * sizeof(*txq->skb));
1357a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1358a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	for (i = 0; i < MWL8K_TX_DESCS; i++) {
1359a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		struct mwl8k_tx_desc *tx_desc;
1360a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		int nexti;
1361a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
136245eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		tx_desc = txq->txd + i;
1363a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		nexti = (i + 1) % MWL8K_TX_DESCS;
1364a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1365a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		tx_desc->status = 0;
136645eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		tx_desc->next_txd_phys_addr =
136745eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek			cpu_to_le32(txq->txd_dma + nexti * sizeof(*tx_desc));
1368a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
1369a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1370a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return 0;
1371a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1372a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1373a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic inline void mwl8k_tx_start(struct mwl8k_priv *priv)
1374a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1375a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(MWL8K_H2A_INT_PPA_READY,
1376a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
1377a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(MWL8K_H2A_INT_DUMMY,
1378a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
1379a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	ioread32(priv->regs + MWL8K_HIU_INT_CODE);
1380a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1381a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
13827e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhekstatic void mwl8k_dump_tx_rings(struct ieee80211_hw *hw)
1383a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
13847e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
13857e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	int i;
13867e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek
13877e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	for (i = 0; i < MWL8K_TX_QUEUES; i++) {
13887e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		struct mwl8k_tx_queue *txq = priv->txq + i;
13897e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		int fw_owned = 0;
13907e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		int drv_owned = 0;
13917e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		int unused = 0;
13927e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		int desc;
13937e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek
1394a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		for (desc = 0; desc < MWL8K_TX_DESCS; desc++) {
13957e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek			struct mwl8k_tx_desc *tx_desc = txq->txd + desc;
13967e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek			u32 status;
1397a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
13987e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek			status = le32_to_cpu(tx_desc->status);
1399a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			if (status & MWL8K_TXD_STATUS_FW_OWNED)
14007e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek				fw_owned++;
1401a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			else
14027e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek				drv_owned++;
1403a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1404a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			if (tx_desc->pkt_len == 0)
14057e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek				unused++;
1406a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		}
1407a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1408c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches		wiphy_err(hw->wiphy,
1409c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches			  "txq[%d] len=%d head=%d tail=%d "
1410c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches			  "fw_owned=%d drv_owned=%d unused=%d\n",
1411c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches			  i,
1412c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches			  txq->len, txq->head, txq->tail,
1413c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches			  fw_owned, drv_owned, unused);
14147e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	}
1415a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1416a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1417618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek/*
141888de754ad59025eba797e7a8375807755577f450Lennert Buytenhek * Must be called with priv->fw_mutex held and tx queues stopped.
1419618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek */
142062abd3cfb2f1a0ab1963ac4c4087c477da6b1f2aLennert Buytenhek#define MWL8K_TX_WAIT_TIMEOUT_MS	5000
14217e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek
1422950d5b0191dc3e71017f336017f75f6189f39f08Lennert Buytenhekstatic int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
1423a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1424a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
142588de754ad59025eba797e7a8375807755577f450Lennert Buytenhek	DECLARE_COMPLETION_ONSTACK(tx_wait);
14267e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	int retry;
14277e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	int rc;
1428a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1429a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	might_sleep();
1430a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
14317e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	/*
14327e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	 * The TX queues are stopped at this point, so this test
14337e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	 * doesn't need to take ->tx_lock.
14347e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	 */
14357e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	if (!priv->pending_tx_pkts)
14367e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		return 0;
14377e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek
14387e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	retry = 0;
14397e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	rc = 0;
14407e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek
1441a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	spin_lock_bh(&priv->tx_lock);
14427e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	priv->tx_wait = &tx_wait;
14437e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	while (!rc) {
14447e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		int oldcount;
14457e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		unsigned long timeout;
1446a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
14477e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		oldcount = priv->pending_tx_pkts;
1448a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
14497e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		spin_unlock_bh(&priv->tx_lock);
145088de754ad59025eba797e7a8375807755577f450Lennert Buytenhek		timeout = wait_for_completion_timeout(&tx_wait,
14517e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek			    msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS));
1452a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		spin_lock_bh(&priv->tx_lock);
14537e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek
14547e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		if (timeout) {
14557e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek			WARN_ON(priv->pending_tx_pkts);
14567e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek			if (retry) {
1457c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches				wiphy_notice(hw->wiphy, "tx rings drained\n");
14587e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek			}
14597e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek			break;
14607e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		}
14617e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek
14627e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		if (priv->pending_tx_pkts < oldcount) {
1463c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches			wiphy_notice(hw->wiphy,
1464c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches				     "waiting for tx rings to drain (%d -> %d pkts)\n",
1465c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches				     oldcount, priv->pending_tx_pkts);
14667e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek			retry = 1;
14677e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek			continue;
14687e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		}
14697e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek
1470a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		priv->tx_wait = NULL;
1471a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1472c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches		wiphy_err(hw->wiphy, "tx rings stuck for %d ms\n",
1473c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches			  MWL8K_TX_WAIT_TIMEOUT_MS);
14747e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		mwl8k_dump_tx_rings(hw);
14757e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek
14767e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		rc = -ETIMEDOUT;
1477a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
14787e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	spin_unlock_bh(&priv->tx_lock);
1479a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
14807e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	return rc;
1481a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1482a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1483c23b5a699471ea2ef9d146eae80e64836cfbf001Lennert Buytenhek#define MWL8K_TXD_SUCCESS(status)				\
1484c23b5a699471ea2ef9d146eae80e64836cfbf001Lennert Buytenhek	((status) & (MWL8K_TXD_STATUS_OK |			\
1485c23b5a699471ea2ef9d146eae80e64836cfbf001Lennert Buytenhek		     MWL8K_TXD_STATUS_OK_RETRY |		\
1486c23b5a699471ea2ef9d146eae80e64836cfbf001Lennert Buytenhek		     MWL8K_TXD_STATUS_OK_MORE_RETRY))
1487a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1488efb7c49a68cf206f35793d7799608e1d69a209f9Lennert Buytenhekstatic int
1489efb7c49a68cf206f35793d7799608e1d69a209f9Lennert Buytenhekmwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force)
1490a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1491a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
1492a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_tx_queue *txq = priv->txq + index;
1493efb7c49a68cf206f35793d7799608e1d69a209f9Lennert Buytenhek	int processed;
1494a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1495efb7c49a68cf206f35793d7799608e1d69a209f9Lennert Buytenhek	processed = 0;
14968ccbc3b8b0c919e8609560ca56cd777ece8d2c41Kalle Valo	while (txq->len > 0 && limit--) {
1497a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		int tx;
1498a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		struct mwl8k_tx_desc *tx_desc;
1499a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		unsigned long addr;
1500ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek		int size;
1501a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		struct sk_buff *skb;
1502a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		struct ieee80211_tx_info *info;
1503a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		u32 status;
1504a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
150545eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		tx = txq->head;
150645eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		tx_desc = txq->txd + tx;
1507a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1508a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		status = le32_to_cpu(tx_desc->status);
1509a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1510a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (status & MWL8K_TXD_STATUS_FW_OWNED) {
1511a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			if (!force)
1512a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				break;
1513a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			tx_desc->status &=
1514a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				~cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED);
1515a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		}
1516a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
151745eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		txq->head = (tx + 1) % MWL8K_TX_DESCS;
15188ccbc3b8b0c919e8609560ca56cd777ece8d2c41Kalle Valo		BUG_ON(txq->len == 0);
15198ccbc3b8b0c919e8609560ca56cd777ece8d2c41Kalle Valo		txq->len--;
1520a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		priv->pending_tx_pkts--;
1521a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1522a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		addr = le32_to_cpu(tx_desc->pkt_phys_addr);
1523ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek		size = le16_to_cpu(tx_desc->pkt_len);
152445eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		skb = txq->skb[tx];
152545eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		txq->skb[tx] = NULL;
1526a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1527a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		BUG_ON(skb == NULL);
1528a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		pci_unmap_single(priv->pdev, addr, size, PCI_DMA_TODEVICE);
1529a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
153020f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek		mwl8k_remove_dma_header(skb, tx_desc->qos_control);
1531a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1532a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		/* Mark descriptor as unused */
1533a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		tx_desc->pkt_phys_addr = 0;
1534a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		tx_desc->pkt_len = 0;
1535a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1536a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		info = IEEE80211_SKB_CB(skb);
1537a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		ieee80211_tx_info_clear_status(info);
15380bf22c3751d19f9be20205c0e7112723618a4858Nishant Sarmukadam
15390bf22c3751d19f9be20205c0e7112723618a4858Nishant Sarmukadam		/* Rate control is happening in the firmware.
15400bf22c3751d19f9be20205c0e7112723618a4858Nishant Sarmukadam		 * Ensure no tx rate is being reported.
15410bf22c3751d19f9be20205c0e7112723618a4858Nishant Sarmukadam		 */
15420bf22c3751d19f9be20205c0e7112723618a4858Nishant Sarmukadam                info->status.rates[0].idx = -1;
15430bf22c3751d19f9be20205c0e7112723618a4858Nishant Sarmukadam                info->status.rates[0].count = 1;
15440bf22c3751d19f9be20205c0e7112723618a4858Nishant Sarmukadam
1545ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek		if (MWL8K_TXD_SUCCESS(status))
1546a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			info->flags |= IEEE80211_TX_STAT_ACK;
1547a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1548a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		ieee80211_tx_status_irqsafe(hw, skb);
1549a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1550efb7c49a68cf206f35793d7799608e1d69a209f9Lennert Buytenhek		processed++;
1551a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
1552a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1553efb7c49a68cf206f35793d7799608e1d69a209f9Lennert Buytenhek	if (processed && priv->radio_on && !mutex_is_locked(&priv->fw_mutex))
1554a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		ieee80211_wake_queue(hw, index);
1555efb7c49a68cf206f35793d7799608e1d69a209f9Lennert Buytenhek
1556efb7c49a68cf206f35793d7799608e1d69a209f9Lennert Buytenhek	return processed;
1557a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1558a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1559a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/* must be called only when the card's transmit is completely halted */
1560a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index)
1561a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1562a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
1563a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_tx_queue *txq = priv->txq + index;
1564a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1565efb7c49a68cf206f35793d7799608e1d69a209f9Lennert Buytenhek	mwl8k_txq_reclaim(hw, index, INT_MAX, 1);
1566a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
156745eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	kfree(txq->skb);
156845eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	txq->skb = NULL;
1569a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1570a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	pci_free_consistent(priv->pdev,
1571a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			    MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc),
157245eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek			    txq->txd, txq->txd_dma);
157345eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	txq->txd = NULL;
1574a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1575a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1576a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int
1577a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekmwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
1578a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1579a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
1580a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct ieee80211_tx_info *tx_info;
158123b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	struct mwl8k_vif *mwl8k_vif;
1582a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct ieee80211_hdr *wh;
1583a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_tx_queue *txq;
1584a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_tx_desc *tx;
1585a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dma_addr_t dma;
158623b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	u32 txstatus;
158723b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	u8 txdatarate;
158823b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	u16 qos;
1589a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
159023b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	wh = (struct ieee80211_hdr *)skb->data;
159123b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	if (ieee80211_is_data_qos(wh->frame_control))
159223b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek		qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh)));
159323b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	else
159423b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek		qos = 0;
1595a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1596d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	if (priv->ap_fw)
1597d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		mwl8k_encapsulate_tx_frame(skb);
1598d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	else
1599d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		mwl8k_add_dma_header(skb, 0);
1600d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
160123b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	wh = &((struct mwl8k_dma_data *)skb->data)->wh;
1602a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1603a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	tx_info = IEEE80211_SKB_CB(skb);
1604a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	mwl8k_vif = MWL8K_VIF(tx_info->control.vif);
1605a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1606a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
1607a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
1608657232b625890f202867ede0ed67ecf15827ff80Lennert Buytenhek		wh->seq_ctrl |= cpu_to_le16(mwl8k_vif->seqno);
1609657232b625890f202867ede0ed67ecf15827ff80Lennert Buytenhek		mwl8k_vif->seqno += 0x10;
1610a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
1611a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
161223b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	/* Setup firmware control bit fields for each frame type.  */
161323b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	txstatus = 0;
161423b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	txdatarate = 0;
161523b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	if (ieee80211_is_mgmt(wh->frame_control) ||
161623b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	    ieee80211_is_ctl(wh->frame_control)) {
161723b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek		txdatarate = 0;
1618e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhek		qos |= MWL8K_QOS_QLEN_UNSPEC | MWL8K_QOS_EOSP;
161923b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	} else if (ieee80211_is_data(wh->frame_control)) {
162023b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek		txdatarate = 1;
162123b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek		if (is_multicast_ether_addr(wh->addr1))
162223b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek			txstatus |= MWL8K_TXD_STATUS_MULTICAST_TX;
162323b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek
1624e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhek		qos &= ~MWL8K_QOS_ACK_POLICY_MASK;
162523b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
1626e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhek			qos |= MWL8K_QOS_ACK_POLICY_BLOCKACK;
162723b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek		else
1628e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhek			qos |= MWL8K_QOS_ACK_POLICY_NORMAL;
162923b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	}
1630a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1631a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dma = pci_map_single(priv->pdev, skb->data,
1632a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				skb->len, PCI_DMA_TODEVICE);
1633a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1634a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (pci_dma_mapping_error(priv->pdev, dma)) {
1635c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches		wiphy_debug(hw->wiphy,
1636c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches			    "failed to dma map skb, dropping TX frame.\n");
163723b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek		dev_kfree_skb(skb);
1638a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return NETDEV_TX_OK;
1639a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
1640a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
164123b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	spin_lock_bh(&priv->tx_lock);
1642a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
164323b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	txq = priv->txq + index;
1644a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
164545eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	BUG_ON(txq->skb[txq->tail] != NULL);
164645eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	txq->skb[txq->tail] = skb;
1647a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
164845eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	tx = txq->txd + txq->tail;
164923b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	tx->data_rate = txdatarate;
165023b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	tx->tx_priority = index;
1651a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	tx->qos_control = cpu_to_le16(qos);
1652a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	tx->pkt_phys_addr = cpu_to_le32(dma);
1653a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	tx->pkt_len = cpu_to_le16(skb->len);
165423b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	tx->rate_info = 0;
1655a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	if (!priv->ap_fw && tx_info->control.sta != NULL)
1656a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek		tx->peer_id = MWL8K_STA(tx_info->control.sta)->peer_id;
1657a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	else
1658a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek		tx->peer_id = 0;
1659a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	wmb();
166023b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus);
166123b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek
16628ccbc3b8b0c919e8609560ca56cd777ece8d2c41Kalle Valo	txq->len++;
1663a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	priv->pending_tx_pkts++;
1664a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
166545eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	txq->tail++;
166645eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	if (txq->tail == MWL8K_TX_DESCS)
166745eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		txq->tail = 0;
166823b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek
166945eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	if (txq->head == txq->tail)
1670a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		ieee80211_stop_queue(hw, index);
1671a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
167223b339062f247e0be84eaabb15e17b403c4388b6Lennert Buytenhek	mwl8k_tx_start(priv);
1673a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1674a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	spin_unlock_bh(&priv->tx_lock);
1675a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1676a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return NETDEV_TX_OK;
1677a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1678a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1679a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1680a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
1681618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek * Firmware access.
1682618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek *
1683618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek * We have the following requirements for issuing firmware commands:
1684618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek * - Some commands require that the packet transmit path is idle when
1685618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek *   the command is issued.  (For simplicity, we'll just quiesce the
1686618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek *   transmit path for every command.)
1687618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek * - There are certain sequences of commands that need to be issued to
1688618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek *   the hardware sequentially, with no other intervening commands.
1689618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek *
1690618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek * This leads to an implementation of a "firmware lock" as a mutex that
1691618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek * can be taken recursively, and which is taken by both the low-level
1692618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek * command submission function (mwl8k_post_cmd) as well as any users of
1693618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek * that function that require issuing of an atomic sequence of commands,
1694618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek * and quiesces the transmit path whenever it's taken.
1695618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek */
1696618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhekstatic int mwl8k_fw_lock(struct ieee80211_hw *hw)
1697618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek{
1698618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
1699618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
1700618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	if (priv->fw_mutex_owner != current) {
1701618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek		int rc;
1702618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
1703618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek		mutex_lock(&priv->fw_mutex);
1704618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek		ieee80211_stop_queues(hw);
1705618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
1706618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek		rc = mwl8k_tx_wait_empty(hw);
1707618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek		if (rc) {
1708618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek			ieee80211_wake_queues(hw);
1709618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek			mutex_unlock(&priv->fw_mutex);
1710618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
1711618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek			return rc;
1712618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek		}
1713618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
1714618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek		priv->fw_mutex_owner = current;
1715618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	}
1716618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
1717618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	priv->fw_mutex_depth++;
1718618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
1719618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	return 0;
1720618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek}
1721618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
1722618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhekstatic void mwl8k_fw_unlock(struct ieee80211_hw *hw)
1723618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek{
1724618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
1725618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
1726618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	if (!--priv->fw_mutex_depth) {
1727618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek		ieee80211_wake_queues(hw);
1728618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek		priv->fw_mutex_owner = NULL;
1729618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek		mutex_unlock(&priv->fw_mutex);
1730618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	}
1731618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek}
1732618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
1733618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
1734618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek/*
1735a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * Command processing.
1736a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
1737a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
17380c9cc640225f4bd7c9aad87b1431bd8d9a29b338Lennert Buytenhek/* Timeout firmware commands after 10s */
17390c9cc640225f4bd7c9aad87b1431bd8d9a29b338Lennert Buytenhek#define MWL8K_CMD_TIMEOUT_MS	10000
1740a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1741a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
1742a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1743a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	DECLARE_COMPLETION_ONSTACK(cmd_wait);
1744a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
1745a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	void __iomem *regs = priv->regs;
1746a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dma_addr_t dma_addr;
1747a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	unsigned int dma_size;
1748a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
1749a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	unsigned long timeout = 0;
1750a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	u8 buf[32];
1751a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1752b603742f49c3ec922522602e18ac22e8f6835132John W. Linville	cmd->result = (__force __le16) 0xffff;
1753a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dma_size = le16_to_cpu(cmd->length);
1754a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dma_addr = pci_map_single(priv->pdev, cmd, dma_size,
1755a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				  PCI_DMA_BIDIRECTIONAL);
1756a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (pci_dma_mapping_error(priv->pdev, dma_addr))
1757a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
1758a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1759618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	rc = mwl8k_fw_lock(hw);
176039a1e42eb4d0a2bc3f1211e9012bd23734ab86dbLennert Buytenhek	if (rc) {
176139a1e42eb4d0a2bc3f1211e9012bd23734ab86dbLennert Buytenhek		pci_unmap_single(priv->pdev, dma_addr, dma_size,
176239a1e42eb4d0a2bc3f1211e9012bd23734ab86dbLennert Buytenhek						PCI_DMA_BIDIRECTIONAL);
1763618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek		return rc;
176439a1e42eb4d0a2bc3f1211e9012bd23734ab86dbLennert Buytenhek	}
1765a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1766a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	priv->hostcmd_wait = &cmd_wait;
1767a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR);
1768a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(MWL8K_H2A_INT_DOORBELL,
1769a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
1770a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(MWL8K_H2A_INT_DUMMY,
1771a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
1772a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1773a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	timeout = wait_for_completion_timeout(&cmd_wait,
1774a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				msecs_to_jiffies(MWL8K_CMD_TIMEOUT_MS));
1775a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1776618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	priv->hostcmd_wait = NULL;
1777618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
1778618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek	mwl8k_fw_unlock(hw);
1779618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
178037055bd455b31b8220c35a1ede9c6aceb791cc88Lennert Buytenhek	pci_unmap_single(priv->pdev, dma_addr, dma_size,
178137055bd455b31b8220c35a1ede9c6aceb791cc88Lennert Buytenhek					PCI_DMA_BIDIRECTIONAL);
178237055bd455b31b8220c35a1ede9c6aceb791cc88Lennert Buytenhek
1783a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (!timeout) {
17845db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches		wiphy_err(hw->wiphy, "Command %s timeout after %u ms\n",
1785c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches			  mwl8k_cmd_name(cmd->code, buf, sizeof(buf)),
1786c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches			  MWL8K_CMD_TIMEOUT_MS);
1787a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		rc = -ETIMEDOUT;
1788a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	} else {
17890c9cc640225f4bd7c9aad87b1431bd8d9a29b338Lennert Buytenhek		int ms;
17900c9cc640225f4bd7c9aad87b1431bd8d9a29b338Lennert Buytenhek
17910c9cc640225f4bd7c9aad87b1431bd8d9a29b338Lennert Buytenhek		ms = MWL8K_CMD_TIMEOUT_MS - jiffies_to_msecs(timeout);
17920c9cc640225f4bd7c9aad87b1431bd8d9a29b338Lennert Buytenhek
1793ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek		rc = cmd->result ? -EINVAL : 0;
1794a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (rc)
17955db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches			wiphy_err(hw->wiphy, "Command %s error 0x%x\n",
1796c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches				  mwl8k_cmd_name(cmd->code, buf, sizeof(buf)),
1797c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches				  le16_to_cpu(cmd->result));
17980c9cc640225f4bd7c9aad87b1431bd8d9a29b338Lennert Buytenhek		else if (ms > 2000)
17995db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches			wiphy_notice(hw->wiphy, "Command %s took %d ms\n",
1800c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches				     mwl8k_cmd_name(cmd->code,
1801c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches						    buf, sizeof(buf)),
1802c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches				     ms);
1803a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
1804a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1805a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
1806a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1807a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1808f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhekstatic int mwl8k_post_pervif_cmd(struct ieee80211_hw *hw,
1809f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek				 struct ieee80211_vif *vif,
1810f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek				 struct mwl8k_cmd_pkt *cmd)
1811f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek{
1812f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek	if (vif != NULL)
1813f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek		cmd->macid = MWL8K_VIF(vif)->macid;
1814f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek	return mwl8k_post_cmd(hw, cmd);
1815f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek}
1816f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek
1817a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
18181349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek * Setup code shared between STA and AP firmware images.
18191349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek */
18201349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhekstatic void mwl8k_setup_2ghz_band(struct ieee80211_hw *hw)
18211349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek{
18221349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
18231349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek
18241349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek	BUILD_BUG_ON(sizeof(priv->channels_24) != sizeof(mwl8k_channels_24));
18251349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek	memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24));
18261349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek
18271349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek	BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24));
18281349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek	memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24));
18291349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek
18301349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek	priv->band_24.band = IEEE80211_BAND_2GHZ;
18311349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek	priv->band_24.channels = priv->channels_24;
18321349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek	priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24);
18331349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek	priv->band_24.bitrates = priv->rates_24;
18341349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek	priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24);
18351349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek
18361349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek	hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band_24;
18371349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek}
18381349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek
18394eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhekstatic void mwl8k_setup_5ghz_band(struct ieee80211_hw *hw)
18404eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek{
18414eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
18424eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek
18434eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	BUILD_BUG_ON(sizeof(priv->channels_50) != sizeof(mwl8k_channels_50));
18444eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	memcpy(priv->channels_50, mwl8k_channels_50, sizeof(mwl8k_channels_50));
18454eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek
18464eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl8k_rates_50));
18474eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	memcpy(priv->rates_50, mwl8k_rates_50, sizeof(mwl8k_rates_50));
18484eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek
18494eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	priv->band_50.band = IEEE80211_BAND_5GHZ;
18504eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	priv->band_50.channels = priv->channels_50;
18514eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	priv->band_50.n_channels = ARRAY_SIZE(mwl8k_channels_50);
18524eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	priv->band_50.bitrates = priv->rates_50;
18534eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	priv->band_50.n_bitrates = ARRAY_SIZE(mwl8k_rates_50);
18544eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek
18554eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &priv->band_50;
18564eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek}
18574eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek
18581349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek/*
185904b147b19303724aac5ee8e56f113f1935a5c255Lennert Buytenhek * CMD_GET_HW_SPEC (STA version).
1860a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
186104b147b19303724aac5ee8e56f113f1935a5c255Lennert Buytenhekstruct mwl8k_cmd_get_hw_spec_sta {
1862a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_pkt header;
1863a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 hw_rev;
1864a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 host_interface;
1865a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 num_mcaddrs;
1866d89173f25228b8795af2d4b53e985cc44c729332Lennert Buytenhek	__u8 perm_addr[ETH_ALEN];
1867a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 region_code;
1868a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 fw_rev;
1869a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 ps_cookie;
1870a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 caps;
1871a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 mcs_bitmap[16];
1872a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 rx_queue_ptr;
1873a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 num_tx_queues;
1874a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 tx_queue_ptrs[MWL8K_TX_QUEUES];
1875a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 caps2;
1876a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 num_tx_desc_per_queue;
187745eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	__le32 total_rxd;
1878ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
1879a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1880341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek#define MWL8K_CAP_MAX_AMSDU		0x20000000
1881341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek#define MWL8K_CAP_GREENFIELD		0x08000000
1882341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek#define MWL8K_CAP_AMPDU			0x04000000
1883341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek#define MWL8K_CAP_RX_STBC		0x01000000
1884341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek#define MWL8K_CAP_TX_STBC		0x00800000
1885341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek#define MWL8K_CAP_SHORTGI_40MHZ		0x00400000
1886341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek#define MWL8K_CAP_SHORTGI_20MHZ		0x00200000
1887341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek#define MWL8K_CAP_RX_ANTENNA_MASK	0x000e0000
1888341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek#define MWL8K_CAP_TX_ANTENNA_MASK	0x0001c000
1889341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek#define MWL8K_CAP_DELAY_BA		0x00003000
1890341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek#define MWL8K_CAP_MIMO			0x00000200
1891341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek#define MWL8K_CAP_40MHZ			0x00000100
189206953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek#define MWL8K_CAP_BAND_MASK		0x00000007
189306953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek#define MWL8K_CAP_5GHZ			0x00000004
189406953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek#define MWL8K_CAP_2GHZ4			0x00000001
1895341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek
189606953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhekstatic void
189706953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhekmwl8k_set_ht_caps(struct ieee80211_hw *hw,
189806953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek		  struct ieee80211_supported_band *band, u32 cap)
1899341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek{
1900341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	int rx_streams;
1901341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	int tx_streams;
1902341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek
1903777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek	band->ht_cap.ht_supported = 1;
1904341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek
1905341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	if (cap & MWL8K_CAP_MAX_AMSDU)
1906777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		band->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
1907341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	if (cap & MWL8K_CAP_GREENFIELD)
1908777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		band->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD;
1909341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	if (cap & MWL8K_CAP_AMPDU) {
1910341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek		hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
1911777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
1912777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
1913341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	}
1914341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	if (cap & MWL8K_CAP_RX_STBC)
1915777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		band->ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC;
1916341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	if (cap & MWL8K_CAP_TX_STBC)
1917777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		band->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
1918341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	if (cap & MWL8K_CAP_SHORTGI_40MHZ)
1919777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
1920341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	if (cap & MWL8K_CAP_SHORTGI_20MHZ)
1921777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
1922341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	if (cap & MWL8K_CAP_DELAY_BA)
1923777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		band->ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA;
1924341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	if (cap & MWL8K_CAP_40MHZ)
1925777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
1926341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek
1927341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	rx_streams = hweight32(cap & MWL8K_CAP_RX_ANTENNA_MASK);
1928341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	tx_streams = hweight32(cap & MWL8K_CAP_TX_ANTENNA_MASK);
1929341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek
1930777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek	band->ht_cap.mcs.rx_mask[0] = 0xff;
1931341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	if (rx_streams >= 2)
1932777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		band->ht_cap.mcs.rx_mask[1] = 0xff;
1933341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	if (rx_streams >= 3)
1934777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		band->ht_cap.mcs.rx_mask[2] = 0xff;
1935777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek	band->ht_cap.mcs.rx_mask[4] = 0x01;
1936777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek	band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
1937341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek
1938341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	if (rx_streams != tx_streams) {
1939777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		band->ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
1940777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		band->ht_cap.mcs.tx_params |= (tx_streams - 1) <<
1941341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek				IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
1942341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek	}
1943341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek}
1944341c97918ab7e84a155ea8b18759425304d213b6Lennert Buytenhek
194506953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhekstatic void
194606953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhekmwl8k_set_caps(struct ieee80211_hw *hw, u32 caps)
194706953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek{
194806953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
194906953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek
195006953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek	if ((caps & MWL8K_CAP_2GHZ4) || !(caps & MWL8K_CAP_BAND_MASK)) {
195106953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek		mwl8k_setup_2ghz_band(hw);
195206953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek		if (caps & MWL8K_CAP_MIMO)
195306953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek			mwl8k_set_ht_caps(hw, &priv->band_24, caps);
195406953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek	}
195506953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek
195606953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek	if (caps & MWL8K_CAP_5GHZ) {
195706953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek		mwl8k_setup_5ghz_band(hw);
195806953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek		if (caps & MWL8K_CAP_MIMO)
195906953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek			mwl8k_set_ht_caps(hw, &priv->band_50, caps);
196006953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek	}
196106953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek}
196206953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek
196304b147b19303724aac5ee8e56f113f1935a5c255Lennert Buytenhekstatic int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw)
1964a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1965a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
196604b147b19303724aac5ee8e56f113f1935a5c255Lennert Buytenhek	struct mwl8k_cmd_get_hw_spec_sta *cmd;
1967a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
1968a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int i;
1969a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1970a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
1971a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
1972a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
1973a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1974a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC);
1975a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
1976a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1977a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr));
1978a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
197945eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma);
19804ff6432ea620ba467e50ec04b8271ea0eb94e62eLennert Buytenhek	cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES);
1981a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	for (i = 0; i < MWL8K_TX_QUEUES; i++)
198245eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma);
19834ff6432ea620ba467e50ec04b8271ea0eb94e62eLennert Buytenhek	cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);
198445eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS);
1985a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1986a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
1987a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1988a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (!rc) {
1989a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr);
1990a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
19914ff6432ea620ba467e50ec04b8271ea0eb94e62eLennert Buytenhek		priv->fw_rev = le32_to_cpu(cmd->fw_rev);
1992a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		priv->hw_rev = cmd->hw_rev;
199306953235f48c696b22c5ed45570680fb070f7277Lennert Buytenhek		mwl8k_set_caps(hw, le32_to_cpu(cmd->caps));
1994ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek		priv->ap_macids_supported = 0x00000000;
1995ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek		priv->sta_macids_supported = 0x00000001;
1996a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
1997a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1998a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
1999a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
2000a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2001a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2002a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
200342fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek * CMD_GET_HW_SPEC (AP version).
200442fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek */
200542fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhekstruct mwl8k_cmd_get_hw_spec_ap {
200642fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	struct mwl8k_cmd_pkt header;
200742fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__u8 hw_rev;
200842fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__u8 host_interface;
200942fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le16 num_wcb;
201042fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le16 num_mcaddrs;
201142fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__u8 perm_addr[ETH_ALEN];
201242fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le16 region_code;
201342fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le16 num_antenna;
201442fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 fw_rev;
201542fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 wcbbase0;
201642fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 rxwrptr;
201742fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 rxrdptr;
201842fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 ps_cookie;
201942fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 wcbbase1;
202042fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 wcbbase2;
202142fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 wcbbase3;
2022952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo	__le32 fw_api_version;
2023ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
202442fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
202542fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhekstatic int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw)
202642fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek{
202742fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
202842fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	struct mwl8k_cmd_get_hw_spec_ap *cmd;
202942fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	int rc;
2030952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo	u32 api_version;
203142fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
203242fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
203342fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	if (cmd == NULL)
203442fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		return -ENOMEM;
203542fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
203642fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC);
203742fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
203842fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
203942fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr));
204042fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
204142fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
204242fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
204342fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
204442fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	if (!rc) {
204542fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		int off;
204642fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
2047952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo		api_version = le32_to_cpu(cmd->fw_api_version);
2048952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo		if (priv->device_info->fw_api_ap != api_version) {
2049952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo			printk(KERN_ERR "%s: Unsupported fw API version for %s."
2050952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo			       "  Expected %d got %d.\n", MWL8K_NAME,
2051952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo			       priv->device_info->part_name,
2052952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo			       priv->device_info->fw_api_ap,
2053952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo			       api_version);
2054952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo			rc = -EINVAL;
2055952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo			goto done;
2056952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo		}
205742fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr);
205842fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
205942fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		priv->fw_rev = le32_to_cpu(cmd->fw_rev);
206042fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		priv->hw_rev = cmd->hw_rev;
20611349ad2f06f86f41415cf7ffa9e068fd4f89be87Lennert Buytenhek		mwl8k_setup_2ghz_band(hw);
2062ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek		priv->ap_macids_supported = 0x000000ff;
2063ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek		priv->sta_macids_supported = 0x00000000;
206442fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
206542fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		off = le32_to_cpu(cmd->wcbbase0) & 0xffff;
2066b603742f49c3ec922522602e18ac22e8f6835132John W. Linville		iowrite32(priv->txq[0].txd_dma, priv->sram + off);
206742fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
206842fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		off = le32_to_cpu(cmd->rxwrptr) & 0xffff;
2069b603742f49c3ec922522602e18ac22e8f6835132John W. Linville		iowrite32(priv->rxq[0].rxd_dma, priv->sram + off);
207042fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
207142fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		off = le32_to_cpu(cmd->rxrdptr) & 0xffff;
2072b603742f49c3ec922522602e18ac22e8f6835132John W. Linville		iowrite32(priv->rxq[0].rxd_dma, priv->sram + off);
207342fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
207442fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		off = le32_to_cpu(cmd->wcbbase1) & 0xffff;
2075b603742f49c3ec922522602e18ac22e8f6835132John W. Linville		iowrite32(priv->txq[1].txd_dma, priv->sram + off);
207642fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
207742fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		off = le32_to_cpu(cmd->wcbbase2) & 0xffff;
2078b603742f49c3ec922522602e18ac22e8f6835132John W. Linville		iowrite32(priv->txq[2].txd_dma, priv->sram + off);
207942fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
208042fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		off = le32_to_cpu(cmd->wcbbase3) & 0xffff;
2081b603742f49c3ec922522602e18ac22e8f6835132John W. Linville		iowrite32(priv->txq[3].txd_dma, priv->sram + off);
208242fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	}
208342fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
2084952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolodone:
208542fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	kfree(cmd);
208642fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	return rc;
208742fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek}
208842fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
208942fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek/*
209042fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek * CMD_SET_HW_SPEC.
209142fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek */
209242fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhekstruct mwl8k_cmd_set_hw_spec {
209342fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	struct mwl8k_cmd_pkt header;
209442fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__u8 hw_rev;
209542fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__u8 host_interface;
209642fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le16 num_mcaddrs;
209742fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__u8 perm_addr[ETH_ALEN];
209842fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le16 region_code;
209942fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 fw_rev;
210042fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 ps_cookie;
210142fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 caps;
210242fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 rx_queue_ptr;
210342fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 num_tx_queues;
210442fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 tx_queue_ptrs[MWL8K_TX_QUEUES];
210542fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 flags;
210642fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 num_tx_desc_per_queue;
210742fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	__le32 total_rxd;
2108ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
210942fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
2110b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT		0x00000080
2111b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP	0x00000020
2112b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON		0x00000010
211342fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
211442fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhekstatic int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw)
211542fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek{
211642fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
211742fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	struct mwl8k_cmd_set_hw_spec *cmd;
211842fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	int rc;
211942fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	int i;
212042fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
212142fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
212242fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	if (cmd == NULL)
212342fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		return -ENOMEM;
212442fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
212542fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_HW_SPEC);
212642fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
212742fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
212842fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
212942fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma);
213042fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES);
213142fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	for (i = 0; i < MWL8K_TX_QUEUES; i++)
213242fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma);
2133b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT |
2134b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek				 MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP |
2135b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek				 MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON);
213642fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);
213742fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS);
213842fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
213942fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
214042fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	kfree(cmd);
214142fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
214242fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	return rc;
214342fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek}
214442fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek
214542fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek/*
2146a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * CMD_MAC_MULTICAST_ADR.
2147a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
2148a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstruct mwl8k_cmd_mac_multicast_adr {
2149a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_pkt header;
2150a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 action;
2151a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 numaddr;
2152ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek	__u8 addr[0][ETH_ALEN];
2153a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek};
2154a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2155d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek#define MWL8K_ENABLE_RX_DIRECTED	0x0001
2156d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek#define MWL8K_ENABLE_RX_MULTICAST	0x0002
2157d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek#define MWL8K_ENABLE_RX_ALL_MULTICAST	0x0004
2158d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek#define MWL8K_ENABLE_RX_BROADCAST	0x0008
2159ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek
2160e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhekstatic struct mwl8k_cmd_pkt *
2161447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek__mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti,
216222bedad3ce112d5ca1eaf043d4990fa2ed698c87Jiri Pirko			      struct netdev_hw_addr_list *mc_list)
2163a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
2164e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
2165a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_mac_multicast_adr *cmd;
2166e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhek	int size;
216722bedad3ce112d5ca1eaf043d4990fa2ed698c87Jiri Pirko	int mc_count = 0;
216822bedad3ce112d5ca1eaf043d4990fa2ed698c87Jiri Pirko
216922bedad3ce112d5ca1eaf043d4990fa2ed698c87Jiri Pirko	if (mc_list)
217022bedad3ce112d5ca1eaf043d4990fa2ed698c87Jiri Pirko		mc_count = netdev_hw_addr_list_count(mc_list);
2171e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhek
2172447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	if (allmulti || mc_count > priv->num_mcaddrs) {
2173d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek		allmulti = 1;
2174d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek		mc_count = 0;
2175d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek	}
2176e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhek
2177e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhek	size = sizeof(*cmd) + mc_count * ETH_ALEN;
2178ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek
2179e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhek	cmd = kzalloc(size, GFP_ATOMIC);
2180a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
2181e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhek		return NULL;
2182a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2183a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_MAC_MULTICAST_ADR);
2184a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(size);
2185d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek	cmd->action = cpu_to_le16(MWL8K_ENABLE_RX_DIRECTED |
2186d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek				  MWL8K_ENABLE_RX_BROADCAST);
2187d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek
2188d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek	if (allmulti) {
2189d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek		cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_ALL_MULTICAST);
2190d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek	} else if (mc_count) {
219122bedad3ce112d5ca1eaf043d4990fa2ed698c87Jiri Pirko		struct netdev_hw_addr *ha;
219222bedad3ce112d5ca1eaf043d4990fa2ed698c87Jiri Pirko		int i = 0;
2193d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek
2194d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek		cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST);
2195d5e308457e8e9b4988fbd69d38c30782125b3f65Lennert Buytenhek		cmd->numaddr = cpu_to_le16(mc_count);
219622bedad3ce112d5ca1eaf043d4990fa2ed698c87Jiri Pirko		netdev_hw_addr_list_for_each(ha, mc_list) {
219722bedad3ce112d5ca1eaf043d4990fa2ed698c87Jiri Pirko			memcpy(cmd->addr[i], ha->addr, ETH_ALEN);
2198a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		}
2199a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
2200a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2201e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhek	return &cmd->header;
2202a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2203a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2204a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
220555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek * CMD_GET_STAT.
2206a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
220755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstruct mwl8k_cmd_get_stat {
2208a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_pkt header;
2209a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 stats[64];
2210ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
2211a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2212a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_STAT_ACK_FAILURE	9
2213a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_STAT_RTS_FAILURE	12
2214a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_STAT_FCS_ERROR	24
2215a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_STAT_RTS_SUCCESS	11
2216a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
221755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstatic int mwl8k_cmd_get_stat(struct ieee80211_hw *hw,
221855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			      struct ieee80211_low_level_stats *stats)
2219a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
222055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_get_stat *cmd;
2221a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
2222a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2223a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2224a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
2225a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
2226a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2227a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_STAT);
2228a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
2229a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2230a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
2231a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (!rc) {
2232a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		stats->dot11ACKFailureCount =
2233a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			le32_to_cpu(cmd->stats[MWL8K_STAT_ACK_FAILURE]);
2234a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		stats->dot11RTSFailureCount =
2235a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_FAILURE]);
2236a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		stats->dot11FCSErrorCount =
2237a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			le32_to_cpu(cmd->stats[MWL8K_STAT_FCS_ERROR]);
2238a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		stats->dot11RTSSuccessCount =
2239a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_SUCCESS]);
2240a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
2241a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
2242a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2243a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
2244a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2245a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2246a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
224755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek * CMD_RADIO_CONTROL.
2248a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
224955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstruct mwl8k_cmd_radio_control {
2250a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_pkt header;
2251a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 action;
2252a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 control;
2253a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 radio_on;
2254ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
2255a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2256c46563b714b09d44eec4d1fd8a0f53e79ddaa3aaLennert Buytenhekstatic int
225755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekmwl8k_cmd_radio_control(struct ieee80211_hw *hw, bool enable, bool force)
2258a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
2259a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
226055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_radio_control *cmd;
2261a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
2262a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2263c46563b714b09d44eec4d1fd8a0f53e79ddaa3aaLennert Buytenhek	if (enable == priv->radio_on && !force)
2264a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return 0;
2265a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2266a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2267a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
2268a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
2269a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2270a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_RADIO_CONTROL);
2271a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
2272a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
227368ce38845c23443b15e374fb7362916c1231278eLennert Buytenhek	cmd->control = cpu_to_le16(priv->radio_short_preamble ? 3 : 1);
2274a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->radio_on = cpu_to_le16(enable ? 0x0001 : 0x0000);
2275a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2276a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
2277a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
2278a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2279a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (!rc)
2280c46563b714b09d44eec4d1fd8a0f53e79ddaa3aaLennert Buytenhek		priv->radio_on = enable;
2281a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2282a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
2283a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2284a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
228555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstatic int mwl8k_cmd_radio_disable(struct ieee80211_hw *hw)
2286c46563b714b09d44eec4d1fd8a0f53e79ddaa3aaLennert Buytenhek{
228755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	return mwl8k_cmd_radio_control(hw, 0, 0);
2288c46563b714b09d44eec4d1fd8a0f53e79ddaa3aaLennert Buytenhek}
2289c46563b714b09d44eec4d1fd8a0f53e79ddaa3aaLennert Buytenhek
229055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstatic int mwl8k_cmd_radio_enable(struct ieee80211_hw *hw)
2291c46563b714b09d44eec4d1fd8a0f53e79ddaa3aaLennert Buytenhek{
229255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	return mwl8k_cmd_radio_control(hw, 1, 0);
2293c46563b714b09d44eec4d1fd8a0f53e79ddaa3aaLennert Buytenhek}
2294c46563b714b09d44eec4d1fd8a0f53e79ddaa3aaLennert Buytenhek
2295a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int
2296a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekmwl8k_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble)
2297a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
229899200a992e365a73dc67a6570524e5f3af4386ddLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
2299a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
230068ce38845c23443b15e374fb7362916c1231278eLennert Buytenhek	priv->radio_short_preamble = short_preamble;
2301a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
230255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	return mwl8k_cmd_radio_control(hw, 1, 1);
2303a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2304a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2305a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
230655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek * CMD_RF_TX_POWER.
2307a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
230841fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam#define MWL8K_RF_TX_POWER_LEVEL_TOTAL	8
2309a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
231055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstruct mwl8k_cmd_rf_tx_power {
2311a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_pkt header;
2312a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 action;
2313a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 support_level;
2314a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 current_level;
2315a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 reserved;
231641fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	__le16 power_level_list[MWL8K_RF_TX_POWER_LEVEL_TOTAL];
2317ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
2318a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
231955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstatic int mwl8k_cmd_rf_tx_power(struct ieee80211_hw *hw, int dBm)
2320a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
232155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_rf_tx_power *cmd;
2322a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
2323a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2324a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2325a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
2326a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
2327a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2328a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_TX_POWER);
2329a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
2330a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
2331a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->support_level = cpu_to_le16(dBm);
2332a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2333a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
2334a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
2335a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2336a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
2337a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2338a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2339a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
234041fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam * CMD_TX_POWER.
234141fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam */
234241fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam#define MWL8K_TX_POWER_LEVEL_TOTAL      12
234341fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam
234441fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadamstruct mwl8k_cmd_tx_power {
234541fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	struct mwl8k_cmd_pkt header;
234641fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	__le16 action;
234741fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	__le16 band;
234841fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	__le16 channel;
234941fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	__le16 bw;
235041fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	__le16 sub_ch;
235141fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	__le16 power_level_list[MWL8K_TX_POWER_LEVEL_TOTAL];
235241fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam} __attribute__((packed));
235341fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam
235441fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadamstatic int mwl8k_cmd_tx_power(struct ieee80211_hw *hw,
235541fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam				     struct ieee80211_conf *conf,
235641fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam				     unsigned short pwr)
235741fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam{
235841fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	struct ieee80211_channel *channel = conf->channel;
235941fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	struct mwl8k_cmd_tx_power *cmd;
236041fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	int rc;
236141fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	int i;
236241fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam
236341fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
236441fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	if (cmd == NULL)
236541fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam		return -ENOMEM;
236641fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam
236741fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	cmd->header.code = cpu_to_le16(MWL8K_CMD_TX_POWER);
236841fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	cmd->header.length = cpu_to_le16(sizeof(*cmd));
236941fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	cmd->action = cpu_to_le16(MWL8K_CMD_SET_LIST);
237041fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam
237141fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	if (channel->band == IEEE80211_BAND_2GHZ)
237241fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam		cmd->band = cpu_to_le16(0x1);
237341fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	else if (channel->band == IEEE80211_BAND_5GHZ)
237441fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam		cmd->band = cpu_to_le16(0x4);
237541fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam
237641fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	cmd->channel = channel->hw_value;
237741fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam
237841fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	if (conf->channel_type == NL80211_CHAN_NO_HT ||
237941fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	    conf->channel_type == NL80211_CHAN_HT20) {
238041fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam		cmd->bw = cpu_to_le16(0x2);
238141fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	} else {
238241fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam		cmd->bw = cpu_to_le16(0x4);
238341fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam		if (conf->channel_type == NL80211_CHAN_HT40MINUS)
238441fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam			cmd->sub_ch = cpu_to_le16(0x3);
238541fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam		else if (conf->channel_type == NL80211_CHAN_HT40PLUS)
238641fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam			cmd->sub_ch = cpu_to_le16(0x1);
238741fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	}
238841fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam
238941fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	for (i = 0; i < MWL8K_TX_POWER_LEVEL_TOTAL; i++)
239041fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam		cmd->power_level_list[i] = cpu_to_le16(pwr);
239141fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam
239241fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	rc = mwl8k_post_cmd(hw, &cmd->header);
239341fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	kfree(cmd);
239441fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam
239541fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	return rc;
239641fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam}
239741fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam
239841fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam/*
239908b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek * CMD_RF_ANTENNA.
240008b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek */
240108b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhekstruct mwl8k_cmd_rf_antenna {
240208b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	struct mwl8k_cmd_pkt header;
240308b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	__le16 antenna;
240408b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	__le16 mode;
2405ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
240608b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek
240708b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek#define MWL8K_RF_ANTENNA_RX		1
240808b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek#define MWL8K_RF_ANTENNA_TX		2
240908b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek
241008b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhekstatic int
241108b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhekmwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask)
241208b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek{
241308b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	struct mwl8k_cmd_rf_antenna *cmd;
241408b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	int rc;
241508b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek
241608b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
241708b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	if (cmd == NULL)
241808b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek		return -ENOMEM;
241908b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek
242008b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_ANTENNA);
242108b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
242208b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	cmd->antenna = cpu_to_le16(antenna);
242308b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	cmd->mode = cpu_to_le16(mask);
242408b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek
242508b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
242608b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	kfree(cmd);
242708b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek
242808b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	return rc;
242908b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek}
243008b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek
243108b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek/*
2432b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek * CMD_SET_BEACON.
2433b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek */
2434b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhekstruct mwl8k_cmd_set_beacon {
2435b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	struct mwl8k_cmd_pkt header;
2436b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	__le16 beacon_len;
2437b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	__u8 beacon[0];
2438b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek};
2439b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
2440aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhekstatic int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw,
2441aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek				struct ieee80211_vif *vif, u8 *beacon, int len)
2442b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek{
2443b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	struct mwl8k_cmd_set_beacon *cmd;
2444b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	int rc;
2445b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
2446b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	cmd = kzalloc(sizeof(*cmd) + len, GFP_KERNEL);
2447b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	if (cmd == NULL)
2448b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		return -ENOMEM;
2449b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
2450b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_BEACON);
2451b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd) + len);
2452b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	cmd->beacon_len = cpu_to_le16(len);
2453b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	memcpy(cmd->beacon, beacon, len);
2454b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
2455aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
2456b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	kfree(cmd);
2457b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
2458b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	return rc;
2459b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek}
2460b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
2461b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek/*
2462a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * CMD_SET_PRE_SCAN.
2463a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
2464a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstruct mwl8k_cmd_set_pre_scan {
2465a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_pkt header;
2466ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
2467a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2468a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int mwl8k_cmd_set_pre_scan(struct ieee80211_hw *hw)
2469a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
2470a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_set_pre_scan *cmd;
2471a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
2472a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2473a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2474a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
2475a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
2476a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2477a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_PRE_SCAN);
2478a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
2479a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2480a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
2481a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
2482a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2483a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
2484a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2485a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2486a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
2487a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * CMD_SET_POST_SCAN.
2488a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
2489a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstruct mwl8k_cmd_set_post_scan {
2490a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_pkt header;
2491a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 isibss;
2492d89173f25228b8795af2d4b53e985cc44c729332Lennert Buytenhek	__u8 bssid[ETH_ALEN];
2493ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
2494a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2495a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int
24960a11dfc36604d9b24deda17461b7ea69851846aaLennert Buytenhekmwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac)
2497a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
2498a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_set_post_scan *cmd;
2499a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
2500a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2501a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2502a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
2503a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
2504a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2505a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_POST_SCAN);
2506a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
2507a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->isibss = 0;
2508d89173f25228b8795af2d4b53e985cc44c729332Lennert Buytenhek	memcpy(cmd->bssid, mac, ETH_ALEN);
2509a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2510a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
2511a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
2512a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2513a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
2514a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2515a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2516a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
2517a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * CMD_SET_RF_CHANNEL.
2518a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
2519a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstruct mwl8k_cmd_set_rf_channel {
2520a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_pkt header;
2521a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 action;
2522a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 current_channel;
2523a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le32 channel_flags;
2524ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
2525a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2526a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw,
2527610677d2f0415570a7590790e8be376a946cd08bLennert Buytenhek				    struct ieee80211_conf *conf)
2528a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
2529610677d2f0415570a7590790e8be376a946cd08bLennert Buytenhek	struct ieee80211_channel *channel = conf->channel;
2530a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_set_rf_channel *cmd;
2531a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
2532a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2533a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2534a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
2535a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
2536a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2537a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RF_CHANNEL);
2538a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
2539a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
2540a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->current_channel = channel->hw_value;
2541610677d2f0415570a7590790e8be376a946cd08bLennert Buytenhek
2542a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (channel->band == IEEE80211_BAND_2GHZ)
2543610677d2f0415570a7590790e8be376a946cd08bLennert Buytenhek		cmd->channel_flags |= cpu_to_le32(0x00000001);
254442574ea2274ec0a2a9c58ab01be91b65e60a2291Lennert Buytenhek	else if (channel->band == IEEE80211_BAND_5GHZ)
254542574ea2274ec0a2a9c58ab01be91b65e60a2291Lennert Buytenhek		cmd->channel_flags |= cpu_to_le32(0x00000004);
2546610677d2f0415570a7590790e8be376a946cd08bLennert Buytenhek
2547610677d2f0415570a7590790e8be376a946cd08bLennert Buytenhek	if (conf->channel_type == NL80211_CHAN_NO_HT ||
2548610677d2f0415570a7590790e8be376a946cd08bLennert Buytenhek	    conf->channel_type == NL80211_CHAN_HT20)
2549610677d2f0415570a7590790e8be376a946cd08bLennert Buytenhek		cmd->channel_flags |= cpu_to_le32(0x00000080);
2550610677d2f0415570a7590790e8be376a946cd08bLennert Buytenhek	else if (conf->channel_type == NL80211_CHAN_HT40MINUS)
2551610677d2f0415570a7590790e8be376a946cd08bLennert Buytenhek		cmd->channel_flags |= cpu_to_le32(0x000001900);
2552610677d2f0415570a7590790e8be376a946cd08bLennert Buytenhek	else if (conf->channel_type == NL80211_CHAN_HT40PLUS)
2553610677d2f0415570a7590790e8be376a946cd08bLennert Buytenhek		cmd->channel_flags |= cpu_to_le32(0x000000900);
2554a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2555a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
2556a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
2557a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2558a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
2559a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2560a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2561a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
256255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek * CMD_SET_AID.
2563a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
256455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek#define MWL8K_FRAME_PROT_DISABLED			0x00
256555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek#define MWL8K_FRAME_PROT_11G				0x07
256655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek#define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY		0x02
256755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek#define MWL8K_FRAME_PROT_11N_HT_ALL			0x06
2568a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
256955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstruct mwl8k_cmd_update_set_aid {
257055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct	mwl8k_cmd_pkt header;
257155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__le16	aid;
2572a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
257355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	 /* AP's MAC address (BSSID) */
257455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__u8	bssid[ETH_ALEN];
257555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__le16	protection_mode;
257655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__u8	supp_rates[14];
2577ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
2578a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2579c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhekstatic void legacy_rate_mask_to_array(u8 *rates, u32 mask)
2580c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek{
2581c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek	int i;
2582c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek	int j;
2583c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek
2584c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek	/*
2585c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek	 * Clear nonstandard rates 4 and 13.
2586c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek	 */
2587c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek	mask &= 0x1fef;
2588c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek
2589c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek	for (i = 0, j = 0; i < 14; i++) {
2590c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek		if (mask & (1 << i))
2591777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek			rates[j++] = mwl8k_rates_24[i].hw_value;
2592c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek	}
2593c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek}
2594c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek
259555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstatic int
2596c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhekmwl8k_cmd_set_aid(struct ieee80211_hw *hw,
2597c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek		  struct ieee80211_vif *vif, u32 legacy_rate_mask)
2598a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
259955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_update_set_aid *cmd;
260055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	u16 prot_mode;
2601a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
2602a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2603a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2604a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
2605a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
2606a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
260755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID);
2608a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
26097dc6a7a7635365b140af969e972900866d0bf34bLennert Buytenhek	cmd->aid = cpu_to_le16(vif->bss_conf.aid);
26100a11dfc36604d9b24deda17461b7ea69851846aaLennert Buytenhek	memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN);
2611a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
26127dc6a7a7635365b140af969e972900866d0bf34bLennert Buytenhek	if (vif->bss_conf.use_cts_prot) {
261355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		prot_mode = MWL8K_FRAME_PROT_11G;
261455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	} else {
26157dc6a7a7635365b140af969e972900866d0bf34bLennert Buytenhek		switch (vif->bss_conf.ht_operation_mode &
261655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			IEEE80211_HT_OP_MODE_PROTECTION) {
261755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
261855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY;
261955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			break;
262055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
262155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL;
262255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			break;
262355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		default:
262455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			prot_mode = MWL8K_FRAME_PROT_DISABLED;
262555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			break;
262655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		}
262755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	}
262855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->protection_mode = cpu_to_le16(prot_mode);
2629a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2630c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek	legacy_rate_mask_to_array(cmd->supp_rates, legacy_rate_mask);
2631a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2632a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
2633a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
2634a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2635a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
2636a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2637a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2638a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
263955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek * CMD_SET_RATE.
264032060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek */
264155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstruct mwl8k_cmd_set_rate {
264255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct	mwl8k_cmd_pkt header;
264355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__u8	legacy_rates[14];
264455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
264555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	/* Bitmap for supported MCS codes.  */
264655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__u8	mcs_set[16];
264755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__u8	reserved[16];
2648ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
264932060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek
265055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstatic int
2651c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhekmwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
265213935e2cf39b124c9a2ff0349b294e0b1e2e3aefLennert Buytenhek		   u32 legacy_rate_mask, u8 *mcs_rates)
265332060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek{
265455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_set_rate *cmd;
265532060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek	int rc;
265632060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek
265732060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
265832060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek	if (cmd == NULL)
265932060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek		return -ENOMEM;
266032060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek
266155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE);
266232060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
2663c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek	legacy_rate_mask_to_array(cmd->legacy_rates, legacy_rate_mask);
266413935e2cf39b124c9a2ff0349b294e0b1e2e3aefLennert Buytenhek	memcpy(cmd->mcs_set, mcs_rates, 16);
266532060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek
266632060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
266732060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek	kfree(cmd);
266832060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek
266932060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek	return rc;
267032060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek}
267132060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek
267232060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek/*
267355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek * CMD_FINALIZE_JOIN.
2674a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
267555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek#define MWL8K_FJ_BEACON_MAXLEN	128
267655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
267755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstruct mwl8k_cmd_finalize_join {
2678a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_pkt header;
267955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__le32 sleep_interval;	/* Number of beacon periods to sleep */
268055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__u8 beacon_data[MWL8K_FJ_BEACON_MAXLEN];
2681ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
2682a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
268355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstatic int mwl8k_cmd_finalize_join(struct ieee80211_hw *hw, void *frame,
268455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek				   int framelen, int dtim)
2685a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
268655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_finalize_join *cmd;
268755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct ieee80211_mgmt *payload = frame;
268855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	int payload_len;
2689a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
2690a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2691a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2692a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
2693a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
2694a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
269555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN);
2696a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
269755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1);
269855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
269955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	payload_len = framelen - ieee80211_hdrlen(payload->frame_control);
270055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	if (payload_len < 0)
270155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		payload_len = 0;
270255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	else if (payload_len > MWL8K_FJ_BEACON_MAXLEN)
270355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		payload_len = MWL8K_FJ_BEACON_MAXLEN;
270455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
270555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	memcpy(cmd->beacon_data, &payload->u.beacon, payload_len);
2706a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2707a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
2708a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
2709a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2710a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
2711a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2712a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2713a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
271455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek * CMD_SET_RTS_THRESHOLD.
2715a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
271655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstruct mwl8k_cmd_set_rts_threshold {
2717a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_pkt header;
2718a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 action;
271955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__le16 threshold;
2720ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
2721a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2722c2c2b12a8b6cd23d4abbc086642647c656bf406cLennert Buytenhekstatic int
2723c2c2b12a8b6cd23d4abbc086642647c656bf406cLennert Buytenhekmwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, int rts_thresh)
2724a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
272555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_set_rts_threshold *cmd;
2726a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
2727a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2728a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2729a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
2730a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
2731a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
273255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD);
2733a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
2734c2c2b12a8b6cd23d4abbc086642647c656bf406cLennert Buytenhek	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
2735c2c2b12a8b6cd23d4abbc086642647c656bf406cLennert Buytenhek	cmd->threshold = cpu_to_le16(rts_thresh);
2736a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2737a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
2738a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
2739a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2740a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
2741a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2742a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2743a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
274455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek * CMD_SET_SLOT.
2745a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
274655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstruct mwl8k_cmd_set_slot {
2747a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_pkt header;
2748a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 action;
274955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__u8 short_slot;
2750ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
2751a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
275255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstatic int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time)
2753a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
275455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_set_slot *cmd;
2755a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
2756a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2757a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2758a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
2759a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
2760a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
276155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT);
2762a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
276355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
276455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->short_slot = short_slot_time;
2765a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2766a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
2767a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
2768a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2769a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
2770a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2771a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2772a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
2773a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * CMD_SET_EDCA_PARAMS.
2774a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
2775a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstruct mwl8k_cmd_set_edca_params {
2776a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_pkt header;
2777a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2778a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* See MWL8K_SET_EDCA_XXX below */
2779a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 action;
2780a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2781a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* TX opportunity in units of 32 us */
2782a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__le16 txop;
2783a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
27842e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek	union {
27852e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek		struct {
27862e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			/* Log exponent of max contention period: 0...15 */
27872e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			__le32 log_cw_max;
27882e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek
27892e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			/* Log exponent of min contention period: 0...15 */
27902e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			__le32 log_cw_min;
27912e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek
27922e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			/* Adaptive interframe spacing in units of 32us */
27932e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			__u8 aifs;
27942e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek
27952e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			/* TX queue to configure */
27962e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			__u8 txq;
27972e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek		} ap;
27982e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek		struct {
27992e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			/* Log exponent of max contention period: 0...15 */
28002e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			__u8 log_cw_max;
2801a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
28022e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			/* Log exponent of min contention period: 0...15 */
28032e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			__u8 log_cw_min;
2804a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
28052e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			/* Adaptive interframe spacing in units of 32us */
28062e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			__u8 aifs;
2807a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
28082e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			/* TX queue to configure */
28092e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek			__u8 txq;
28102e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek		} sta;
28112e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek	};
2812ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
2813a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2814a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_SET_EDCA_CW	0x01
2815a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_SET_EDCA_TXOP	0x02
2816a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_SET_EDCA_AIFS	0x04
2817a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2818a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define MWL8K_SET_EDCA_ALL	(MWL8K_SET_EDCA_CW | \
2819a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				 MWL8K_SET_EDCA_TXOP | \
2820a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				 MWL8K_SET_EDCA_AIFS)
2821a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2822a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int
282355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekmwl8k_cmd_set_edca_params(struct ieee80211_hw *hw, __u8 qnum,
282455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			  __u16 cw_min, __u16 cw_max,
282555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			  __u8 aifs, __u16 txop)
2826a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
28272e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
2828a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_set_edca_params *cmd;
2829a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
2830a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2831a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2832a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
2833a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
2834a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2835a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS);
2836a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
2837a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL);
2838a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->txop = cpu_to_le16(txop);
28392e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek	if (priv->ap_fw) {
28402e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek		cmd->ap.log_cw_max = cpu_to_le32(ilog2(cw_max + 1));
28412e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek		cmd->ap.log_cw_min = cpu_to_le32(ilog2(cw_min + 1));
28422e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek		cmd->ap.aifs = aifs;
28432e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek		cmd->ap.txq = qnum;
28442e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek	} else {
28452e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek		cmd->sta.log_cw_max = (u8)ilog2(cw_max + 1);
28462e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek		cmd->sta.log_cw_min = (u8)ilog2(cw_min + 1);
28472e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek		cmd->sta.aifs = aifs;
28482e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek		cmd->sta.txq = qnum;
28492e484c8964f7845d320eb1161c514dbfcafdda78Lennert Buytenhek	}
2850a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2851a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
2852a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
2853a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2854a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
2855a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2856a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2857a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
285855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek * CMD_SET_WMM_MODE.
2859a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
286055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstruct mwl8k_cmd_set_wmm_mode {
2861a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_cmd_pkt header;
286255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__le16 action;
2863ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
2864a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
286555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstatic int mwl8k_cmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable)
2866a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
286755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
286855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_set_wmm_mode *cmd;
2869a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
2870a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2871a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2872a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
2873a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
2874a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
287555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_WMM_MODE);
2876a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
287755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->action = cpu_to_le16(!!enable);
2878a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2879a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
2880a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
288116cec43da50c4b4702653ca710549fd3457a4e6cLennert Buytenhek
288255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	if (!rc)
288355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		priv->wmm_enabled = enable;
2884a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2885a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
2886a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2887a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2888a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
288955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek * CMD_MIMO_CONFIG.
2890a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
289155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstruct mwl8k_cmd_mimo_config {
289255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_pkt header;
289355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__le32 action;
289455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__u8 rx_antenna_map;
289555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__u8 tx_antenna_map;
2896ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
2897a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
289855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstatic int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx)
2899a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
290055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_mimo_config *cmd;
2901a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
2902a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2903a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2904a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
2905a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
2906a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
290755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_MIMO_CONFIG);
2908a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
290955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->action = cpu_to_le32((u32)MWL8K_CMD_SET);
291055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->rx_antenna_map = rx;
291155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->tx_antenna_map = tx;
2912a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2913a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
2914a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
2915a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2916a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
2917a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2918a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2919a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
2920b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek * CMD_USE_FIXED_RATE (STA version).
2921a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
2922b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhekstruct mwl8k_cmd_use_fixed_rate_sta {
2923b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek	struct mwl8k_cmd_pkt header;
2924b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek	__le32 action;
2925b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek	__le32 allow_rate_drop;
2926b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek	__le32 num_rates;
2927b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek	struct {
2928b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek		__le32 is_ht_rate;
2929b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek		__le32 enable_retry;
2930b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek		__le32 rate;
2931b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek		__le32 retry_count;
2932b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek	} rate_entry[8];
2933b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek	__le32 rate_type;
2934b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek	__le32 reserved1;
2935b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek	__le32 reserved2;
2936ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
2937a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2938b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek#define MWL8K_USE_AUTO_RATE	0x0002
2939b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek#define MWL8K_UCAST_RATE	0
2940a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2941b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhekstatic int mwl8k_cmd_use_fixed_rate_sta(struct ieee80211_hw *hw)
2942a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
2943b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek	struct mwl8k_cmd_use_fixed_rate_sta *cmd;
2944a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
2945a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2946a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2947a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cmd == NULL)
2948a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return -ENOMEM;
2949a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2950a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE);
2951a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
2952b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek	cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE);
2953b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek	cmd->rate_type = cpu_to_le32(MWL8K_UCAST_RATE);
2954a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2955a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
2956a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(cmd);
2957a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2958a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
2959a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
2960a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
296155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek/*
2962088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek * CMD_USE_FIXED_RATE (AP version).
2963088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek */
2964088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhekstruct mwl8k_cmd_use_fixed_rate_ap {
2965088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	struct mwl8k_cmd_pkt header;
2966088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	__le32 action;
2967088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	__le32 allow_rate_drop;
2968088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	__le32 num_rates;
2969088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	struct mwl8k_rate_entry_ap {
2970088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek		__le32 is_ht_rate;
2971088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek		__le32 enable_retry;
2972088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek		__le32 rate;
2973088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek		__le32 retry_count;
2974088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	} rate_entry[4];
2975088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	u8 multicast_rate;
2976088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	u8 multicast_rate_type;
2977088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	u8 management_rate;
2978ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
2979088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek
2980088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhekstatic int
2981088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhekmwl8k_cmd_use_fixed_rate_ap(struct ieee80211_hw *hw, int mcast, int mgmt)
2982088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek{
2983088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	struct mwl8k_cmd_use_fixed_rate_ap *cmd;
2984088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	int rc;
2985088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek
2986088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
2987088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	if (cmd == NULL)
2988088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek		return -ENOMEM;
2989088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek
2990088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE);
2991088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
2992088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE);
2993088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	cmd->multicast_rate = mcast;
2994088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	cmd->management_rate = mgmt;
2995088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek
2996088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
2997088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	kfree(cmd);
2998088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek
2999088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek	return rc;
3000088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek}
3001088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek
3002088aab8b62666a002907c912cd346ae6dc9f42b7Lennert Buytenhek/*
300355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek * CMD_ENABLE_SNIFFER.
300455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek */
300555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstruct mwl8k_cmd_enable_sniffer {
300655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_pkt header;
300755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__le32 action;
3008ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
300955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
301055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstatic int mwl8k_cmd_enable_sniffer(struct ieee80211_hw *hw, bool enable)
301155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek{
301255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_enable_sniffer *cmd;
301355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	int rc;
301455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
301555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
301655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	if (cmd == NULL)
301755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		return -ENOMEM;
301855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
301955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER);
302055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
302155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->action = cpu_to_le32(!!enable);
302255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
302355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
302455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	kfree(cmd);
302555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
302655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	return rc;
302755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek}
302855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
302955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek/*
303055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek * CMD_SET_MAC_ADDR.
303155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek */
303255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstruct mwl8k_cmd_set_mac_addr {
303355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_pkt header;
303455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	union {
303555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		struct {
303655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			__le16 mac_type;
303755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			__u8 mac_addr[ETH_ALEN];
303855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		} mbss;
303955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		__u8 mac_addr[ETH_ALEN];
304055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	};
3041ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
304255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
3043ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek#define MWL8K_MAC_TYPE_PRIMARY_CLIENT		0
3044ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek#define MWL8K_MAC_TYPE_SECONDARY_CLIENT		1
3045ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek#define MWL8K_MAC_TYPE_PRIMARY_AP		2
3046ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek#define MWL8K_MAC_TYPE_SECONDARY_AP		3
3047a9e00b151ec2121b7ae09d84a2b5a68b6461e98aLennert Buytenhek
3048aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhekstatic int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw,
3049aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek				  struct ieee80211_vif *vif, u8 *mac)
305055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek{
305155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
3052ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
305355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_set_mac_addr *cmd;
3054ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	int mac_type;
305555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	int rc;
305655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
3057ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	mac_type = MWL8K_MAC_TYPE_PRIMARY_AP;
3058ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) {
3059ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek		if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported))
3060ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek			mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT;
3061ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek		else
3062ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek			mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT;
3063ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	} else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) {
3064ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek		if (mwl8k_vif->macid + 1 == ffs(priv->ap_macids_supported))
3065ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek			mac_type = MWL8K_MAC_TYPE_PRIMARY_AP;
3066ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek		else
3067ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek			mac_type = MWL8K_MAC_TYPE_SECONDARY_AP;
3068ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	}
3069ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek
307055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
307155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	if (cmd == NULL)
307255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		return -ENOMEM;
307355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
307455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR);
307555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
307655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	if (priv->ap_fw) {
3077ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek		cmd->mbss.mac_type = cpu_to_le16(mac_type);
307855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN);
307955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	} else {
308055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		memcpy(cmd->mac_addr, mac, ETH_ALEN);
308155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	}
308255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
3083aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
308455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	kfree(cmd);
308555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
308655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	return rc;
308755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek}
308855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
308955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek/*
309055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek * CMD_SET_RATEADAPT_MODE.
309155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek */
309255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstruct mwl8k_cmd_set_rate_adapt_mode {
309355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_pkt header;
309455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__le16 action;
309555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__le16 mode;
3096ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
309755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
309855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstatic int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode)
309955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek{
310055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_set_rate_adapt_mode *cmd;
310155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	int rc;
310255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
310355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
310455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	if (cmd == NULL)
310555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		return -ENOMEM;
310655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
310755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATEADAPT_MODE);
310855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
310955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
311055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->mode = cpu_to_le16(mode);
311155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
311255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
311355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	kfree(cmd);
311455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
311555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	return rc;
311655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek}
311755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
311855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek/*
3119b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek * CMD_BSS_START.
3120b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek */
3121b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhekstruct mwl8k_cmd_bss_start {
3122b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	struct mwl8k_cmd_pkt header;
3123b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	__le32 enable;
3124ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
3125b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
3126aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhekstatic int mwl8k_cmd_bss_start(struct ieee80211_hw *hw,
3127aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek			       struct ieee80211_vif *vif, int enable)
3128b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek{
3129b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	struct mwl8k_cmd_bss_start *cmd;
3130b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	int rc;
3131b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
3132b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3133b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	if (cmd == NULL)
3134b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		return -ENOMEM;
3135b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
3136b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_BSS_START);
3137b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
3138b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	cmd->enable = cpu_to_le32(enable);
3139b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
3140aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
3141b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	kfree(cmd);
3142b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
3143b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	return rc;
3144b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek}
3145b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
3146b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek/*
31473f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek * CMD_SET_NEW_STN.
31483f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek */
31493f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhekstruct mwl8k_cmd_set_new_stn {
31503f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	struct mwl8k_cmd_pkt header;
31513f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__le16 aid;
31523f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__u8 mac_addr[6];
31533f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__le16 stn_id;
31543f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__le16 action;
31553f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__le16 rsvd;
31563f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__le32 legacy_rates;
31573f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__u8 ht_rates[4];
31583f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__le16 cap_info;
31593f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__le16 ht_capabilities_info;
31603f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__u8 mac_ht_param_info;
31613f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__u8 rev;
31623f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__u8 control_channel;
31633f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__u8 add_channel;
31643f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__le16 op_mode;
31653f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__le16 stbc;
31663f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__u8 add_qos_info;
31673f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__u8 is_qos_sta;
31683f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	__le32 fw_sta_ptr;
3169ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
31703f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek
31713f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek#define MWL8K_STA_ACTION_ADD		0
31723f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek#define MWL8K_STA_ACTION_REMOVE		2
31733f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek
31743f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhekstatic int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw,
31753f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek				     struct ieee80211_vif *vif,
31763f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek				     struct ieee80211_sta *sta)
31773f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek{
31783f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	struct mwl8k_cmd_set_new_stn *cmd;
31798707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek	u32 rates;
31803f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	int rc;
31813f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek
31823f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
31833f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	if (cmd == NULL)
31843f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek		return -ENOMEM;
31853f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek
31863f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
31873f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
31883f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	cmd->aid = cpu_to_le16(sta->aid);
31893f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	memcpy(cmd->mac_addr, sta->addr, ETH_ALEN);
31903f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	cmd->stn_id = cpu_to_le16(sta->aid);
31913f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	cmd->action = cpu_to_le16(MWL8K_STA_ACTION_ADD);
31928707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek	if (hw->conf.channel->band == IEEE80211_BAND_2GHZ)
31938707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek		rates = sta->supp_rates[IEEE80211_BAND_2GHZ];
31948707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek	else
31958707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek		rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5;
31968707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek	cmd->legacy_rates = cpu_to_le32(rates);
31973f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	if (sta->ht_cap.ht_supported) {
31983f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek		cmd->ht_rates[0] = sta->ht_cap.mcs.rx_mask[0];
31993f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek		cmd->ht_rates[1] = sta->ht_cap.mcs.rx_mask[1];
32003f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek		cmd->ht_rates[2] = sta->ht_cap.mcs.rx_mask[2];
32013f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek		cmd->ht_rates[3] = sta->ht_cap.mcs.rx_mask[3];
32023f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek		cmd->ht_capabilities_info = cpu_to_le16(sta->ht_cap.cap);
32033f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek		cmd->mac_ht_param_info = (sta->ht_cap.ampdu_factor & 3) |
32043f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek			((sta->ht_cap.ampdu_density & 7) << 2);
32053f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek		cmd->is_qos_sta = 1;
32063f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	}
32073f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek
3208aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
32093f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	kfree(cmd);
32103f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek
32113f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	return rc;
32123f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek}
32133f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek
3214b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhekstatic int mwl8k_cmd_set_new_stn_add_self(struct ieee80211_hw *hw,
3215b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek					  struct ieee80211_vif *vif)
3216b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek{
3217b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	struct mwl8k_cmd_set_new_stn *cmd;
3218b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	int rc;
3219b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
3220b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3221b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	if (cmd == NULL)
3222b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		return -ENOMEM;
3223b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
3224b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
3225b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
3226b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	memcpy(cmd->mac_addr, vif->addr, ETH_ALEN);
3227b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
3228aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
3229b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	kfree(cmd);
3230b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
3231b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	return rc;
3232b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek}
3233b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
32343f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhekstatic int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw,
32353f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek				     struct ieee80211_vif *vif, u8 *addr)
32363f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek{
32373f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	struct mwl8k_cmd_set_new_stn *cmd;
32383f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	int rc;
32393f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek
32403f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
32413f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	if (cmd == NULL)
32423f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek		return -ENOMEM;
32433f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek
32443f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
32453f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
32463f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	memcpy(cmd->mac_addr, addr, ETH_ALEN);
32473f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	cmd->action = cpu_to_le16(MWL8K_STA_ACTION_REMOVE);
32483f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek
3249aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
32503f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	kfree(cmd);
32513f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek
32523f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	return rc;
32533f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek}
32543f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek
32553f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek/*
3256fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam * CMD_UPDATE_ENCRYPTION.
3257fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam */
3258fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3259fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam#define MAX_ENCR_KEY_LENGTH	16
3260fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam#define MIC_KEY_LENGTH		8
3261fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3262fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadamstruct mwl8k_cmd_update_encryption {
3263fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	struct mwl8k_cmd_pkt header;
3264fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3265fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__le32 action;
3266fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__le32 reserved;
3267fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__u8 mac_addr[6];
3268fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__u8 encr_type;
3269fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3270fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam} __attribute__((packed));
3271fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3272fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadamstruct mwl8k_cmd_set_key {
3273fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	struct mwl8k_cmd_pkt header;
3274fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3275fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__le32 action;
3276fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__le32 reserved;
3277fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__le16 length;
3278fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__le16 key_type_id;
3279fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__le32 key_info;
3280fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__le32 key_id;
3281fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__le16 key_len;
3282fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__u8 key_material[MAX_ENCR_KEY_LENGTH];
3283fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__u8 tkip_tx_mic_key[MIC_KEY_LENGTH];
3284fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__u8 tkip_rx_mic_key[MIC_KEY_LENGTH];
3285fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__le16 tkip_rsc_low;
3286fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__le32 tkip_rsc_high;
3287fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__le16 tkip_tsc_low;
3288fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__le32 tkip_tsc_high;
3289fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	__u8 mac_addr[6];
3290fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam} __attribute__((packed));
3291fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3292fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadamenum {
3293fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	MWL8K_ENCR_ENABLE,
3294fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	MWL8K_ENCR_SET_KEY,
3295fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	MWL8K_ENCR_REMOVE_KEY,
3296fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	MWL8K_ENCR_SET_GROUP_KEY,
3297fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam};
3298fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3299fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam#define MWL8K_UPDATE_ENCRYPTION_TYPE_WEP	0
3300fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam#define MWL8K_UPDATE_ENCRYPTION_TYPE_DISABLE	1
3301fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam#define MWL8K_UPDATE_ENCRYPTION_TYPE_TKIP	4
3302fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam#define MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED	7
3303fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam#define MWL8K_UPDATE_ENCRYPTION_TYPE_AES	8
3304fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3305fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadamenum {
3306fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	MWL8K_ALG_WEP,
3307fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	MWL8K_ALG_TKIP,
3308fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	MWL8K_ALG_CCMP,
3309fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam};
3310fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3311fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam#define MWL8K_KEY_FLAG_TXGROUPKEY	0x00000004
3312fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam#define MWL8K_KEY_FLAG_PAIRWISE		0x00000008
3313fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam#define MWL8K_KEY_FLAG_TSC_VALID	0x00000040
3314fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam#define MWL8K_KEY_FLAG_WEP_TXKEY	0x01000000
3315fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam#define MWL8K_KEY_FLAG_MICKEY_VALID	0x02000000
3316fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3317fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadamstatic int mwl8k_cmd_update_encryption_enable(struct ieee80211_hw *hw,
3318fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam					      struct ieee80211_vif *vif,
3319fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam					      u8 *addr,
3320fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam					      u8 encr_type)
3321fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam{
3322fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	struct mwl8k_cmd_update_encryption *cmd;
3323fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	int rc;
3324fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3325fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3326fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	if (cmd == NULL)
3327fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		return -ENOMEM;
3328fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3329fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION);
3330fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	cmd->header.length = cpu_to_le16(sizeof(*cmd));
3331fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	cmd->action = cpu_to_le32(MWL8K_ENCR_ENABLE);
3332fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	memcpy(cmd->mac_addr, addr, ETH_ALEN);
3333fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	cmd->encr_type = encr_type;
3334fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3335fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
3336fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	kfree(cmd);
3337fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3338fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	return rc;
3339fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam}
3340fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3341fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadamstatic int mwl8k_encryption_set_cmd_info(struct mwl8k_cmd_set_key *cmd,
3342fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam						u8 *addr,
3343fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam						struct ieee80211_key_conf *key)
3344fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam{
3345fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION);
3346fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	cmd->header.length = cpu_to_le16(sizeof(*cmd));
3347fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	cmd->length = cpu_to_le16(sizeof(*cmd) -
3348fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam				offsetof(struct mwl8k_cmd_set_key, length));
3349fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	cmd->key_id = cpu_to_le32(key->keyidx);
3350fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	cmd->key_len = cpu_to_le16(key->keylen);
3351fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	memcpy(cmd->mac_addr, addr, ETH_ALEN);
3352fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3353fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	switch (key->cipher) {
3354fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	case WLAN_CIPHER_SUITE_WEP40:
3355fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	case WLAN_CIPHER_SUITE_WEP104:
3356fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		cmd->key_type_id = cpu_to_le16(MWL8K_ALG_WEP);
3357fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		if (key->keyidx == 0)
3358fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			cmd->key_info =	cpu_to_le32(MWL8K_KEY_FLAG_WEP_TXKEY);
3359fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3360fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		break;
3361fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	case WLAN_CIPHER_SUITE_TKIP:
3362fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		cmd->key_type_id = cpu_to_le16(MWL8K_ALG_TKIP);
3363fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		cmd->key_info =	(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
3364fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE)
3365fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			: cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY);
3366fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		cmd->key_info |= cpu_to_le32(MWL8K_KEY_FLAG_MICKEY_VALID
3367fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam						| MWL8K_KEY_FLAG_TSC_VALID);
3368fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		break;
3369fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	case WLAN_CIPHER_SUITE_CCMP:
3370fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		cmd->key_type_id = cpu_to_le16(MWL8K_ALG_CCMP);
3371fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		cmd->key_info =	(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
3372fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE)
3373fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			: cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY);
3374fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		break;
3375fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	default:
3376fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		return -ENOTSUPP;
3377fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	}
3378fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3379fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	return 0;
3380fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam}
3381fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3382fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadamstatic int mwl8k_cmd_encryption_set_key(struct ieee80211_hw *hw,
3383fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam						struct ieee80211_vif *vif,
3384fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam						u8 *addr,
3385fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam						struct ieee80211_key_conf *key)
3386fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam{
3387fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	struct mwl8k_cmd_set_key *cmd;
3388fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	int rc;
3389fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	int keymlen;
3390fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	u32 action;
3391fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	u8 idx;
3392fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
3393fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3394fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3395fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	if (cmd == NULL)
3396fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		return -ENOMEM;
3397fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3398fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	rc = mwl8k_encryption_set_cmd_info(cmd, addr, key);
3399fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	if (rc < 0)
3400fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		goto done;
3401fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3402fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	idx = key->keyidx;
3403fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3404fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
3405fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		action = MWL8K_ENCR_SET_KEY;
3406fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	else
3407fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		action = MWL8K_ENCR_SET_GROUP_KEY;
3408fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3409fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	switch (key->cipher) {
3410fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	case WLAN_CIPHER_SUITE_WEP40:
3411fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	case WLAN_CIPHER_SUITE_WEP104:
3412fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		if (!mwl8k_vif->wep_key_conf[idx].enabled) {
3413fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			memcpy(mwl8k_vif->wep_key_conf[idx].key, key,
3414fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam						sizeof(*key) + key->keylen);
3415fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			mwl8k_vif->wep_key_conf[idx].enabled = 1;
3416fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		}
3417fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3418fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		keymlen = 0;
3419fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		action = MWL8K_ENCR_SET_KEY;
3420fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		break;
3421fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	case WLAN_CIPHER_SUITE_TKIP:
3422fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH;
3423fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		break;
3424fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	case WLAN_CIPHER_SUITE_CCMP:
3425fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		keymlen = key->keylen;
3426fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		break;
3427fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	default:
3428fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		rc = -ENOTSUPP;
3429fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		goto done;
3430fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	}
3431fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3432fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	memcpy(cmd->key_material, key->key, keymlen);
3433fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	cmd->action = cpu_to_le32(action);
3434fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3435fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
3436fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadamdone:
3437fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	kfree(cmd);
3438fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3439fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	return rc;
3440fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam}
3441fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3442fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadamstatic int mwl8k_cmd_encryption_remove_key(struct ieee80211_hw *hw,
3443fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam						struct ieee80211_vif *vif,
3444fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam						u8 *addr,
3445fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam						struct ieee80211_key_conf *key)
3446fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam{
3447fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	struct mwl8k_cmd_set_key *cmd;
3448fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	int rc;
3449fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
3450fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3451fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3452fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	if (cmd == NULL)
3453fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		return -ENOMEM;
3454fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3455fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	rc = mwl8k_encryption_set_cmd_info(cmd, addr, key);
3456fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	if (rc < 0)
3457fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		goto done;
3458fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3459fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	if (key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
3460fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			WLAN_CIPHER_SUITE_WEP104)
3461fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		mwl8k_vif->wep_key_conf[key->keyidx].enabled = 0;
3462fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3463fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	cmd->action = cpu_to_le32(MWL8K_ENCR_REMOVE_KEY);
3464fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3465fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
3466fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadamdone:
3467fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	kfree(cmd);
3468fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3469fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	return rc;
3470fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam}
3471fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3472fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadamstatic int mwl8k_set_key(struct ieee80211_hw *hw,
3473fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			 enum set_key_cmd cmd_param,
3474fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			 struct ieee80211_vif *vif,
3475fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			 struct ieee80211_sta *sta,
3476fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			 struct ieee80211_key_conf *key)
3477fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam{
3478fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	int rc = 0;
3479fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	u8 encr_type;
3480fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	u8 *addr;
3481fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
3482fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3483fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	if (vif->type == NL80211_IFTYPE_STATION)
3484fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		return -EOPNOTSUPP;
3485fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3486fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	if (sta == NULL)
3487fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		addr = hw->wiphy->perm_addr;
3488fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	else
3489fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		addr = sta->addr;
3490fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3491fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	if (cmd_param == SET_KEY) {
3492fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
3493fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		rc = mwl8k_cmd_encryption_set_key(hw, vif, addr, key);
3494fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		if (rc)
3495fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			goto out;
3496fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3497fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		if ((key->cipher == WLAN_CIPHER_SUITE_WEP40)
3498fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam				|| (key->cipher == WLAN_CIPHER_SUITE_WEP104))
3499fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_WEP;
3500fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		else
3501fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED;
3502fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3503fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		rc = mwl8k_cmd_update_encryption_enable(hw, vif, addr,
3504fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam								encr_type);
3505fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		if (rc)
3506fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			goto out;
3507fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3508fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		mwl8k_vif->is_hw_crypto_enabled = true;
3509fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3510fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	} else {
3511fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		rc = mwl8k_cmd_encryption_remove_key(hw, vif, addr, key);
3512fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3513fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		if (rc)
3514fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			goto out;
3515fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3516fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam		mwl8k_vif->is_hw_crypto_enabled = false;
3517fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3518fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	}
3519fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadamout:
3520fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	return rc;
3521fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam}
3522fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
3523fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam/*
352455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek * CMD_UPDATE_STADB.
352555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek */
352625d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhekstruct ewc_ht_info {
352725d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	__le16	control1;
352825d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	__le16	control2;
352925d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	__le16	control3;
3530ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
353125d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek
353225d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhekstruct peer_capability_info {
353325d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	/* Peer type - AP vs. STA.  */
353425d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	__u8	peer_type;
353525d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek
353625d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	/* Basic 802.11 capabilities from assoc resp.  */
353725d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	__le16	basic_caps;
353825d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek
353925d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	/* Set if peer supports 802.11n high throughput (HT).  */
354025d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	__u8	ht_support;
354125d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek
354225d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	/* Valid if HT is supported.  */
354325d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	__le16	ht_caps;
354425d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	__u8	extended_ht_caps;
354525d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	struct ewc_ht_info	ewc_info;
354625d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek
354725d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	/* Legacy rate table. Intersection of our rates and peer rates.  */
354825d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	__u8	legacy_rates[12];
354925d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek
355025d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	/* HT rate table. Intersection of our rates and peer rates.  */
355125d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	__u8	ht_rates[16];
355225d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	__u8	pad[16];
355325d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek
355425d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	/* If set, interoperability mode, no proprietary extensions.  */
355525d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	__u8	interop;
355625d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	__u8	pad2;
355725d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	__u8	station_id;
355825d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek	__le16	amsdu_enabled;
3559ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
356025d81b1e1a0cca41a71a08468a7d3a4c751c8565Lennert Buytenhek
356155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhekstruct mwl8k_cmd_update_stadb {
356255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_pkt header;
356355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
356455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	/* See STADB_ACTION_TYPE */
356555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__le32	action;
356655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
356755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	/* Peer MAC address */
356855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__u8	peer_addr[ETH_ALEN];
356955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
357055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	__le32	reserved;
357155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
357255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	/* Peer info - valid during add/update.  */
357355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct peer_capability_info	peer_info;
3574ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
357555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
3576a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek#define MWL8K_STA_DB_MODIFY_ENTRY	1
3577a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek#define MWL8K_STA_DB_DEL_ENTRY		2
3578a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek
3579a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek/* Peer Entry flags - used to define the type of the peer node */
3580a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek#define MWL8K_PEER_TYPE_ACCESSPOINT	2
3581a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek
3582a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhekstatic int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw,
3583c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek				      struct ieee80211_vif *vif,
358413935e2cf39b124c9a2ff0349b294e0b1e2e3aefLennert Buytenhek				      struct ieee80211_sta *sta)
358555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek{
358655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	struct mwl8k_cmd_update_stadb *cmd;
3587a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	struct peer_capability_info *p;
35888707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek	u32 rates;
358955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	int rc;
359055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
359155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
359255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	if (cmd == NULL)
359355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		return -ENOMEM;
359455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
359555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB);
359655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
3597a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	cmd->action = cpu_to_le32(MWL8K_STA_DB_MODIFY_ENTRY);
359813935e2cf39b124c9a2ff0349b294e0b1e2e3aefLennert Buytenhek	memcpy(cmd->peer_addr, sta->addr, ETH_ALEN);
359955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
3600a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	p = &cmd->peer_info;
3601a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT;
3602a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability);
360313935e2cf39b124c9a2ff0349b294e0b1e2e3aefLennert Buytenhek	p->ht_support = sta->ht_cap.ht_supported;
3604b603742f49c3ec922522602e18ac22e8f6835132John W. Linville	p->ht_caps = cpu_to_le16(sta->ht_cap.cap);
360513935e2cf39b124c9a2ff0349b294e0b1e2e3aefLennert Buytenhek	p->extended_ht_caps = (sta->ht_cap.ampdu_factor & 3) |
360613935e2cf39b124c9a2ff0349b294e0b1e2e3aefLennert Buytenhek		((sta->ht_cap.ampdu_density & 7) << 2);
36078707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek	if (hw->conf.channel->band == IEEE80211_BAND_2GHZ)
36088707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek		rates = sta->supp_rates[IEEE80211_BAND_2GHZ];
36098707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek	else
36108707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek		rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5;
36118707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek	legacy_rate_mask_to_array(p->legacy_rates, rates);
361213935e2cf39b124c9a2ff0349b294e0b1e2e3aefLennert Buytenhek	memcpy(p->ht_rates, sta->ht_cap.mcs.rx_mask, 16);
3613a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	p->interop = 1;
3614a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	p->amsdu_enabled = 0;
3615a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek
3616a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
3617a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	kfree(cmd);
3618a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek
3619a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	return rc ? rc : p->station_id;
3620a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek}
3621a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek
3622a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhekstatic int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw,
3623a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek				      struct ieee80211_vif *vif, u8 *addr)
3624a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek{
3625a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	struct mwl8k_cmd_update_stadb *cmd;
3626a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	int rc;
3627a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek
3628a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
3629a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	if (cmd == NULL)
3630a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek		return -ENOMEM;
3631a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek
3632a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB);
3633a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	cmd->header.length = cpu_to_le16(sizeof(*cmd));
3634a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	cmd->action = cpu_to_le32(MWL8K_STA_DB_DEL_ENTRY);
3635bbfd9128d3b4a80bea017ebdd47b31a80dc7eadfLennert Buytenhek	memcpy(cmd->peer_addr, addr, ETH_ALEN);
363655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
3637a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	rc = mwl8k_post_cmd(hw, &cmd->header);
363855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	kfree(cmd);
363955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
364055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	return rc;
364155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek}
364255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek
3643a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3644a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
3645a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * Interrupt handling.
3646a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
3647a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic irqreturn_t mwl8k_interrupt(int irq, void *dev_id)
3648a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
3649a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct ieee80211_hw *hw = dev_id;
3650a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
3651a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	u32 status;
3652a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3653a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
3654a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (!status)
3655a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return IRQ_NONE;
3656a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
36571e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	if (status & MWL8K_A2H_INT_TX_DONE) {
36581e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek		status &= ~MWL8K_A2H_INT_TX_DONE;
36591e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek		tasklet_schedule(&priv->poll_tx_task);
36601e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	}
36611e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek
3662a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (status & MWL8K_A2H_INT_RX_READY) {
366367e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek		status &= ~MWL8K_A2H_INT_RX_READY;
366467e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek		tasklet_schedule(&priv->poll_rx_task);
3665a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
3666a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
366767e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	if (status)
366867e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek		iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
366967e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek
3670a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (status & MWL8K_A2H_INT_OPC_DONE) {
3671618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek		if (priv->hostcmd_wait != NULL)
3672a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			complete(priv->hostcmd_wait);
3673a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
3674a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3675a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (status & MWL8K_A2H_INT_QUEUE_EMPTY) {
3676618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek		if (!mutex_is_locked(&priv->fw_mutex) &&
367788de754ad59025eba797e7a8375807755577f450Lennert Buytenhek		    priv->radio_on && priv->pending_tx_pkts)
3678618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek			mwl8k_tx_start(priv);
3679a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
3680a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3681a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return IRQ_HANDLED;
3682a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
3683a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
36841e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhekstatic void mwl8k_tx_poll(unsigned long data)
36851e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek{
36861e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
36871e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
36881e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	int limit;
36891e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	int i;
36901e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek
36911e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	limit = 32;
36921e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek
36931e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	spin_lock_bh(&priv->tx_lock);
36941e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek
36951e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	for (i = 0; i < MWL8K_TX_QUEUES; i++)
36961e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek		limit -= mwl8k_txq_reclaim(hw, i, limit, 0);
36971e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek
36981e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	if (!priv->pending_tx_pkts && priv->tx_wait != NULL) {
36991e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek		complete(priv->tx_wait);
37001e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek		priv->tx_wait = NULL;
37011e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	}
37021e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek
37031e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	spin_unlock_bh(&priv->tx_lock);
37041e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek
37051e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	if (limit) {
37061e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek		writel(~MWL8K_A2H_INT_TX_DONE,
37071e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek		       priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
37081e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	} else {
37091e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek		tasklet_schedule(&priv->poll_tx_task);
37101e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	}
37111e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek}
37121e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek
371367e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhekstatic void mwl8k_rx_poll(unsigned long data)
371467e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek{
371567e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
371667e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
371767e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	int limit;
371867e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek
371967e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	limit = 32;
372067e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	limit -= rxq_process(hw, 0, limit);
372167e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	limit -= rxq_refill(hw, 0, limit);
372267e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek
372367e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	if (limit) {
372467e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek		writel(~MWL8K_A2H_INT_RX_READY,
372567e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek		       priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
372667e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	} else {
372767e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek		tasklet_schedule(&priv->poll_rx_task);
372867e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	}
372967e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek}
373067e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek
3731a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3732a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
3733a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * Core driver operations.
3734a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
3735a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
3736a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
3737a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
3738a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int index = skb_get_queue_mapping(skb);
3739a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
3740a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
37419189c10087a738c764046fa27651d332594cd8e6Lennert Buytenhek	if (!priv->radio_on) {
3742c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches		wiphy_debug(hw->wiphy,
3743c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches			    "dropped TX frame since radio disabled\n");
3744a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		dev_kfree_skb(skb);
3745a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return NETDEV_TX_OK;
3746a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
3747a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3748a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_txq_xmit(hw, index, skb);
3749a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3750a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
3751a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
3752a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3753a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int mwl8k_start(struct ieee80211_hw *hw)
3754a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
3755a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
3756a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
3757a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3758a0607fd3a25ba1848a63a0d925e36d914735ab47Joe Perches	rc = request_irq(priv->pdev->irq, mwl8k_interrupt,
3759a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			 IRQF_SHARED, MWL8K_NAME, hw);
3760a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (rc) {
37615db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches		wiphy_err(hw->wiphy, "failed to register IRQ handler\n");
37622ec610cb6d57032cdab89781e37ed3e442c73367Lennert Buytenhek		return -EIO;
3763a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
3764a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
376567e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	/* Enable TX reclaim and RX tasklets.  */
37661e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	tasklet_enable(&priv->poll_tx_task);
376767e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	tasklet_enable(&priv->poll_rx_task);
37682ec610cb6d57032cdab89781e37ed3e442c73367Lennert Buytenhek
3769a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* Enable interrupts */
3770c23b5a699471ea2ef9d146eae80e64836cfbf001Lennert Buytenhek	iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
3771a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
37722ec610cb6d57032cdab89781e37ed3e442c73367Lennert Buytenhek	rc = mwl8k_fw_lock(hw);
37732ec610cb6d57032cdab89781e37ed3e442c73367Lennert Buytenhek	if (!rc) {
377455489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		rc = mwl8k_cmd_radio_enable(hw);
3775a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
37765e4cf166f4a9801ea9ca1bab210d763d27538de6Lennert Buytenhek		if (!priv->ap_fw) {
37775e4cf166f4a9801ea9ca1bab210d763d27538de6Lennert Buytenhek			if (!rc)
377855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek				rc = mwl8k_cmd_enable_sniffer(hw, 0);
3779a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
37805e4cf166f4a9801ea9ca1bab210d763d27538de6Lennert Buytenhek			if (!rc)
37815e4cf166f4a9801ea9ca1bab210d763d27538de6Lennert Buytenhek				rc = mwl8k_cmd_set_pre_scan(hw);
37825e4cf166f4a9801ea9ca1bab210d763d27538de6Lennert Buytenhek
37835e4cf166f4a9801ea9ca1bab210d763d27538de6Lennert Buytenhek			if (!rc)
37845e4cf166f4a9801ea9ca1bab210d763d27538de6Lennert Buytenhek				rc = mwl8k_cmd_set_post_scan(hw,
37855e4cf166f4a9801ea9ca1bab210d763d27538de6Lennert Buytenhek						"\x00\x00\x00\x00\x00\x00");
37865e4cf166f4a9801ea9ca1bab210d763d27538de6Lennert Buytenhek		}
37872ec610cb6d57032cdab89781e37ed3e442c73367Lennert Buytenhek
37882ec610cb6d57032cdab89781e37ed3e442c73367Lennert Buytenhek		if (!rc)
378955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			rc = mwl8k_cmd_set_rateadapt_mode(hw, 0);
3790a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
37912ec610cb6d57032cdab89781e37ed3e442c73367Lennert Buytenhek		if (!rc)
379255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			rc = mwl8k_cmd_set_wmm_mode(hw, 0);
3793a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
37942ec610cb6d57032cdab89781e37ed3e442c73367Lennert Buytenhek		mwl8k_fw_unlock(hw);
37952ec610cb6d57032cdab89781e37ed3e442c73367Lennert Buytenhek	}
37962ec610cb6d57032cdab89781e37ed3e442c73367Lennert Buytenhek
37972ec610cb6d57032cdab89781e37ed3e442c73367Lennert Buytenhek	if (rc) {
37982ec610cb6d57032cdab89781e37ed3e442c73367Lennert Buytenhek		iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
37992ec610cb6d57032cdab89781e37ed3e442c73367Lennert Buytenhek		free_irq(priv->pdev->irq, hw);
38001e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek		tasklet_disable(&priv->poll_tx_task);
380167e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek		tasklet_disable(&priv->poll_rx_task);
38022ec610cb6d57032cdab89781e37ed3e442c73367Lennert Buytenhek	}
3803a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3804a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
3805a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
3806a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3807a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void mwl8k_stop(struct ieee80211_hw *hw)
3808a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
3809a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
3810a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int i;
3811a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
381255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	mwl8k_cmd_radio_disable(hw);
3813a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3814a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	ieee80211_stop_queues(hw);
3815a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3816a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* Disable interrupts */
3817a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
3818a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	free_irq(priv->pdev->irq, hw);
3819a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3820a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* Stop finalize join worker */
3821a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	cancel_work_sync(&priv->finalize_join_worker);
3822a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (priv->beacon_skb != NULL)
3823a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		dev_kfree_skb(priv->beacon_skb);
3824a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
382567e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	/* Stop TX reclaim and RX tasklets.  */
38261e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	tasklet_disable(&priv->poll_tx_task);
382767e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	tasklet_disable(&priv->poll_rx_task);
3828a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3829a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* Return all skbs to mac80211 */
3830a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	for (i = 0; i < MWL8K_TX_QUEUES; i++)
3831efb7c49a68cf206f35793d7799608e1d69a209f9Lennert Buytenhek		mwl8k_txq_reclaim(hw, i, INT_MAX, 1);
3832a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
3833a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
38340863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolostatic int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image);
38350863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo
3836a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int mwl8k_add_interface(struct ieee80211_hw *hw,
3837f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek			       struct ieee80211_vif *vif)
3838a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
3839a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
3840a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_vif *mwl8k_vif;
3841ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	u32 macids_supported;
38420863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	int macid, rc;
38430863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	struct mwl8k_device_info *di;
3844a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3845a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/*
3846a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	 * Reject interface creation if sniffer mode is active, as
3847a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	 * STA operation is mutually exclusive with hardware sniffer
3848b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	 * mode.  (Sniffer mode is only used on STA firmware.)
3849a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	 */
3850a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	if (priv->sniffer_enabled) {
3851c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches		wiphy_info(hw->wiphy,
3852c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches			   "unable to create STA interface because sniffer mode is enabled\n");
3853a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek		return -EINVAL;
3854a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	}
3855a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek
38560863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	di = priv->device_info;
3857ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	switch (vif->type) {
3858ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	case NL80211_IFTYPE_AP:
38590863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo		if (!priv->ap_fw && di->fw_image_ap) {
38600863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo			/* we must load the ap fw to meet this request */
38610863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo			if (!list_empty(&priv->vif_list))
38620863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo				return -EBUSY;
38630863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo			rc = mwl8k_reload_firmware(hw, di->fw_image_ap);
38640863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo			if (rc)
38650863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo				return rc;
38660863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo		}
3867ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek		macids_supported = priv->ap_macids_supported;
3868ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek		break;
3869ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	case NL80211_IFTYPE_STATION:
38700863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo		if (priv->ap_fw && di->fw_image_sta) {
38710863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo			/* we must load the sta fw to meet this request */
38720863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo			if (!list_empty(&priv->vif_list))
38730863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo				return -EBUSY;
38740863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo			rc = mwl8k_reload_firmware(hw, di->fw_image_sta);
38750863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo			if (rc)
38760863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo				return rc;
38770863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo		}
3878ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek		macids_supported = priv->sta_macids_supported;
3879ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek		break;
3880ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	default:
3881ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek		return -EINVAL;
3882ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	}
3883ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek
3884ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	macid = ffs(macids_supported & ~priv->macids_used);
3885ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	if (!macid--)
3886ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek		return -EBUSY;
3887ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek
3888f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek	/* Setup driver private area. */
38891ed32e4fc8cfc9656cc1101e7f9617d485fcbe7bJohannes Berg	mwl8k_vif = MWL8K_VIF(vif);
3890a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	memset(mwl8k_vif, 0, sizeof(*mwl8k_vif));
3891f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek	mwl8k_vif->vif = vif;
3892ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	mwl8k_vif->macid = macid;
3893a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	mwl8k_vif->seqno = 0;
3894d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	memcpy(mwl8k_vif->bssid, vif->addr, ETH_ALEN);
3895d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	mwl8k_vif->is_hw_crypto_enabled = false;
3896a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3897aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek	/* Set the mac address.  */
3898aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek	mwl8k_cmd_set_mac_addr(hw, vif, vif->addr);
3899aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek
3900aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek	if (priv->ap_fw)
3901aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek		mwl8k_cmd_set_new_stn_add_self(hw, vif);
3902aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek
3903ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	priv->macids_used |= 1 << mwl8k_vif->macid;
3904f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek	list_add_tail(&mwl8k_vif->list, &priv->vif_list);
3905a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3906a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return 0;
3907a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
3908a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3909a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void mwl8k_remove_interface(struct ieee80211_hw *hw,
39101ed32e4fc8cfc9656cc1101e7f9617d485fcbe7bJohannes Berg				   struct ieee80211_vif *vif)
3911a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
3912a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
3913f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
3914a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3915b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	if (priv->ap_fw)
3916b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr);
3917b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
3918aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek	mwl8k_cmd_set_mac_addr(hw, vif, "\x00\x00\x00\x00\x00\x00");
391932060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek
3920ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	priv->macids_used &= ~(1 << mwl8k_vif->macid);
3921f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek	list_del(&mwl8k_vif->list);
3922a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
3923a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3924ee03a93241eb954d669fb795b4e5c0eec92eef22Lennert Buytenhekstatic int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
3925a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
3926a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct ieee80211_conf *conf = &hw->conf;
3927a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
3928ee03a93241eb954d669fb795b4e5c0eec92eef22Lennert Buytenhek	int rc;
3929a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
39307595d67a06466cc00e3aae1b86544278b57481eeLennert Buytenhek	if (conf->flags & IEEE80211_CONF_IDLE) {
393155489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		mwl8k_cmd_radio_disable(hw);
3932ee03a93241eb954d669fb795b4e5c0eec92eef22Lennert Buytenhek		return 0;
39337595d67a06466cc00e3aae1b86544278b57481eeLennert Buytenhek	}
39347595d67a06466cc00e3aae1b86544278b57481eeLennert Buytenhek
3935ee03a93241eb954d669fb795b4e5c0eec92eef22Lennert Buytenhek	rc = mwl8k_fw_lock(hw);
3936ee03a93241eb954d669fb795b4e5c0eec92eef22Lennert Buytenhek	if (rc)
3937ee03a93241eb954d669fb795b4e5c0eec92eef22Lennert Buytenhek		return rc;
3938a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
393955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	rc = mwl8k_cmd_radio_enable(hw);
3940ee03a93241eb954d669fb795b4e5c0eec92eef22Lennert Buytenhek	if (rc)
3941ee03a93241eb954d669fb795b4e5c0eec92eef22Lennert Buytenhek		goto out;
3942a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3943610677d2f0415570a7590790e8be376a946cd08bLennert Buytenhek	rc = mwl8k_cmd_set_rf_channel(hw, conf);
3944ee03a93241eb954d669fb795b4e5c0eec92eef22Lennert Buytenhek	if (rc)
3945ee03a93241eb954d669fb795b4e5c0eec92eef22Lennert Buytenhek		goto out;
3946ee03a93241eb954d669fb795b4e5c0eec92eef22Lennert Buytenhek
3947a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (conf->power_level > 18)
3948a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		conf->power_level = 18;
3949a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
395008b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	if (priv->ap_fw) {
395141fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam		rc = mwl8k_cmd_tx_power(hw, conf, conf->power_level);
395241fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam		if (rc)
395341fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam			goto out;
395441fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam
3955da62b761769f60e5d476ad882c5ba40fb5d61664Nishant Sarmukadam		rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3);
3956da62b761769f60e5d476ad882c5ba40fb5d61664Nishant Sarmukadam		if (rc)
3957da62b761769f60e5d476ad882c5ba40fb5d61664Nishant Sarmukadam			wiphy_warn(hw->wiphy, "failed to set # of RX antennas");
3958da62b761769f60e5d476ad882c5ba40fb5d61664Nishant Sarmukadam		rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7);
3959da62b761769f60e5d476ad882c5ba40fb5d61664Nishant Sarmukadam		if (rc)
3960da62b761769f60e5d476ad882c5ba40fb5d61664Nishant Sarmukadam			wiphy_warn(hw->wiphy, "failed to set # of TX antennas");
3961da62b761769f60e5d476ad882c5ba40fb5d61664Nishant Sarmukadam
396208b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	} else {
396341fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam		rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level);
396441fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam		if (rc)
396541fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam			goto out;
396608b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek		rc = mwl8k_cmd_mimo_config(hw, 0x7, 0x7);
396708b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	}
3968a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3969ee03a93241eb954d669fb795b4e5c0eec92eef22Lennert Buytenhekout:
3970ee03a93241eb954d669fb795b4e5c0eec92eef22Lennert Buytenhek	mwl8k_fw_unlock(hw);
3971a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3972ee03a93241eb954d669fb795b4e5c0eec92eef22Lennert Buytenhek	return rc;
3973a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
3974a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3975b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhekstatic void
3976b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhekmwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
3977b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek			   struct ieee80211_bss_conf *info, u32 changed)
3978a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
3979a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
3980c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	u32 ap_legacy_rates;
398113935e2cf39b124c9a2ff0349b294e0b1e2e3aefLennert Buytenhek	u8 ap_mcs_rates[16];
39823a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhek	int rc;
39833a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhek
3984c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	if (mwl8k_fw_lock(hw))
39853a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhek		return;
3986a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
3987c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	/*
3988c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	 * No need to capture a beacon if we're no longer associated.
3989c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	 */
3990c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	if ((changed & BSS_CHANGED_ASSOC) && !vif->bss_conf.assoc)
3991c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek		priv->capture_beacon = false;
39923a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhek
3993c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	/*
399413935e2cf39b124c9a2ff0349b294e0b1e2e3aefLennert Buytenhek	 * Get the AP's legacy and MCS rates.
3995c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	 */
39967dc6a7a7635365b140af969e972900866d0bf34bLennert Buytenhek	if (vif->bss_conf.assoc) {
3997c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek		struct ieee80211_sta *ap;
3998c97470dd253831e880c72ea5d022ed7f3aee45c3Lennert Buytenhek
3999c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek		rcu_read_lock();
4000c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek
4001c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek		ap = ieee80211_find_sta(vif, vif->bss_conf.bssid);
4002c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek		if (ap == NULL) {
4003c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek			rcu_read_unlock();
4004c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek			goto out;
4005c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek		}
4006c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek
40078707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek		if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) {
40088707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek			ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ];
40098707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek		} else {
40108707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek			ap_legacy_rates =
40118707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek				ap->supp_rates[IEEE80211_BAND_5GHZ] << 5;
40128707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek		}
401313935e2cf39b124c9a2ff0349b294e0b1e2e3aefLennert Buytenhek		memcpy(ap_mcs_rates, ap->ht_cap.mcs.rx_mask, 16);
4014c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek
4015c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek		rcu_read_unlock();
4016c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	}
4017c6e9601071173fed2a77f9c435c41f3b33d1018fLennert Buytenhek
4018c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) {
401913935e2cf39b124c9a2ff0349b294e0b1e2e3aefLennert Buytenhek		rc = mwl8k_cmd_set_rate(hw, vif, ap_legacy_rates, ap_mcs_rates);
40203a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhek		if (rc)
40213a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhek			goto out;
4022a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4023b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684Lennert Buytenhek		rc = mwl8k_cmd_use_fixed_rate_sta(hw);
40243a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhek		if (rc)
40253a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhek			goto out;
4026c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	}
4027a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4028c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
40297dc6a7a7635365b140af969e972900866d0bf34bLennert Buytenhek		rc = mwl8k_set_radio_preamble(hw,
40307dc6a7a7635365b140af969e972900866d0bf34bLennert Buytenhek				vif->bss_conf.use_short_preamble);
40313a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhek		if (rc)
40323a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhek			goto out;
4033c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	}
4034a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4035c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	if (changed & BSS_CHANGED_ERP_SLOT) {
40367dc6a7a7635365b140af969e972900866d0bf34bLennert Buytenhek		rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot);
40373a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhek		if (rc)
40383a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhek			goto out;
4039c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	}
4040a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4041c97470dd253831e880c72ea5d022ed7f3aee45c3Lennert Buytenhek	if (vif->bss_conf.assoc &&
4042c97470dd253831e880c72ea5d022ed7f3aee45c3Lennert Buytenhek	    (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT |
4043c97470dd253831e880c72ea5d022ed7f3aee45c3Lennert Buytenhek			BSS_CHANGED_HT))) {
4044c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek		rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates);
40453a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhek		if (rc)
40463a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhek			goto out;
4047c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	}
4048a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4049c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	if (vif->bss_conf.assoc &&
4050c3cbbe8a5cd2886e13e9c93e059d9761f3715665Lennert Buytenhek	    (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INT))) {
4051a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		/*
4052a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		 * Finalize the join.  Tell rx handler to process
4053a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		 * next beacon from our BSSID.
4054a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		 */
40550a11dfc36604d9b24deda17461b7ea69851846aaLennert Buytenhek		memcpy(priv->capture_bssid, vif->bss_conf.bssid, ETH_ALEN);
4056a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		priv->capture_beacon = true;
4057a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
4058a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
40593a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhekout:
40603a980d0a505161b99fc936827cb28ec8eb853284Lennert Buytenhek	mwl8k_fw_unlock(hw);
4061a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
4062a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4063b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhekstatic void
4064b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhekmwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
4065b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek			  struct ieee80211_bss_conf *info, u32 changed)
4066b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek{
4067b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	int rc;
4068b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
4069b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	if (mwl8k_fw_lock(hw))
4070b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		return;
4071b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
4072b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
4073b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		rc = mwl8k_set_radio_preamble(hw,
4074b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek				vif->bss_conf.use_short_preamble);
4075b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		if (rc)
4076b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek			goto out;
4077b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	}
4078b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
4079b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	if (changed & BSS_CHANGED_BASIC_RATES) {
4080b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		int idx;
4081b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		int rate;
4082b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
4083b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		/*
4084b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		 * Use lowest supported basic rate for multicasts
4085b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		 * and management frames (such as probe responses --
4086b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		 * beacons will always go out at 1 Mb/s).
4087b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		 */
4088b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		idx = ffs(vif->bss_conf.basic_rates);
40898707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek		if (idx)
40908707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek			idx--;
40918707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek
40928707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek		if (hw->conf.channel->band == IEEE80211_BAND_2GHZ)
40938707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek			rate = mwl8k_rates_24[idx].hw_value;
40948707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek		else
40958707d0262585423cdc053bf8db0912e53915e5e4Lennert Buytenhek			rate = mwl8k_rates_50[idx].hw_value;
4096b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
4097b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate);
4098b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	}
4099b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
4100b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) {
4101b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		struct sk_buff *skb;
4102b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
4103b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		skb = ieee80211_beacon_get(hw, vif);
4104b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		if (skb != NULL) {
4105aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek			mwl8k_cmd_set_beacon(hw, vif, skb->data, skb->len);
4106b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek			kfree_skb(skb);
4107b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		}
4108b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	}
4109b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
4110b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	if (changed & BSS_CHANGED_BEACON_ENABLED)
4111aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek		mwl8k_cmd_bss_start(hw, vif, info->enable_beacon);
4112b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
4113b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhekout:
4114b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	mwl8k_fw_unlock(hw);
4115b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek}
4116b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
4117b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhekstatic void
4118b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhekmwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
4119b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		       struct ieee80211_bss_conf *info, u32 changed)
4120b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek{
4121b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
4122b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
4123b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	if (!priv->ap_fw)
4124b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		mwl8k_bss_info_changed_sta(hw, vif, info, changed);
4125b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek	else
4126b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek		mwl8k_bss_info_changed_ap(hw, vif, info, changed);
4127b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek}
4128b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek
4129e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhekstatic u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw,
413022bedad3ce112d5ca1eaf043d4990fa2ed698c87Jiri Pirko				   struct netdev_hw_addr_list *mc_list)
4131e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhek{
4132e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhek	struct mwl8k_cmd_pkt *cmd;
4133e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhek
4134447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	/*
4135447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	 * Synthesize and return a command packet that programs the
4136447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	 * hardware multicast address filter.  At this point we don't
4137447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	 * know whether FIF_ALLMULTI is being requested, but if it is,
4138447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	 * we'll end up throwing this packet away and creating a new
4139447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	 * one in mwl8k_configure_filter().
4140447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	 */
414122bedad3ce112d5ca1eaf043d4990fa2ed698c87Jiri Pirko	cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_list);
4142e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhek
4143e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhek	return (unsigned long)cmd;
4144e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhek}
4145e81cd2d664fe5b75a0db9bb24b43c0dfbde32319Lennert Buytenhek
4146a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhekstatic int
4147a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhekmwl8k_configure_filter_sniffer(struct ieee80211_hw *hw,
4148a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek			       unsigned int changed_flags,
4149a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek			       unsigned int *total_flags)
4150a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek{
4151a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
4152a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek
4153a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	/*
4154a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	 * Hardware sniffer mode is mutually exclusive with STA
4155a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	 * operation, so refuse to enable sniffer mode if a STA
4156a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	 * interface is active.
4157a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	 */
4158f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek	if (!list_empty(&priv->vif_list)) {
4159a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek		if (net_ratelimit())
4160c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches			wiphy_info(hw->wiphy,
4161c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches				   "not enabling sniffer mode because STA interface is active\n");
4162a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek		return 0;
4163a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	}
4164a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek
4165a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	if (!priv->sniffer_enabled) {
416655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		if (mwl8k_cmd_enable_sniffer(hw, 1))
4167a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek			return 0;
4168a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek		priv->sniffer_enabled = true;
4169a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	}
4170a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek
4171a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	*total_flags &=	FIF_PROMISC_IN_BSS | FIF_ALLMULTI |
4172a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek			FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL |
4173a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek			FIF_OTHER_BSS;
4174a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek
4175a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	return 1;
4176a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek}
4177a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek
4178f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhekstatic struct mwl8k_vif *mwl8k_first_vif(struct mwl8k_priv *priv)
4179f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek{
4180f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek	if (!list_empty(&priv->vif_list))
4181f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek		return list_entry(priv->vif_list.next, struct mwl8k_vif, list);
4182f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek
4183f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek	return NULL;
4184f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek}
4185f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek
4186e6935ea10485f34b82d16f8dff9aa2bdf32ab4bfLennert Buytenhekstatic void mwl8k_configure_filter(struct ieee80211_hw *hw,
4187e6935ea10485f34b82d16f8dff9aa2bdf32ab4bfLennert Buytenhek				   unsigned int changed_flags,
4188e6935ea10485f34b82d16f8dff9aa2bdf32ab4bfLennert Buytenhek				   unsigned int *total_flags,
4189e6935ea10485f34b82d16f8dff9aa2bdf32ab4bfLennert Buytenhek				   u64 multicast)
4190e6935ea10485f34b82d16f8dff9aa2bdf32ab4bfLennert Buytenhek{
4191e6935ea10485f34b82d16f8dff9aa2bdf32ab4bfLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
4192a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	struct mwl8k_cmd_pkt *cmd = (void *)(unsigned long)multicast;
4193a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek
4194a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	/*
4195c0adae2caa1a152c6ec691c5d1e815e47dac2a0cLennert Buytenhek	 * AP firmware doesn't allow fine-grained control over
4196c0adae2caa1a152c6ec691c5d1e815e47dac2a0cLennert Buytenhek	 * the receive filter.
4197c0adae2caa1a152c6ec691c5d1e815e47dac2a0cLennert Buytenhek	 */
4198c0adae2caa1a152c6ec691c5d1e815e47dac2a0cLennert Buytenhek	if (priv->ap_fw) {
4199c0adae2caa1a152c6ec691c5d1e815e47dac2a0cLennert Buytenhek		*total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC;
4200c0adae2caa1a152c6ec691c5d1e815e47dac2a0cLennert Buytenhek		kfree(cmd);
4201c0adae2caa1a152c6ec691c5d1e815e47dac2a0cLennert Buytenhek		return;
4202c0adae2caa1a152c6ec691c5d1e815e47dac2a0cLennert Buytenhek	}
4203c0adae2caa1a152c6ec691c5d1e815e47dac2a0cLennert Buytenhek
4204c0adae2caa1a152c6ec691c5d1e815e47dac2a0cLennert Buytenhek	/*
4205a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	 * Enable hardware sniffer mode if FIF_CONTROL or
4206a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	 * FIF_OTHER_BSS is requested.
4207a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	 */
4208a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	if (*total_flags & (FIF_CONTROL | FIF_OTHER_BSS) &&
4209a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	    mwl8k_configure_filter_sniffer(hw, changed_flags, total_flags)) {
4210a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek		kfree(cmd);
4211a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek		return;
4212a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	}
4213a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4214e6935ea10485f34b82d16f8dff9aa2bdf32ab4bfLennert Buytenhek	/* Clear unsupported feature flags */
4215447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	*total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC;
4216a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
421790852f7aed0f90d443efd7e0f9b82d8ac8186848Lennert Buytenhek	if (mwl8k_fw_lock(hw)) {
421890852f7aed0f90d443efd7e0f9b82d8ac8186848Lennert Buytenhek		kfree(cmd);
4219e6935ea10485f34b82d16f8dff9aa2bdf32ab4bfLennert Buytenhek		return;
422090852f7aed0f90d443efd7e0f9b82d8ac8186848Lennert Buytenhek	}
4221a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4222a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	if (priv->sniffer_enabled) {
422355489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek		mwl8k_cmd_enable_sniffer(hw, 0);
4224a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek		priv->sniffer_enabled = false;
4225a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek	}
4226a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhek
4227e6935ea10485f34b82d16f8dff9aa2bdf32ab4bfLennert Buytenhek	if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
422877165d8809cda1a77bc8752148a6252d7735c12eLennert Buytenhek		if (*total_flags & FIF_BCN_PRBRESP_PROMISC) {
422977165d8809cda1a77bc8752148a6252d7735c12eLennert Buytenhek			/*
423077165d8809cda1a77bc8752148a6252d7735c12eLennert Buytenhek			 * Disable the BSS filter.
423177165d8809cda1a77bc8752148a6252d7735c12eLennert Buytenhek			 */
4232e6935ea10485f34b82d16f8dff9aa2bdf32ab4bfLennert Buytenhek			mwl8k_cmd_set_pre_scan(hw);
423377165d8809cda1a77bc8752148a6252d7735c12eLennert Buytenhek		} else {
4234f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek			struct mwl8k_vif *mwl8k_vif;
42350a11dfc36604d9b24deda17461b7ea69851846aaLennert Buytenhek			const u8 *bssid;
4236a94cc97e14c5750ec2b50b2e4ecdfb0f369ed0f4Lennert Buytenhek
423777165d8809cda1a77bc8752148a6252d7735c12eLennert Buytenhek			/*
423877165d8809cda1a77bc8752148a6252d7735c12eLennert Buytenhek			 * Enable the BSS filter.
423977165d8809cda1a77bc8752148a6252d7735c12eLennert Buytenhek			 *
424077165d8809cda1a77bc8752148a6252d7735c12eLennert Buytenhek			 * If there is an active STA interface, use that
424177165d8809cda1a77bc8752148a6252d7735c12eLennert Buytenhek			 * interface's BSSID, otherwise use a dummy one
424277165d8809cda1a77bc8752148a6252d7735c12eLennert Buytenhek			 * (where the OUI part needs to be nonzero for
424377165d8809cda1a77bc8752148a6252d7735c12eLennert Buytenhek			 * the BSSID to be accepted by POST_SCAN).
424477165d8809cda1a77bc8752148a6252d7735c12eLennert Buytenhek			 */
4245f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek			mwl8k_vif = mwl8k_first_vif(priv);
4246f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek			if (mwl8k_vif != NULL)
4247f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek				bssid = mwl8k_vif->vif->bss_conf.bssid;
4248f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek			else
4249f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek				bssid = "\x01\x00\x00\x00\x00\x00";
4250a94cc97e14c5750ec2b50b2e4ecdfb0f369ed0f4Lennert Buytenhek
4251e6935ea10485f34b82d16f8dff9aa2bdf32ab4bfLennert Buytenhek			mwl8k_cmd_set_post_scan(hw, bssid);
4252a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		}
4253a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
4254a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4255447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	/*
4256447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	 * If FIF_ALLMULTI is being requested, throw away the command
4257447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	 * packet that ->prepare_multicast() built and replace it with
4258447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	 * a command packet that enables reception of all multicast
4259447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	 * packets.
4260447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	 */
4261447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	if (*total_flags & FIF_ALLMULTI) {
4262447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek		kfree(cmd);
426322bedad3ce112d5ca1eaf043d4990fa2ed698c87Jiri Pirko		cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, NULL);
4264447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	}
4265447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek
4266447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek	if (cmd != NULL) {
4267447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek		mwl8k_post_cmd(hw, cmd);
4268447ced07d04525218ae586cd70b759b48bcb1fc8Lennert Buytenhek		kfree(cmd);
4269e6935ea10485f34b82d16f8dff9aa2bdf32ab4bfLennert Buytenhek	}
4270a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4271e6935ea10485f34b82d16f8dff9aa2bdf32ab4bfLennert Buytenhek	mwl8k_fw_unlock(hw);
4272a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
4273a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4274a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
4275a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
4276c2c2b12a8b6cd23d4abbc086642647c656bf406cLennert Buytenhek	return mwl8k_cmd_set_rts_threshold(hw, value);
4277a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
4278a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
42794a6967b88af02eebeedfbb91bc09160750225bb5Johannes Bergstatic int mwl8k_sta_remove(struct ieee80211_hw *hw,
42804a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg			    struct ieee80211_vif *vif,
42814a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg			    struct ieee80211_sta *sta)
42823f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek{
42833f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
42843f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek
42854a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg	if (priv->ap_fw)
42864a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg		return mwl8k_cmd_set_new_stn_del(hw, vif, sta->addr);
42874a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg	else
42884a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg		return mwl8k_cmd_update_stadb_del(hw, vif, sta->addr);
4289bbfd9128d3b4a80bea017ebdd47b31a80dc7eadfLennert Buytenhek}
4290bbfd9128d3b4a80bea017ebdd47b31a80dc7eadfLennert Buytenhek
42914a6967b88af02eebeedfbb91bc09160750225bb5Johannes Bergstatic int mwl8k_sta_add(struct ieee80211_hw *hw,
42924a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg			 struct ieee80211_vif *vif,
42934a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg			 struct ieee80211_sta *sta)
4294bbfd9128d3b4a80bea017ebdd47b31a80dc7eadfLennert Buytenhek{
4295bbfd9128d3b4a80bea017ebdd47b31a80dc7eadfLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
42964a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg	int ret;
4297fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	int i;
4298fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
4299fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	struct ieee80211_key_conf *key;
4300bbfd9128d3b4a80bea017ebdd47b31a80dc7eadfLennert Buytenhek
43014a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg	if (!priv->ap_fw) {
43024a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg		ret = mwl8k_cmd_update_stadb_add(hw, vif, sta);
43034a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg		if (ret >= 0) {
43044a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg			MWL8K_STA(sta)->peer_id = ret;
4305fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam			ret = 0;
43064a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg		}
4307bbfd9128d3b4a80bea017ebdd47b31a80dc7eadfLennert Buytenhek
4308d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	} else {
4309d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		ret = mwl8k_cmd_set_new_stn_add(hw, vif, sta);
4310bbfd9128d3b4a80bea017ebdd47b31a80dc7eadfLennert Buytenhek	}
43114a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg
4312d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	for (i = 0; i < NUM_WEP_KEYS; i++) {
4313d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		key = IEEE80211_KEY_CONF(mwl8k_vif->wep_key_conf[i].key);
4314d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		if (mwl8k_vif->wep_key_conf[i].enabled)
4315d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			mwl8k_set_key(hw, SET_KEY, vif, sta, key);
4316d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	}
4317fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	return ret;
4318bbfd9128d3b4a80bea017ebdd47b31a80dc7eadfLennert Buytenhek}
4319bbfd9128d3b4a80bea017ebdd47b31a80dc7eadfLennert Buytenhek
4320a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue,
4321a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			 const struct ieee80211_tx_queue_params *params)
4322a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
43233e4f542cfbf5a60f2295b2de9a31bdd14beb7b4aLennert Buytenhek	struct mwl8k_priv *priv = hw->priv;
4324a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
4325a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
43263e4f542cfbf5a60f2295b2de9a31bdd14beb7b4aLennert Buytenhek	rc = mwl8k_fw_lock(hw);
43273e4f542cfbf5a60f2295b2de9a31bdd14beb7b4aLennert Buytenhek	if (!rc) {
43280863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo		BUG_ON(queue > MWL8K_TX_QUEUES - 1);
43290863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo		memcpy(&priv->wmm_params[queue], params, sizeof(*params));
43300863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo
43313e4f542cfbf5a60f2295b2de9a31bdd14beb7b4aLennert Buytenhek		if (!priv->wmm_enabled)
433255489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			rc = mwl8k_cmd_set_wmm_mode(hw, 1);
4333a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
43343e4f542cfbf5a60f2295b2de9a31bdd14beb7b4aLennert Buytenhek		if (!rc)
433555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek			rc = mwl8k_cmd_set_edca_params(hw, queue,
433655489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek						       params->cw_min,
433755489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek						       params->cw_max,
433855489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek						       params->aifs,
433955489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek						       params->txop);
43403e4f542cfbf5a60f2295b2de9a31bdd14beb7b4aLennert Buytenhek
43413e4f542cfbf5a60f2295b2de9a31bdd14beb7b4aLennert Buytenhek		mwl8k_fw_unlock(hw);
4342a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
43433e4f542cfbf5a60f2295b2de9a31bdd14beb7b4aLennert Buytenhek
4344a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
4345a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
4346a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4347a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int mwl8k_get_stats(struct ieee80211_hw *hw,
4348a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			   struct ieee80211_low_level_stats *stats)
4349a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
435055489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	return mwl8k_cmd_get_stat(hw, stats);
4351a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
4352a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
43530d462bbb0e20863b6c796abd779bfdb534d60278John W. Linvillestatic int mwl8k_get_survey(struct ieee80211_hw *hw, int idx,
43540d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville				struct survey_info *survey)
43550d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville{
43560d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville	struct mwl8k_priv *priv = hw->priv;
43570d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville	struct ieee80211_conf *conf = &hw->conf;
43580d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville
43590d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville	if (idx != 0)
43600d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville		return -ENOENT;
43610d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville
43620d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville	survey->channel = conf->channel;
43630d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville	survey->filled = SURVEY_INFO_NOISE_DBM;
43640d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville	survey->noise = priv->noise;
43650d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville
43660d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville	return 0;
43670d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville}
43680d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville
4369a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhekstatic int
4370a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhekmwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
4371a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhek		   enum ieee80211_ampdu_mlme_action action,
43720b01f030d38e00650e2db42da083d8647aad40a5Johannes Berg		   struct ieee80211_sta *sta, u16 tid, u16 *ssn,
43730b01f030d38e00650e2db42da083d8647aad40a5Johannes Berg		   u8 buf_size)
4374a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhek{
4375a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhek	switch (action) {
4376a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhek	case IEEE80211_AMPDU_RX_START:
4377a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhek	case IEEE80211_AMPDU_RX_STOP:
4378a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhek		if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION))
4379a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhek			return -ENOTSUPP;
4380a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhek		return 0;
4381a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhek	default:
4382a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhek		return -ENOTSUPP;
4383a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhek	}
4384a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhek}
4385a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhek
4386a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic const struct ieee80211_ops mwl8k_ops = {
4387a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.tx			= mwl8k_tx,
4388a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.start			= mwl8k_start,
4389a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.stop			= mwl8k_stop,
4390a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.add_interface		= mwl8k_add_interface,
4391a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.remove_interface	= mwl8k_remove_interface,
4392a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.config			= mwl8k_config,
4393a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.bss_info_changed	= mwl8k_bss_info_changed,
43943ac64beecd27400d12cc7afb4108eef26c499f6aJohannes Berg	.prepare_multicast	= mwl8k_prepare_multicast,
4395a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.configure_filter	= mwl8k_configure_filter,
4396fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	.set_key                = mwl8k_set_key,
4397a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.set_rts_threshold	= mwl8k_set_rts_threshold,
43984a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg	.sta_add		= mwl8k_sta_add,
43994a6967b88af02eebeedfbb91bc09160750225bb5Johannes Berg	.sta_remove		= mwl8k_sta_remove,
4400a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.conf_tx		= mwl8k_conf_tx,
4401a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.get_stats		= mwl8k_get_stats,
44020d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville	.get_survey		= mwl8k_get_survey,
4403a2292d83b5dcb7f378956a124854d2b17fa53aa3Lennert Buytenhek	.ampdu_action		= mwl8k_ampdu_action,
4404a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek};
4405a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4406a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void mwl8k_finalize_join_worker(struct work_struct *work)
4407a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
4408a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv =
4409a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		container_of(work, struct mwl8k_priv, finalize_join_worker);
4410a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct sk_buff *skb = priv->beacon_skb;
441156007a028c51cbf800a6c969d6f6431d23443b99Johannes Berg	struct ieee80211_mgmt *mgmt = (void *)skb->data;
441256007a028c51cbf800a6c969d6f6431d23443b99Johannes Berg	int len = skb->len - offsetof(struct ieee80211_mgmt, u.beacon.variable);
441356007a028c51cbf800a6c969d6f6431d23443b99Johannes Berg	const u8 *tim = cfg80211_find_ie(WLAN_EID_TIM,
441456007a028c51cbf800a6c969d6f6431d23443b99Johannes Berg					 mgmt->u.beacon.variable, len);
441556007a028c51cbf800a6c969d6f6431d23443b99Johannes Berg	int dtim_period = 1;
441656007a028c51cbf800a6c969d6f6431d23443b99Johannes Berg
441756007a028c51cbf800a6c969d6f6431d23443b99Johannes Berg	if (tim && tim[1] >= 2)
441856007a028c51cbf800a6c969d6f6431d23443b99Johannes Berg		dtim_period = tim[3];
4419a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
442056007a028c51cbf800a6c969d6f6431d23443b99Johannes Berg	mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len, dtim_period);
4421a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4422f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek	dev_kfree_skb(skb);
4423a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	priv->beacon_skb = NULL;
4424a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
4425a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4426bcb628d579a61d0ab0cac4c6cc8a403de5254920John W. Linvilleenum {
44279e1b17ead81e72d3db37b4cf15cde1f613603822Lennert Buytenhek	MWL8363 = 0,
44289e1b17ead81e72d3db37b4cf15cde1f613603822Lennert Buytenhek	MWL8687,
4429bcb628d579a61d0ab0cac4c6cc8a403de5254920John W. Linville	MWL8366,
44306f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek};
44316f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
4432952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo#define MWL8K_8366_AP_FW_API 1
4433952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo#define _MWL8K_8366_AP_FW(api) "mwl8k/fmimage_8366_ap-" #api ".fw"
4434952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo#define MWL8K_8366_AP_FW(api) _MWL8K_8366_AP_FW(api)
4435952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo
4436bcb628d579a61d0ab0cac4c6cc8a403de5254920John W. Linvillestatic struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = {
44379e1b17ead81e72d3db37b4cf15cde1f613603822Lennert Buytenhek	[MWL8363] = {
44389e1b17ead81e72d3db37b4cf15cde1f613603822Lennert Buytenhek		.part_name	= "88w8363",
44399e1b17ead81e72d3db37b4cf15cde1f613603822Lennert Buytenhek		.helper_image	= "mwl8k/helper_8363.fw",
44400863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo		.fw_image_sta	= "mwl8k/fmimage_8363.fw",
44419e1b17ead81e72d3db37b4cf15cde1f613603822Lennert Buytenhek	},
444249eb691c8f48a29adfdfbdeb82433f1f8cb6524dLennert Buytenhek	[MWL8687] = {
4443bcb628d579a61d0ab0cac4c6cc8a403de5254920John W. Linville		.part_name	= "88w8687",
4444bcb628d579a61d0ab0cac4c6cc8a403de5254920John W. Linville		.helper_image	= "mwl8k/helper_8687.fw",
44450863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo		.fw_image_sta	= "mwl8k/fmimage_8687.fw",
4446bcb628d579a61d0ab0cac4c6cc8a403de5254920John W. Linville	},
444749eb691c8f48a29adfdfbdeb82433f1f8cb6524dLennert Buytenhek	[MWL8366] = {
4448bcb628d579a61d0ab0cac4c6cc8a403de5254920John W. Linville		.part_name	= "88w8366",
4449bcb628d579a61d0ab0cac4c6cc8a403de5254920John W. Linville		.helper_image	= "mwl8k/helper_8366.fw",
44500863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo		.fw_image_sta	= "mwl8k/fmimage_8366.fw",
4451952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo		.fw_image_ap	= MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API),
4452952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo		.fw_api_ap	= MWL8K_8366_AP_FW_API,
445389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek		.ap_rxd_ops	= &rxd_8366_ap_ops,
4454bcb628d579a61d0ab0cac4c6cc8a403de5254920John W. Linville	},
445545a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhek};
445645a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhek
4457c92d4edecf489dbcbb2e5dd3c513790e57e2ea0eLennert BuytenhekMODULE_FIRMWARE("mwl8k/helper_8363.fw");
4458c92d4edecf489dbcbb2e5dd3c513790e57e2ea0eLennert BuytenhekMODULE_FIRMWARE("mwl8k/fmimage_8363.fw");
4459c92d4edecf489dbcbb2e5dd3c513790e57e2ea0eLennert BuytenhekMODULE_FIRMWARE("mwl8k/helper_8687.fw");
4460c92d4edecf489dbcbb2e5dd3c513790e57e2ea0eLennert BuytenhekMODULE_FIRMWARE("mwl8k/fmimage_8687.fw");
4461c92d4edecf489dbcbb2e5dd3c513790e57e2ea0eLennert BuytenhekMODULE_FIRMWARE("mwl8k/helper_8366.fw");
4462c92d4edecf489dbcbb2e5dd3c513790e57e2ea0eLennert BuytenhekMODULE_FIRMWARE("mwl8k/fmimage_8366.fw");
4463952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian CavagnoloMODULE_FIRMWARE(MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API));
4464c92d4edecf489dbcbb2e5dd3c513790e57e2ea0eLennert Buytenhek
446545a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhekstatic DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = {
4466e5868ba10c3c5d8a56c06bbafe098103356ac03fBenjamin Larsson	{ PCI_VDEVICE(MARVELL, 0x2a0a), .driver_data = MWL8363, },
44679e1b17ead81e72d3db37b4cf15cde1f613603822Lennert Buytenhek	{ PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, },
44689e1b17ead81e72d3db37b4cf15cde1f613603822Lennert Buytenhek	{ PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, },
4469bcb628d579a61d0ab0cac4c6cc8a403de5254920John W. Linville	{ PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, },
4470bcb628d579a61d0ab0cac4c6cc8a403de5254920John W. Linville	{ PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, },
4471bcb628d579a61d0ab0cac4c6cc8a403de5254920John W. Linville	{ PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, },
4472ca66527c60385dcec878ebd90749d1fdc43bc870Lennert Buytenhek	{ PCI_VDEVICE(MARVELL, 0x2a43), .driver_data = MWL8366, },
4473bcb628d579a61d0ab0cac4c6cc8a403de5254920John W. Linville	{ },
447445a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhek};
447545a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert BuytenhekMODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table);
447645a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhek
447799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolostatic int mwl8k_request_alt_fw(struct mwl8k_priv *priv)
447899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo{
447999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	int rc;
448099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	printk(KERN_ERR "%s: Error requesting preferred fw %s.\n"
448199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	       "Trying alternative firmware %s\n", pci_name(priv->pdev),
448299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	       priv->fw_pref, priv->fw_alt);
448399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	rc = mwl8k_request_fw(priv, priv->fw_alt, &priv->fw_ucode, true);
448499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	if (rc) {
448599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		printk(KERN_ERR "%s: Error requesting alt fw %s\n",
448699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		       pci_name(priv->pdev), priv->fw_alt);
448799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		return rc;
448899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	}
448999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	return 0;
449099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo}
449199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
449299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolostatic int mwl8k_firmware_load_success(struct mwl8k_priv *priv);
449399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolostatic void mwl8k_fw_state_machine(const struct firmware *fw, void *context)
449499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo{
449599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	struct mwl8k_priv *priv = context;
449699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	struct mwl8k_device_info *di = priv->device_info;
449799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	int rc;
449899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
449999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	switch (priv->fw_state) {
450099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	case FW_STATE_INIT:
450199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		if (!fw) {
450299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			printk(KERN_ERR "%s: Error requesting helper fw %s\n",
450399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			       pci_name(priv->pdev), di->helper_image);
450499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			goto fail;
450599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		}
450699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		priv->fw_helper = fw;
450799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		rc = mwl8k_request_fw(priv, priv->fw_pref, &priv->fw_ucode,
450899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo				      true);
450999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		if (rc && priv->fw_alt) {
451099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			rc = mwl8k_request_alt_fw(priv);
451199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			if (rc)
451299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo				goto fail;
451399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			priv->fw_state = FW_STATE_LOADING_ALT;
451499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		} else if (rc)
451599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			goto fail;
451699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		else
451799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			priv->fw_state = FW_STATE_LOADING_PREF;
451899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		break;
451999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
452099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	case FW_STATE_LOADING_PREF:
452199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		if (!fw) {
452299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			if (priv->fw_alt) {
452399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo				rc = mwl8k_request_alt_fw(priv);
452499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo				if (rc)
452599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo					goto fail;
452699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo				priv->fw_state = FW_STATE_LOADING_ALT;
452799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			} else
452899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo				goto fail;
452999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		} else {
453099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			priv->fw_ucode = fw;
453199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			rc = mwl8k_firmware_load_success(priv);
453299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			if (rc)
453399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo				goto fail;
453499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			else
453599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo				complete(&priv->firmware_loading_complete);
453699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		}
453799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		break;
453899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
453999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	case FW_STATE_LOADING_ALT:
454099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		if (!fw) {
454199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			printk(KERN_ERR "%s: Error requesting alt fw %s\n",
454299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			       pci_name(priv->pdev), di->helper_image);
454399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			goto fail;
454499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		}
454599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		priv->fw_ucode = fw;
454699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		rc = mwl8k_firmware_load_success(priv);
454799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		if (rc)
454899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			goto fail;
454999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		else
455099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			complete(&priv->firmware_loading_complete);
455199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		break;
455299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
455399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	default:
455499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		printk(KERN_ERR "%s: Unexpected firmware loading state: %d\n",
455599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		       MWL8K_NAME, priv->fw_state);
455699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		BUG_ON(1);
455799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	}
455899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
455999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	return;
456099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
456199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolofail:
456299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	priv->fw_state = FW_STATE_ERROR;
456399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	complete(&priv->firmware_loading_complete);
456499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	device_release_driver(&priv->pdev->dev);
456599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	mwl8k_release_firmware(priv);
456699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo}
456799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
456899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolostatic int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image,
456999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			       bool nowait)
4570a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
45713cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	struct mwl8k_priv *priv = hw->priv;
4572a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int rc;
4573be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek
4574be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	/* Reset firmware and hardware */
4575be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	mwl8k_hw_reset(priv);
4576be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek
4577be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	/* Ask userland hotplug daemon for the device firmware */
457899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	rc = mwl8k_request_firmware(priv, fw_image, nowait);
4579be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	if (rc) {
45805db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches		wiphy_err(hw->wiphy, "Firmware files not found\n");
45813cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		return rc;
4582be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	}
4583be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek
458499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	if (nowait)
458599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		return rc;
458699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
4587be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	/* Load firmware into hardware */
4588be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	rc = mwl8k_load_firmware(hw);
45893cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (rc)
45905db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches		wiphy_err(hw->wiphy, "Cannot start firmware\n");
4591be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek
4592be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	/* Reclaim memory once firmware is successfully loaded */
4593be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	mwl8k_release_firmware(priv);
4594be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek
45953cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	return rc;
45963cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo}
45973cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
45983cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo/* initialize hw after successfully loading a firmware image */
45993cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolostatic int mwl8k_probe_hw(struct ieee80211_hw *hw)
46003cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo{
46013cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	struct mwl8k_priv *priv = hw->priv;
46023cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	int rc = 0;
46033cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	int i;
4604be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek
460591942230689c1758685499e82e53769d5e7f32ebLennert Buytenhek	if (priv->ap_fw) {
460689a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek		priv->rxd_ops = priv->device_info->ap_rxd_ops;
460791942230689c1758685499e82e53769d5e7f32ebLennert Buytenhek		if (priv->rxd_ops == NULL) {
4608c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches			wiphy_err(hw->wiphy,
4609c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches				  "Driver does not have AP firmware image support for this hardware\n");
461091942230689c1758685499e82e53769d5e7f32ebLennert Buytenhek			goto err_stop_firmware;
461191942230689c1758685499e82e53769d5e7f32ebLennert Buytenhek		}
461291942230689c1758685499e82e53769d5e7f32ebLennert Buytenhek	} else {
461389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek		priv->rxd_ops = &rxd_sta_ops;
461491942230689c1758685499e82e53769d5e7f32ebLennert Buytenhek	}
4615be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek
4616be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	priv->sniffer_enabled = false;
4617be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	priv->wmm_enabled = false;
4618be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	priv->pending_tx_pkts = 0;
4619be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek
4620a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rc = mwl8k_rxq_init(hw, 0);
4621a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (rc)
46223cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		goto err_stop_firmware;
4623a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rxq_refill(hw, 0, INT_MAX);
4624a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4625a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	for (i = 0; i < MWL8K_TX_QUEUES; i++) {
4626a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		rc = mwl8k_txq_init(hw, i);
4627a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (rc)
4628a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			goto err_free_queues;
4629a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
4630a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4631a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
4632c23b5a699471ea2ef9d146eae80e64836cfbf001Lennert Buytenhek	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
463367e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	iowrite32(MWL8K_A2H_INT_TX_DONE | MWL8K_A2H_INT_RX_READY,
46341e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek		  priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL);
4635a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(0xffffffff, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
4636a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4637a0607fd3a25ba1848a63a0d925e36d914735ab47Joe Perches	rc = request_irq(priv->pdev->irq, mwl8k_interrupt,
4638a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			 IRQF_SHARED, MWL8K_NAME, hw);
4639a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (rc) {
46405db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches		wiphy_err(hw->wiphy, "failed to register IRQ handler\n");
4641a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		goto err_free_queues;
4642a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
4643a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4644a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/*
4645a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 * Temporarily enable interrupts.  Initial firmware host
4646c2c2b12a8b6cd23d4abbc086642647c656bf406cLennert Buytenhek	 * commands use interrupts and avoid polling.  Disable
4647a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 * interrupts when done.
4648a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	 */
4649c23b5a699471ea2ef9d146eae80e64836cfbf001Lennert Buytenhek	iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
4650a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4651a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* Get config data, mac addrs etc */
465242fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	if (priv->ap_fw) {
465342fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		rc = mwl8k_cmd_get_hw_spec_ap(hw);
465442fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		if (!rc)
465542fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek			rc = mwl8k_cmd_set_hw_spec(hw);
465642fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	} else {
465742fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek		rc = mwl8k_cmd_get_hw_spec_sta(hw);
465842fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	}
4659a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (rc) {
46605db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches		wiphy_err(hw->wiphy, "Cannot initialise firmware\n");
4661be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek		goto err_free_irq;
4662a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
4663a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4664a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* Turn radio off */
466555489b6ed6801a42636fc3d4594b77dda9c409f2Lennert Buytenhek	rc = mwl8k_cmd_radio_disable(hw);
4666a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (rc) {
46675db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches		wiphy_err(hw->wiphy, "Cannot disable\n");
4668be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek		goto err_free_irq;
4669a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
4670a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
467132060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek	/* Clear MAC address */
4672aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek	rc = mwl8k_cmd_set_mac_addr(hw, NULL, "\x00\x00\x00\x00\x00\x00");
467332060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek	if (rc) {
46745db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches		wiphy_err(hw->wiphy, "Cannot clear MAC address\n");
4675be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek		goto err_free_irq;
467632060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek	}
467732060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek
4678a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* Disable interrupts */
4679a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
4680a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	free_irq(priv->pdev->irq, hw);
4681a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4682c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches	wiphy_info(hw->wiphy, "%s v%d, %pm, %s firmware %u.%u.%u.%u\n",
4683c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches		   priv->device_info->part_name,
4684c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches		   priv->hw_rev, hw->wiphy->perm_addr,
4685c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches		   priv->ap_fw ? "AP" : "STA",
4686c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches		   (priv->fw_rev >> 24) & 0xff, (priv->fw_rev >> 16) & 0xff,
4687c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches		   (priv->fw_rev >> 8) & 0xff, priv->fw_rev & 0xff);
4688a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4689a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return 0;
4690a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4691a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekerr_free_irq:
4692a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
4693a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	free_irq(priv->pdev->irq, hw);
4694a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4695a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekerr_free_queues:
4696a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	for (i = 0; i < MWL8K_TX_QUEUES; i++)
4697a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		mwl8k_txq_deinit(hw, i);
4698a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	mwl8k_rxq_deinit(hw, 0);
4699a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
47003cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnoloerr_stop_firmware:
47013cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	mwl8k_hw_reset(priv);
47023cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47033cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	return rc;
47043cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo}
47053cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47063cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo/*
47073cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo * invoke mwl8k_reload_firmware to change the firmware image after the device
47083cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo * has already been registered
47093cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo */
47103cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolostatic int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)
47113cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo{
47123cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	int i, rc = 0;
47133cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	struct mwl8k_priv *priv = hw->priv;
47143cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47153cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	mwl8k_stop(hw);
47163cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	mwl8k_rxq_deinit(hw, 0);
47173cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47183cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	for (i = 0; i < MWL8K_TX_QUEUES; i++)
47193cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		mwl8k_txq_deinit(hw, i);
47203cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
472199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	rc = mwl8k_init_firmware(hw, fw_image, false);
47223cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (rc)
47233cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		goto fail;
47243cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47253cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	rc = mwl8k_probe_hw(hw);
47263cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (rc)
47273cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		goto fail;
47283cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47293cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	rc = mwl8k_start(hw);
47303cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (rc)
47313cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		goto fail;
47323cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47333cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	rc = mwl8k_config(hw, ~0);
47343cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (rc)
47353cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		goto fail;
47363cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47373cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	for (i = 0; i < MWL8K_TX_QUEUES; i++) {
47383cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		rc = mwl8k_conf_tx(hw, i, &priv->wmm_params[i]);
47393cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		if (rc)
47403cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo			goto fail;
47413cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	}
47423cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47433cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	return rc;
47443cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47453cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolofail:
47463cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	printk(KERN_WARNING "mwl8k: Failed to reload firmware image.\n");
47473cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	return rc;
47483cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo}
47493cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47503cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolostatic int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
47513cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo{
47523cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	struct ieee80211_hw *hw = priv->hw;
47533cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	int i, rc;
47543cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
475599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	rc = mwl8k_load_firmware(hw);
475699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	mwl8k_release_firmware(priv);
475799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	if (rc) {
475899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		wiphy_err(hw->wiphy, "Cannot start firmware\n");
475999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		return rc;
476099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	}
476199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
47623cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	/*
47633cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	 * Extra headroom is the size of the required DMA header
47643cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	 * minus the size of the smallest 802.11 frame (CTS frame).
47653cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	 */
47663cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	hw->extra_tx_headroom =
47673cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		sizeof(struct mwl8k_dma_data) - sizeof(struct ieee80211_cts);
47683cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47693cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	hw->channel_change_time = 10;
47703cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47713cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	hw->queues = MWL8K_TX_QUEUES;
47723cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47733cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	/* Set rssi values to dBm */
47740bf22c3751d19f9be20205c0e7112723618a4858Nishant Sarmukadam	hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_HAS_RATE_CONTROL;
47753cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	hw->vif_data_size = sizeof(struct mwl8k_vif);
47763cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	hw->sta_data_size = sizeof(struct mwl8k_sta);
47773cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47783cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	priv->macids_used = 0;
47793cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	INIT_LIST_HEAD(&priv->vif_list);
47803cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47813cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	/* Set default radio state and preamble */
47823cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	priv->radio_on = 0;
47833cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	priv->radio_short_preamble = 0;
47843cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47853cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	/* Finalize join worker */
47863cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker);
47873cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47883cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	/* TX reclaim and RX tasklets.  */
47893cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw);
47903cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	tasklet_disable(&priv->poll_tx_task);
47913cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	tasklet_init(&priv->poll_rx_task, mwl8k_rx_poll, (unsigned long)hw);
47923cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	tasklet_disable(&priv->poll_rx_task);
47933cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47943cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	/* Power management cookie */
47953cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma);
47963cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (priv->cookie == NULL)
47973cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		return -ENOMEM;
47983cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
47993cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	mutex_init(&priv->fw_mutex);
48003cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	priv->fw_mutex_owner = NULL;
48013cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	priv->fw_mutex_depth = 0;
48023cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	priv->hostcmd_wait = NULL;
48033cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48043cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	spin_lock_init(&priv->tx_lock);
48053cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48063cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	priv->tx_wait = NULL;
48073cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48083cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	rc = mwl8k_probe_hw(hw);
48093cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (rc)
48103cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		goto err_free_cookie;
48113cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48123cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	hw->wiphy->interface_modes = 0;
48133cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (priv->ap_macids_supported || priv->device_info->fw_image_ap)
48143cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP);
48153cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (priv->sta_macids_supported || priv->device_info->fw_image_sta)
48163cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
48173cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48183cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	rc = ieee80211_register_hw(hw);
48193cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (rc) {
48203cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		wiphy_err(hw->wiphy, "Cannot register device\n");
48213cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		goto err_unprobe_hw;
48223cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	}
48233cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48243cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	return 0;
48253cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48263cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnoloerr_unprobe_hw:
48273cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	for (i = 0; i < MWL8K_TX_QUEUES; i++)
48283cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		mwl8k_txq_deinit(hw, i);
48293cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	mwl8k_rxq_deinit(hw, 0);
48303cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
4831be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhekerr_free_cookie:
4832a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (priv->cookie != NULL)
4833a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		pci_free_consistent(priv->pdev, 4,
4834a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				priv->cookie, priv->cookie_dma);
4835a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
48363cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	return rc;
48373cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo}
48383cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolostatic int __devinit mwl8k_probe(struct pci_dev *pdev,
48393cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo				 const struct pci_device_id *id)
48403cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo{
48413cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	static int printed_version;
48423cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	struct ieee80211_hw *hw;
48433cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	struct mwl8k_priv *priv;
48440863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	struct mwl8k_device_info *di;
48453cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	int rc;
48463cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48473cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (!printed_version) {
48483cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		printk(KERN_INFO "%s version %s\n", MWL8K_DESC, MWL8K_VERSION);
48493cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		printed_version = 1;
48503cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	}
48513cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48523cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48533cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	rc = pci_enable_device(pdev);
48543cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (rc) {
48553cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		printk(KERN_ERR "%s: Cannot enable new PCI device\n",
48563cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		       MWL8K_NAME);
48573cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		return rc;
48583cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	}
48593cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48603cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	rc = pci_request_regions(pdev, MWL8K_NAME);
48613cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (rc) {
48623cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		printk(KERN_ERR "%s: Cannot obtain PCI resources\n",
48633cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		       MWL8K_NAME);
48643cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		goto err_disable_device;
48653cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	}
48663cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48673cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	pci_set_master(pdev);
48683cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48693cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48703cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	hw = ieee80211_alloc_hw(sizeof(*priv), &mwl8k_ops);
48713cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (hw == NULL) {
48723cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		printk(KERN_ERR "%s: ieee80211 alloc failed\n", MWL8K_NAME);
48733cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		rc = -ENOMEM;
48743cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		goto err_free_reg;
48753cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	}
48763cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48773cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	SET_IEEE80211_DEV(hw, &pdev->dev);
48783cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	pci_set_drvdata(pdev, hw);
48793cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48803cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	priv = hw->priv;
48813cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	priv->hw = hw;
48823cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	priv->pdev = pdev;
48833cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	priv->device_info = &mwl8k_info_tbl[id->driver_data];
48843cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48853cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48863cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	priv->sram = pci_iomap(pdev, 0, 0x10000);
48873cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (priv->sram == NULL) {
48883cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		wiphy_err(hw->wiphy, "Cannot map device SRAM\n");
48893cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		goto err_iounmap;
48903cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	}
48913cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
48923cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	/*
48933cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	 * If BAR0 is a 32 bit BAR, the register BAR will be BAR1.
48943cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	 * If BAR0 is a 64 bit BAR, the register BAR will be BAR2.
48953cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	 */
48963cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	priv->regs = pci_iomap(pdev, 1, 0x10000);
48973cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (priv->regs == NULL) {
48983cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		priv->regs = pci_iomap(pdev, 2, 0x10000);
48993cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		if (priv->regs == NULL) {
49003cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo			wiphy_err(hw->wiphy, "Cannot map device registers\n");
49013cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo			goto err_iounmap;
49023cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		}
49033cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	}
49043cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
49050863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	/*
490699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	 * Choose the initial fw image depending on user input.  If a second
490799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	 * image is available, make it the alternative image that will be
490899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	 * loaded if the first one fails.
49090863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	 */
491099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	init_completion(&priv->firmware_loading_complete);
49110863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	di = priv->device_info;
491299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	if (ap_mode_default && di->fw_image_ap) {
491399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		priv->fw_pref = di->fw_image_ap;
491499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		priv->fw_alt = di->fw_image_sta;
491599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	} else if (!ap_mode_default && di->fw_image_sta) {
491699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		priv->fw_pref = di->fw_image_sta;
491799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		priv->fw_alt = di->fw_image_ap;
491899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	} else if (ap_mode_default && !di->fw_image_ap && di->fw_image_sta) {
49190863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo		printk(KERN_WARNING "AP fw is unavailable.  Using STA fw.");
492099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		priv->fw_pref = di->fw_image_sta;
49210863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	} else if (!ap_mode_default && !di->fw_image_sta && di->fw_image_ap) {
49220863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo		printk(KERN_WARNING "STA fw is unavailable.  Using AP fw.");
492399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		priv->fw_pref = di->fw_image_ap;
492499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	}
492599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	rc = mwl8k_init_firmware(hw, priv->fw_pref, true);
49263cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo	if (rc)
49273cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo		goto err_stop_firmware;
492899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	return rc;
49293cc7772c0a3cc193fa9873816168bd34d4f16837Brian Cavagnolo
4930be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhekerr_stop_firmware:
4931be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	mwl8k_hw_reset(priv);
4932be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek
4933be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhekerr_iounmap:
4934a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (priv->regs != NULL)
4935a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		pci_iounmap(pdev, priv->regs);
4936a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
49375b9482dda6dda11dc7050ffa5b4ebfb0c775880fLennert Buytenhek	if (priv->sram != NULL)
49385b9482dda6dda11dc7050ffa5b4ebfb0c775880fLennert Buytenhek		pci_iounmap(pdev, priv->sram);
49395b9482dda6dda11dc7050ffa5b4ebfb0c775880fLennert Buytenhek
4940a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	pci_set_drvdata(pdev, NULL);
4941a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	ieee80211_free_hw(hw);
4942a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4943a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekerr_free_reg:
4944a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	pci_release_regions(pdev);
49453db95e50c8813d8ed04a1ec7cd7b77dba7c81c80Lennert Buytenhek
49463db95e50c8813d8ed04a1ec7cd7b77dba7c81c80Lennert Buytenhekerr_disable_device:
4947a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	pci_disable_device(pdev);
4948a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4949a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return rc;
4950a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
4951a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4952230f7af0d8f6f2019e64920378b3b66e7d3e99a5Joerg Albertstatic void __devexit mwl8k_shutdown(struct pci_dev *pdev)
4953a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
4954a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	printk(KERN_ERR "===>%s(%u)\n", __func__, __LINE__);
4955a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
4956a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4957230f7af0d8f6f2019e64920378b3b66e7d3e99a5Joerg Albertstatic void __devexit mwl8k_remove(struct pci_dev *pdev)
4958a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
4959a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
4960a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct mwl8k_priv *priv;
4961a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int i;
4962a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4963a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (hw == NULL)
4964a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return;
4965a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	priv = hw->priv;
4966a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
496799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	wait_for_completion(&priv->firmware_loading_complete);
496899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
496999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	if (priv->fw_state == FW_STATE_ERROR) {
497099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		mwl8k_hw_reset(priv);
497199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		goto unmap;
497299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	}
497399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
4974a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	ieee80211_stop_queues(hw);
4975a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
497660aa569f9212a13382c29cc734f275dec0f55e0bLennert Buytenhek	ieee80211_unregister_hw(hw);
497760aa569f9212a13382c29cc734f275dec0f55e0bLennert Buytenhek
497867e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	/* Remove TX reclaim and RX tasklets.  */
49791e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek	tasklet_kill(&priv->poll_tx_task);
498067e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek	tasklet_kill(&priv->poll_rx_task);
4981a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4982a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* Stop hardware */
4983a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	mwl8k_hw_reset(priv);
4984a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4985a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* Return all skbs to mac80211 */
4986a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	for (i = 0; i < MWL8K_TX_QUEUES; i++)
4987efb7c49a68cf206f35793d7799608e1d69a209f9Lennert Buytenhek		mwl8k_txq_reclaim(hw, i, INT_MAX, 1);
4988a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4989a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	for (i = 0; i < MWL8K_TX_QUEUES; i++)
4990a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		mwl8k_txq_deinit(hw, i);
4991a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4992a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	mwl8k_rxq_deinit(hw, 0);
4993a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
4994c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek	pci_free_consistent(priv->pdev, 4, priv->cookie, priv->cookie_dma);
4995a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
499699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolounmap:
4997a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	pci_iounmap(pdev, priv->regs);
49985b9482dda6dda11dc7050ffa5b4ebfb0c775880fLennert Buytenhek	pci_iounmap(pdev, priv->sram);
4999a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	pci_set_drvdata(pdev, NULL);
5000a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	ieee80211_free_hw(hw);
5001a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	pci_release_regions(pdev);
5002a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	pci_disable_device(pdev);
5003a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
5004a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
5005a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic struct pci_driver mwl8k_driver = {
5006a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.name		= MWL8K_NAME,
500745a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhek	.id_table	= mwl8k_pci_id_table,
5008a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.probe		= mwl8k_probe,
5009a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.remove		= __devexit_p(mwl8k_remove),
5010a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.shutdown	= __devexit_p(mwl8k_shutdown),
5011a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek};
5012a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
5013a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int __init mwl8k_init(void)
5014a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
5015a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return pci_register_driver(&mwl8k_driver);
5016a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
5017a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
5018a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void __exit mwl8k_exit(void)
5019a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
5020a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	pci_unregister_driver(&mwl8k_driver);
5021a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
5022a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
5023a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekmodule_init(mwl8k_init);
5024a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekmodule_exit(mwl8k_exit);
5025c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek
5026c2c357ce309221b85fd36e50aade66d01a556cdeLennert BuytenhekMODULE_DESCRIPTION(MWL8K_DESC);
5027c2c357ce309221b85fd36e50aade66d01a556cdeLennert BuytenhekMODULE_VERSION(MWL8K_VERSION);
5028c2c357ce309221b85fd36e50aade66d01a556cdeLennert BuytenhekMODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com>");
5029c2c357ce309221b85fd36e50aade66d01a556cdeLennert BuytenhekMODULE_LICENSE("GPL");
5030