162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * drivers/net/wireless/mwl8k.c
362306a36Sopenharmony_ci * Driver for Marvell TOPDOG 802.11 Wireless cards
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2008, 2009, 2010 Marvell Semiconductor Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
862306a36Sopenharmony_ci * License version 2.  This program is licensed "as is" without any
962306a36Sopenharmony_ci * warranty of any kind, whether express or implied.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/sched.h>
1662306a36Sopenharmony_ci#include <linux/spinlock.h>
1762306a36Sopenharmony_ci#include <linux/list.h>
1862306a36Sopenharmony_ci#include <linux/pci.h>
1962306a36Sopenharmony_ci#include <linux/delay.h>
2062306a36Sopenharmony_ci#include <linux/completion.h>
2162306a36Sopenharmony_ci#include <linux/etherdevice.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci#include <net/mac80211.h>
2462306a36Sopenharmony_ci#include <linux/moduleparam.h>
2562306a36Sopenharmony_ci#include <linux/firmware.h>
2662306a36Sopenharmony_ci#include <linux/workqueue.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define MWL8K_DESC	"Marvell TOPDOG(R) 802.11 Wireless Network Driver"
2962306a36Sopenharmony_ci#define MWL8K_NAME	KBUILD_MODNAME
3062306a36Sopenharmony_ci#define MWL8K_VERSION	"0.13"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* Module parameters */
3362306a36Sopenharmony_cistatic bool ap_mode_default;
3462306a36Sopenharmony_cimodule_param(ap_mode_default, bool, 0);
3562306a36Sopenharmony_ciMODULE_PARM_DESC(ap_mode_default,
3662306a36Sopenharmony_ci		 "Set to 1 to make ap mode the default instead of sta mode");
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* Register definitions */
3962306a36Sopenharmony_ci#define MWL8K_HIU_GEN_PTR			0x00000c10
4062306a36Sopenharmony_ci#define  MWL8K_MODE_STA				 0x0000005a
4162306a36Sopenharmony_ci#define  MWL8K_MODE_AP				 0x000000a5
4262306a36Sopenharmony_ci#define MWL8K_HIU_INT_CODE			0x00000c14
4362306a36Sopenharmony_ci#define  MWL8K_FWSTA_READY			 0xf0f1f2f4
4462306a36Sopenharmony_ci#define  MWL8K_FWAP_READY			 0xf1f2f4a5
4562306a36Sopenharmony_ci#define  MWL8K_INT_CODE_CMD_FINISHED		 0x00000005
4662306a36Sopenharmony_ci#define MWL8K_HIU_SCRATCH			0x00000c40
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* Host->device communications */
4962306a36Sopenharmony_ci#define MWL8K_HIU_H2A_INTERRUPT_EVENTS		0x00000c18
5062306a36Sopenharmony_ci#define MWL8K_HIU_H2A_INTERRUPT_STATUS		0x00000c1c
5162306a36Sopenharmony_ci#define MWL8K_HIU_H2A_INTERRUPT_MASK		0x00000c20
5262306a36Sopenharmony_ci#define MWL8K_HIU_H2A_INTERRUPT_CLEAR_SEL	0x00000c24
5362306a36Sopenharmony_ci#define MWL8K_HIU_H2A_INTERRUPT_STATUS_MASK	0x00000c28
5462306a36Sopenharmony_ci#define  MWL8K_H2A_INT_DUMMY			 (1 << 20)
5562306a36Sopenharmony_ci#define  MWL8K_H2A_INT_RESET			 (1 << 15)
5662306a36Sopenharmony_ci#define  MWL8K_H2A_INT_DOORBELL			 (1 << 1)
5762306a36Sopenharmony_ci#define  MWL8K_H2A_INT_PPA_READY		 (1 << 0)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* Device->host communications */
6062306a36Sopenharmony_ci#define MWL8K_HIU_A2H_INTERRUPT_EVENTS		0x00000c2c
6162306a36Sopenharmony_ci#define MWL8K_HIU_A2H_INTERRUPT_STATUS		0x00000c30
6262306a36Sopenharmony_ci#define MWL8K_HIU_A2H_INTERRUPT_MASK		0x00000c34
6362306a36Sopenharmony_ci#define MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL	0x00000c38
6462306a36Sopenharmony_ci#define MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK	0x00000c3c
6562306a36Sopenharmony_ci#define  MWL8K_A2H_INT_DUMMY			 (1 << 20)
6662306a36Sopenharmony_ci#define  MWL8K_A2H_INT_BA_WATCHDOG		 (1 << 14)
6762306a36Sopenharmony_ci#define  MWL8K_A2H_INT_CHNL_SWITCHED		 (1 << 11)
6862306a36Sopenharmony_ci#define  MWL8K_A2H_INT_QUEUE_EMPTY		 (1 << 10)
6962306a36Sopenharmony_ci#define  MWL8K_A2H_INT_RADAR_DETECT		 (1 << 7)
7062306a36Sopenharmony_ci#define  MWL8K_A2H_INT_RADIO_ON			 (1 << 6)
7162306a36Sopenharmony_ci#define  MWL8K_A2H_INT_RADIO_OFF		 (1 << 5)
7262306a36Sopenharmony_ci#define  MWL8K_A2H_INT_MAC_EVENT		 (1 << 3)
7362306a36Sopenharmony_ci#define  MWL8K_A2H_INT_OPC_DONE			 (1 << 2)
7462306a36Sopenharmony_ci#define  MWL8K_A2H_INT_RX_READY			 (1 << 1)
7562306a36Sopenharmony_ci#define  MWL8K_A2H_INT_TX_DONE			 (1 << 0)
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/* HW micro second timer register
7862306a36Sopenharmony_ci * located at offset 0xA600. This
7962306a36Sopenharmony_ci * will be used to timestamp tx
8062306a36Sopenharmony_ci * packets.
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define	MWL8K_HW_TIMER_REGISTER			0x0000a600
8462306a36Sopenharmony_ci#define BBU_RXRDY_CNT_REG			0x0000a860
8562306a36Sopenharmony_ci#define NOK_CCA_CNT_REG				0x0000a6a0
8662306a36Sopenharmony_ci#define BBU_AVG_NOISE_VAL			0x67
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#define MWL8K_A2H_EVENTS	(MWL8K_A2H_INT_DUMMY | \
8962306a36Sopenharmony_ci				 MWL8K_A2H_INT_CHNL_SWITCHED | \
9062306a36Sopenharmony_ci				 MWL8K_A2H_INT_QUEUE_EMPTY | \
9162306a36Sopenharmony_ci				 MWL8K_A2H_INT_RADAR_DETECT | \
9262306a36Sopenharmony_ci				 MWL8K_A2H_INT_RADIO_ON | \
9362306a36Sopenharmony_ci				 MWL8K_A2H_INT_RADIO_OFF | \
9462306a36Sopenharmony_ci				 MWL8K_A2H_INT_MAC_EVENT | \
9562306a36Sopenharmony_ci				 MWL8K_A2H_INT_OPC_DONE | \
9662306a36Sopenharmony_ci				 MWL8K_A2H_INT_RX_READY | \
9762306a36Sopenharmony_ci				 MWL8K_A2H_INT_TX_DONE | \
9862306a36Sopenharmony_ci				 MWL8K_A2H_INT_BA_WATCHDOG)
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define MWL8K_RX_QUEUES		1
10162306a36Sopenharmony_ci#define MWL8K_TX_WMM_QUEUES	4
10262306a36Sopenharmony_ci#define MWL8K_MAX_AMPDU_QUEUES	8
10362306a36Sopenharmony_ci#define MWL8K_MAX_TX_QUEUES	(MWL8K_TX_WMM_QUEUES + MWL8K_MAX_AMPDU_QUEUES)
10462306a36Sopenharmony_ci#define mwl8k_tx_queues(priv)	(MWL8K_TX_WMM_QUEUES + (priv)->num_ampdu_queues)
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/* txpriorities are mapped with hw queues.
10762306a36Sopenharmony_ci * Each hw queue has a txpriority.
10862306a36Sopenharmony_ci */
10962306a36Sopenharmony_ci#define TOTAL_HW_TX_QUEUES	8
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/* Each HW queue can have one AMPDU stream.
11262306a36Sopenharmony_ci * But, because one of the hw queue is reserved,
11362306a36Sopenharmony_ci * maximum AMPDU queues that can be created are
11462306a36Sopenharmony_ci * one short of total tx queues.
11562306a36Sopenharmony_ci */
11662306a36Sopenharmony_ci#define MWL8K_NUM_AMPDU_STREAMS	(TOTAL_HW_TX_QUEUES - 1)
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci#define MWL8K_NUM_CHANS 18
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistruct rxd_ops {
12162306a36Sopenharmony_ci	int rxd_size;
12262306a36Sopenharmony_ci	void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr);
12362306a36Sopenharmony_ci	void (*rxd_refill)(void *rxd, dma_addr_t addr, int len);
12462306a36Sopenharmony_ci	int (*rxd_process)(void *rxd, struct ieee80211_rx_status *status,
12562306a36Sopenharmony_ci			   __le16 *qos, s8 *noise);
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistruct mwl8k_device_info {
12962306a36Sopenharmony_ci	char *part_name;
13062306a36Sopenharmony_ci	char *helper_image;
13162306a36Sopenharmony_ci	char *fw_image_sta;
13262306a36Sopenharmony_ci	char *fw_image_ap;
13362306a36Sopenharmony_ci	struct rxd_ops *ap_rxd_ops;
13462306a36Sopenharmony_ci	u32 fw_api_ap;
13562306a36Sopenharmony_ci};
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistruct mwl8k_rx_queue {
13862306a36Sopenharmony_ci	int rxd_count;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* hw receives here */
14162306a36Sopenharmony_ci	int head;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/* refill descs here */
14462306a36Sopenharmony_ci	int tail;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	void *rxd;
14762306a36Sopenharmony_ci	dma_addr_t rxd_dma;
14862306a36Sopenharmony_ci	struct {
14962306a36Sopenharmony_ci		struct sk_buff *skb;
15062306a36Sopenharmony_ci		DEFINE_DMA_UNMAP_ADDR(dma);
15162306a36Sopenharmony_ci	} *buf;
15262306a36Sopenharmony_ci};
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistruct mwl8k_tx_queue {
15562306a36Sopenharmony_ci	/* hw transmits here */
15662306a36Sopenharmony_ci	int head;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/* sw appends here */
15962306a36Sopenharmony_ci	int tail;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	unsigned int len;
16262306a36Sopenharmony_ci	struct mwl8k_tx_desc *txd;
16362306a36Sopenharmony_ci	dma_addr_t txd_dma;
16462306a36Sopenharmony_ci	struct sk_buff **skb;
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cienum {
16862306a36Sopenharmony_ci	AMPDU_NO_STREAM,
16962306a36Sopenharmony_ci	AMPDU_STREAM_NEW,
17062306a36Sopenharmony_ci	AMPDU_STREAM_IN_PROGRESS,
17162306a36Sopenharmony_ci	AMPDU_STREAM_ACTIVE,
17262306a36Sopenharmony_ci};
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistruct mwl8k_ampdu_stream {
17562306a36Sopenharmony_ci	struct ieee80211_sta *sta;
17662306a36Sopenharmony_ci	u8 tid;
17762306a36Sopenharmony_ci	u8 state;
17862306a36Sopenharmony_ci	u8 idx;
17962306a36Sopenharmony_ci};
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistruct mwl8k_priv {
18262306a36Sopenharmony_ci	struct ieee80211_hw *hw;
18362306a36Sopenharmony_ci	struct pci_dev *pdev;
18462306a36Sopenharmony_ci	int irq;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	struct mwl8k_device_info *device_info;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	void __iomem *sram;
18962306a36Sopenharmony_ci	void __iomem *regs;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* firmware */
19262306a36Sopenharmony_ci	const struct firmware *fw_helper;
19362306a36Sopenharmony_ci	const struct firmware *fw_ucode;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* hardware/firmware parameters */
19662306a36Sopenharmony_ci	bool ap_fw;
19762306a36Sopenharmony_ci	struct rxd_ops *rxd_ops;
19862306a36Sopenharmony_ci	struct ieee80211_supported_band band_24;
19962306a36Sopenharmony_ci	struct ieee80211_channel channels_24[14];
20062306a36Sopenharmony_ci	struct ieee80211_rate rates_24[13];
20162306a36Sopenharmony_ci	struct ieee80211_supported_band band_50;
20262306a36Sopenharmony_ci	struct ieee80211_channel channels_50[9];
20362306a36Sopenharmony_ci	struct ieee80211_rate rates_50[8];
20462306a36Sopenharmony_ci	u32 ap_macids_supported;
20562306a36Sopenharmony_ci	u32 sta_macids_supported;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* Ampdu stream information */
20862306a36Sopenharmony_ci	u8 num_ampdu_queues;
20962306a36Sopenharmony_ci	spinlock_t stream_lock;
21062306a36Sopenharmony_ci	struct mwl8k_ampdu_stream ampdu[MWL8K_MAX_AMPDU_QUEUES];
21162306a36Sopenharmony_ci	struct work_struct watchdog_ba_handle;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* firmware access */
21462306a36Sopenharmony_ci	struct mutex fw_mutex;
21562306a36Sopenharmony_ci	struct task_struct *fw_mutex_owner;
21662306a36Sopenharmony_ci	struct task_struct *hw_restart_owner;
21762306a36Sopenharmony_ci	int fw_mutex_depth;
21862306a36Sopenharmony_ci	struct completion *hostcmd_wait;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	atomic_t watchdog_event_pending;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* lock held over TX and TX reap */
22362306a36Sopenharmony_ci	spinlock_t tx_lock;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* TX quiesce completion, protected by fw_mutex and tx_lock */
22662306a36Sopenharmony_ci	struct completion *tx_wait;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* List of interfaces.  */
22962306a36Sopenharmony_ci	u32 macids_used;
23062306a36Sopenharmony_ci	struct list_head vif_list;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* power management status cookie from firmware */
23362306a36Sopenharmony_ci	u32 *cookie;
23462306a36Sopenharmony_ci	dma_addr_t cookie_dma;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	u16 num_mcaddrs;
23762306a36Sopenharmony_ci	u8 hw_rev;
23862306a36Sopenharmony_ci	u32 fw_rev;
23962306a36Sopenharmony_ci	u32 caps;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/*
24262306a36Sopenharmony_ci	 * Running count of TX packets in flight, to avoid
24362306a36Sopenharmony_ci	 * iterating over the transmit rings each time.
24462306a36Sopenharmony_ci	 */
24562306a36Sopenharmony_ci	int pending_tx_pkts;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	struct mwl8k_rx_queue rxq[MWL8K_RX_QUEUES];
24862306a36Sopenharmony_ci	struct mwl8k_tx_queue txq[MWL8K_MAX_TX_QUEUES];
24962306a36Sopenharmony_ci	u32 txq_offset[MWL8K_MAX_TX_QUEUES];
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	bool radio_on;
25262306a36Sopenharmony_ci	bool radio_short_preamble;
25362306a36Sopenharmony_ci	bool sniffer_enabled;
25462306a36Sopenharmony_ci	bool wmm_enabled;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* XXX need to convert this to handle multiple interfaces */
25762306a36Sopenharmony_ci	bool capture_beacon;
25862306a36Sopenharmony_ci	u8 capture_bssid[ETH_ALEN];
25962306a36Sopenharmony_ci	struct sk_buff *beacon_skb;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/*
26262306a36Sopenharmony_ci	 * This FJ worker has to be global as it is scheduled from the
26362306a36Sopenharmony_ci	 * RX handler.  At this point we don't know which interface it
26462306a36Sopenharmony_ci	 * belongs to until the list of bssids waiting to complete join
26562306a36Sopenharmony_ci	 * is checked.
26662306a36Sopenharmony_ci	 */
26762306a36Sopenharmony_ci	struct work_struct finalize_join_worker;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	/* Tasklet to perform TX reclaim.  */
27062306a36Sopenharmony_ci	struct tasklet_struct poll_tx_task;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* Tasklet to perform RX.  */
27362306a36Sopenharmony_ci	struct tasklet_struct poll_rx_task;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/* Most recently reported noise in dBm */
27662306a36Sopenharmony_ci	s8 noise;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/*
27962306a36Sopenharmony_ci	 * preserve the queue configurations so they can be restored if/when
28062306a36Sopenharmony_ci	 * the firmware image is swapped.
28162306a36Sopenharmony_ci	 */
28262306a36Sopenharmony_ci	struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_WMM_QUEUES];
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* To perform the task of reloading the firmware */
28562306a36Sopenharmony_ci	struct work_struct fw_reload;
28662306a36Sopenharmony_ci	bool hw_restart_in_progress;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* async firmware loading state */
28962306a36Sopenharmony_ci	unsigned fw_state;
29062306a36Sopenharmony_ci	char *fw_pref;
29162306a36Sopenharmony_ci	char *fw_alt;
29262306a36Sopenharmony_ci	bool is_8764;
29362306a36Sopenharmony_ci	struct completion firmware_loading_complete;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* bitmap of running BSSes */
29662306a36Sopenharmony_ci	u32 running_bsses;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	/* ACS related */
29962306a36Sopenharmony_ci	bool sw_scan_start;
30062306a36Sopenharmony_ci	struct ieee80211_channel *acs_chan;
30162306a36Sopenharmony_ci	unsigned long channel_time;
30262306a36Sopenharmony_ci	struct survey_info survey[MWL8K_NUM_CHANS];
30362306a36Sopenharmony_ci};
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci#define MAX_WEP_KEY_LEN         13
30662306a36Sopenharmony_ci#define NUM_WEP_KEYS            4
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci/* Per interface specific private data */
30962306a36Sopenharmony_cistruct mwl8k_vif {
31062306a36Sopenharmony_ci	struct list_head list;
31162306a36Sopenharmony_ci	struct ieee80211_vif *vif;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/* Firmware macid for this vif.  */
31462306a36Sopenharmony_ci	int macid;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* Non AMPDU sequence number assigned by driver.  */
31762306a36Sopenharmony_ci	u16 seqno;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	/* Saved WEP keys */
32062306a36Sopenharmony_ci	struct {
32162306a36Sopenharmony_ci		u8 enabled;
32262306a36Sopenharmony_ci		u8 key[sizeof(struct ieee80211_key_conf) + MAX_WEP_KEY_LEN];
32362306a36Sopenharmony_ci	} wep_key_conf[NUM_WEP_KEYS];
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/* BSSID */
32662306a36Sopenharmony_ci	u8 bssid[ETH_ALEN];
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* A flag to indicate is HW crypto is enabled for this bssid */
32962306a36Sopenharmony_ci	bool is_hw_crypto_enabled;
33062306a36Sopenharmony_ci};
33162306a36Sopenharmony_ci#define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv))
33262306a36Sopenharmony_ci#define IEEE80211_KEY_CONF(_u8) ((struct ieee80211_key_conf *)(_u8))
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistruct tx_traffic_info {
33562306a36Sopenharmony_ci	u32 start_time;
33662306a36Sopenharmony_ci	u32 pkts;
33762306a36Sopenharmony_ci};
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci#define MWL8K_MAX_TID 8
34062306a36Sopenharmony_cistruct mwl8k_sta {
34162306a36Sopenharmony_ci	/* Index into station database. Returned by UPDATE_STADB.  */
34262306a36Sopenharmony_ci	u8 peer_id;
34362306a36Sopenharmony_ci	u8 is_ampdu_allowed;
34462306a36Sopenharmony_ci	struct tx_traffic_info tx_stats[MWL8K_MAX_TID];
34562306a36Sopenharmony_ci};
34662306a36Sopenharmony_ci#define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv))
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic const struct ieee80211_channel mwl8k_channels_24[] = {
34962306a36Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2412, .hw_value = 1, },
35062306a36Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2417, .hw_value = 2, },
35162306a36Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2422, .hw_value = 3, },
35262306a36Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2427, .hw_value = 4, },
35362306a36Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2432, .hw_value = 5, },
35462306a36Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2437, .hw_value = 6, },
35562306a36Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2442, .hw_value = 7, },
35662306a36Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2447, .hw_value = 8, },
35762306a36Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2452, .hw_value = 9, },
35862306a36Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2457, .hw_value = 10, },
35962306a36Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2462, .hw_value = 11, },
36062306a36Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2467, .hw_value = 12, },
36162306a36Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2472, .hw_value = 13, },
36262306a36Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2484, .hw_value = 14, },
36362306a36Sopenharmony_ci};
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic const struct ieee80211_rate mwl8k_rates_24[] = {
36662306a36Sopenharmony_ci	{ .bitrate = 10, .hw_value = 2, },
36762306a36Sopenharmony_ci	{ .bitrate = 20, .hw_value = 4, },
36862306a36Sopenharmony_ci	{ .bitrate = 55, .hw_value = 11, },
36962306a36Sopenharmony_ci	{ .bitrate = 110, .hw_value = 22, },
37062306a36Sopenharmony_ci	{ .bitrate = 220, .hw_value = 44, },
37162306a36Sopenharmony_ci	{ .bitrate = 60, .hw_value = 12, },
37262306a36Sopenharmony_ci	{ .bitrate = 90, .hw_value = 18, },
37362306a36Sopenharmony_ci	{ .bitrate = 120, .hw_value = 24, },
37462306a36Sopenharmony_ci	{ .bitrate = 180, .hw_value = 36, },
37562306a36Sopenharmony_ci	{ .bitrate = 240, .hw_value = 48, },
37662306a36Sopenharmony_ci	{ .bitrate = 360, .hw_value = 72, },
37762306a36Sopenharmony_ci	{ .bitrate = 480, .hw_value = 96, },
37862306a36Sopenharmony_ci	{ .bitrate = 540, .hw_value = 108, },
37962306a36Sopenharmony_ci};
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic const struct ieee80211_channel mwl8k_channels_50[] = {
38262306a36Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5180, .hw_value = 36, },
38362306a36Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5200, .hw_value = 40, },
38462306a36Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5220, .hw_value = 44, },
38562306a36Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5240, .hw_value = 48, },
38662306a36Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5745, .hw_value = 149, },
38762306a36Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5765, .hw_value = 153, },
38862306a36Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5785, .hw_value = 157, },
38962306a36Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5805, .hw_value = 161, },
39062306a36Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5825, .hw_value = 165, },
39162306a36Sopenharmony_ci};
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic const struct ieee80211_rate mwl8k_rates_50[] = {
39462306a36Sopenharmony_ci	{ .bitrate = 60, .hw_value = 12, },
39562306a36Sopenharmony_ci	{ .bitrate = 90, .hw_value = 18, },
39662306a36Sopenharmony_ci	{ .bitrate = 120, .hw_value = 24, },
39762306a36Sopenharmony_ci	{ .bitrate = 180, .hw_value = 36, },
39862306a36Sopenharmony_ci	{ .bitrate = 240, .hw_value = 48, },
39962306a36Sopenharmony_ci	{ .bitrate = 360, .hw_value = 72, },
40062306a36Sopenharmony_ci	{ .bitrate = 480, .hw_value = 96, },
40162306a36Sopenharmony_ci	{ .bitrate = 540, .hw_value = 108, },
40262306a36Sopenharmony_ci};
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci/* Set or get info from Firmware */
40562306a36Sopenharmony_ci#define MWL8K_CMD_GET			0x0000
40662306a36Sopenharmony_ci#define MWL8K_CMD_SET			0x0001
40762306a36Sopenharmony_ci#define MWL8K_CMD_SET_LIST		0x0002
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci/* Firmware command codes */
41062306a36Sopenharmony_ci#define MWL8K_CMD_CODE_DNLD		0x0001
41162306a36Sopenharmony_ci#define MWL8K_CMD_GET_HW_SPEC		0x0003
41262306a36Sopenharmony_ci#define MWL8K_CMD_SET_HW_SPEC		0x0004
41362306a36Sopenharmony_ci#define MWL8K_CMD_MAC_MULTICAST_ADR	0x0010
41462306a36Sopenharmony_ci#define MWL8K_CMD_GET_STAT		0x0014
41562306a36Sopenharmony_ci#define MWL8K_CMD_BBP_REG_ACCESS	0x001a
41662306a36Sopenharmony_ci#define MWL8K_CMD_RADIO_CONTROL		0x001c
41762306a36Sopenharmony_ci#define MWL8K_CMD_RF_TX_POWER		0x001e
41862306a36Sopenharmony_ci#define MWL8K_CMD_TX_POWER		0x001f
41962306a36Sopenharmony_ci#define MWL8K_CMD_RF_ANTENNA		0x0020
42062306a36Sopenharmony_ci#define MWL8K_CMD_SET_BEACON		0x0100		/* per-vif */
42162306a36Sopenharmony_ci#define MWL8K_CMD_SET_PRE_SCAN		0x0107
42262306a36Sopenharmony_ci#define MWL8K_CMD_SET_POST_SCAN		0x0108
42362306a36Sopenharmony_ci#define MWL8K_CMD_SET_RF_CHANNEL	0x010a
42462306a36Sopenharmony_ci#define MWL8K_CMD_SET_AID		0x010d
42562306a36Sopenharmony_ci#define MWL8K_CMD_SET_RATE		0x0110
42662306a36Sopenharmony_ci#define MWL8K_CMD_SET_FINALIZE_JOIN	0x0111
42762306a36Sopenharmony_ci#define MWL8K_CMD_RTS_THRESHOLD		0x0113
42862306a36Sopenharmony_ci#define MWL8K_CMD_SET_SLOT		0x0114
42962306a36Sopenharmony_ci#define MWL8K_CMD_SET_EDCA_PARAMS	0x0115
43062306a36Sopenharmony_ci#define MWL8K_CMD_SET_WMM_MODE		0x0123
43162306a36Sopenharmony_ci#define MWL8K_CMD_MIMO_CONFIG		0x0125
43262306a36Sopenharmony_ci#define MWL8K_CMD_USE_FIXED_RATE	0x0126
43362306a36Sopenharmony_ci#define MWL8K_CMD_ENABLE_SNIFFER	0x0150
43462306a36Sopenharmony_ci#define MWL8K_CMD_SET_MAC_ADDR		0x0202		/* per-vif */
43562306a36Sopenharmony_ci#define MWL8K_CMD_SET_RATEADAPT_MODE	0x0203
43662306a36Sopenharmony_ci#define MWL8K_CMD_GET_WATCHDOG_BITMAP	0x0205
43762306a36Sopenharmony_ci#define MWL8K_CMD_DEL_MAC_ADDR		0x0206		/* per-vif */
43862306a36Sopenharmony_ci#define MWL8K_CMD_BSS_START		0x1100		/* per-vif */
43962306a36Sopenharmony_ci#define MWL8K_CMD_SET_NEW_STN		0x1111		/* per-vif */
44062306a36Sopenharmony_ci#define MWL8K_CMD_UPDATE_ENCRYPTION	0x1122		/* per-vif */
44162306a36Sopenharmony_ci#define MWL8K_CMD_UPDATE_STADB		0x1123
44262306a36Sopenharmony_ci#define MWL8K_CMD_BASTREAM		0x1125
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci#define MWL8K_LEGACY_5G_RATE_OFFSET \
44562306a36Sopenharmony_ci	(ARRAY_SIZE(mwl8k_rates_24) - ARRAY_SIZE(mwl8k_rates_50))
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	u16 command = le16_to_cpu(cmd);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci#define MWL8K_CMDNAME(x)	case MWL8K_CMD_##x: do {\
45262306a36Sopenharmony_ci					snprintf(buf, bufsize, "%s", #x);\
45362306a36Sopenharmony_ci					return buf;\
45462306a36Sopenharmony_ci					} while (0)
45562306a36Sopenharmony_ci	switch (command & ~0x8000) {
45662306a36Sopenharmony_ci		MWL8K_CMDNAME(CODE_DNLD);
45762306a36Sopenharmony_ci		MWL8K_CMDNAME(GET_HW_SPEC);
45862306a36Sopenharmony_ci		MWL8K_CMDNAME(SET_HW_SPEC);
45962306a36Sopenharmony_ci		MWL8K_CMDNAME(MAC_MULTICAST_ADR);
46062306a36Sopenharmony_ci		MWL8K_CMDNAME(GET_STAT);
46162306a36Sopenharmony_ci		MWL8K_CMDNAME(RADIO_CONTROL);
46262306a36Sopenharmony_ci		MWL8K_CMDNAME(RF_TX_POWER);
46362306a36Sopenharmony_ci		MWL8K_CMDNAME(TX_POWER);
46462306a36Sopenharmony_ci		MWL8K_CMDNAME(RF_ANTENNA);
46562306a36Sopenharmony_ci		MWL8K_CMDNAME(SET_BEACON);
46662306a36Sopenharmony_ci		MWL8K_CMDNAME(SET_PRE_SCAN);
46762306a36Sopenharmony_ci		MWL8K_CMDNAME(SET_POST_SCAN);
46862306a36Sopenharmony_ci		MWL8K_CMDNAME(SET_RF_CHANNEL);
46962306a36Sopenharmony_ci		MWL8K_CMDNAME(SET_AID);
47062306a36Sopenharmony_ci		MWL8K_CMDNAME(SET_RATE);
47162306a36Sopenharmony_ci		MWL8K_CMDNAME(SET_FINALIZE_JOIN);
47262306a36Sopenharmony_ci		MWL8K_CMDNAME(RTS_THRESHOLD);
47362306a36Sopenharmony_ci		MWL8K_CMDNAME(SET_SLOT);
47462306a36Sopenharmony_ci		MWL8K_CMDNAME(SET_EDCA_PARAMS);
47562306a36Sopenharmony_ci		MWL8K_CMDNAME(SET_WMM_MODE);
47662306a36Sopenharmony_ci		MWL8K_CMDNAME(MIMO_CONFIG);
47762306a36Sopenharmony_ci		MWL8K_CMDNAME(USE_FIXED_RATE);
47862306a36Sopenharmony_ci		MWL8K_CMDNAME(ENABLE_SNIFFER);
47962306a36Sopenharmony_ci		MWL8K_CMDNAME(SET_MAC_ADDR);
48062306a36Sopenharmony_ci		MWL8K_CMDNAME(SET_RATEADAPT_MODE);
48162306a36Sopenharmony_ci		MWL8K_CMDNAME(BSS_START);
48262306a36Sopenharmony_ci		MWL8K_CMDNAME(SET_NEW_STN);
48362306a36Sopenharmony_ci		MWL8K_CMDNAME(UPDATE_ENCRYPTION);
48462306a36Sopenharmony_ci		MWL8K_CMDNAME(UPDATE_STADB);
48562306a36Sopenharmony_ci		MWL8K_CMDNAME(BASTREAM);
48662306a36Sopenharmony_ci		MWL8K_CMDNAME(GET_WATCHDOG_BITMAP);
48762306a36Sopenharmony_ci	default:
48862306a36Sopenharmony_ci		snprintf(buf, bufsize, "0x%x", cmd);
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci#undef MWL8K_CMDNAME
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	return buf;
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci/* Hardware and firmware reset */
49662306a36Sopenharmony_cistatic void mwl8k_hw_reset(struct mwl8k_priv *priv)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_RESET,
49962306a36Sopenharmony_ci		priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
50062306a36Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_RESET,
50162306a36Sopenharmony_ci		priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
50262306a36Sopenharmony_ci	msleep(20);
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci/* Release fw image */
50662306a36Sopenharmony_cistatic void mwl8k_release_fw(const struct firmware **fw)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	if (*fw == NULL)
50962306a36Sopenharmony_ci		return;
51062306a36Sopenharmony_ci	release_firmware(*fw);
51162306a36Sopenharmony_ci	*fw = NULL;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic void mwl8k_release_firmware(struct mwl8k_priv *priv)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	mwl8k_release_fw(&priv->fw_ucode);
51762306a36Sopenharmony_ci	mwl8k_release_fw(&priv->fw_helper);
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci/* states for asynchronous f/w loading */
52162306a36Sopenharmony_cistatic void mwl8k_fw_state_machine(const struct firmware *fw, void *context);
52262306a36Sopenharmony_cienum {
52362306a36Sopenharmony_ci	FW_STATE_INIT = 0,
52462306a36Sopenharmony_ci	FW_STATE_LOADING_PREF,
52562306a36Sopenharmony_ci	FW_STATE_LOADING_ALT,
52662306a36Sopenharmony_ci	FW_STATE_ERROR,
52762306a36Sopenharmony_ci};
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci/* Request fw image */
53062306a36Sopenharmony_cistatic int mwl8k_request_fw(struct mwl8k_priv *priv,
53162306a36Sopenharmony_ci			    const char *fname, const struct firmware **fw,
53262306a36Sopenharmony_ci			    bool nowait)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	/* release current image */
53562306a36Sopenharmony_ci	if (*fw != NULL)
53662306a36Sopenharmony_ci		mwl8k_release_fw(fw);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	if (nowait)
53962306a36Sopenharmony_ci		return request_firmware_nowait(THIS_MODULE, 1, fname,
54062306a36Sopenharmony_ci					       &priv->pdev->dev, GFP_KERNEL,
54162306a36Sopenharmony_ci					       priv, mwl8k_fw_state_machine);
54262306a36Sopenharmony_ci	else
54362306a36Sopenharmony_ci		return request_firmware(fw, fname, &priv->pdev->dev);
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic int mwl8k_request_firmware(struct mwl8k_priv *priv, char *fw_image,
54762306a36Sopenharmony_ci				  bool nowait)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct mwl8k_device_info *di = priv->device_info;
55062306a36Sopenharmony_ci	int rc;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (di->helper_image != NULL) {
55362306a36Sopenharmony_ci		if (nowait)
55462306a36Sopenharmony_ci			rc = mwl8k_request_fw(priv, di->helper_image,
55562306a36Sopenharmony_ci					      &priv->fw_helper, true);
55662306a36Sopenharmony_ci		else
55762306a36Sopenharmony_ci			rc = mwl8k_request_fw(priv, di->helper_image,
55862306a36Sopenharmony_ci					      &priv->fw_helper, false);
55962306a36Sopenharmony_ci		if (rc)
56062306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error requesting helper fw %s\n",
56162306a36Sopenharmony_ci			       pci_name(priv->pdev), di->helper_image);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		if (rc || nowait)
56462306a36Sopenharmony_ci			return rc;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	if (nowait) {
56862306a36Sopenharmony_ci		/*
56962306a36Sopenharmony_ci		 * if we get here, no helper image is needed.  Skip the
57062306a36Sopenharmony_ci		 * FW_STATE_INIT state.
57162306a36Sopenharmony_ci		 */
57262306a36Sopenharmony_ci		priv->fw_state = FW_STATE_LOADING_PREF;
57362306a36Sopenharmony_ci		rc = mwl8k_request_fw(priv, fw_image,
57462306a36Sopenharmony_ci				      &priv->fw_ucode,
57562306a36Sopenharmony_ci				      true);
57662306a36Sopenharmony_ci	} else
57762306a36Sopenharmony_ci		rc = mwl8k_request_fw(priv, fw_image,
57862306a36Sopenharmony_ci				      &priv->fw_ucode, false);
57962306a36Sopenharmony_ci	if (rc) {
58062306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error requesting firmware file %s\n",
58162306a36Sopenharmony_ci		       pci_name(priv->pdev), fw_image);
58262306a36Sopenharmony_ci		mwl8k_release_fw(&priv->fw_helper);
58362306a36Sopenharmony_ci		return rc;
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	return 0;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistruct mwl8k_cmd_pkt {
59062306a36Sopenharmony_ci	__le16	code;
59162306a36Sopenharmony_ci	__le16	length;
59262306a36Sopenharmony_ci	__u8	seq_num;
59362306a36Sopenharmony_ci	__u8	macid;
59462306a36Sopenharmony_ci	__le16	result;
59562306a36Sopenharmony_ci	char	payload[];
59662306a36Sopenharmony_ci} __packed;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci/*
59962306a36Sopenharmony_ci * Firmware loading.
60062306a36Sopenharmony_ci */
60162306a36Sopenharmony_cistatic int
60262306a36Sopenharmony_cimwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	void __iomem *regs = priv->regs;
60562306a36Sopenharmony_ci	dma_addr_t dma_addr;
60662306a36Sopenharmony_ci	int loops;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	dma_addr = dma_map_single(&priv->pdev->dev, data, length,
60962306a36Sopenharmony_ci				  DMA_TO_DEVICE);
61062306a36Sopenharmony_ci	if (dma_mapping_error(&priv->pdev->dev, dma_addr))
61162306a36Sopenharmony_ci		return -ENOMEM;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR);
61462306a36Sopenharmony_ci	iowrite32(0, regs + MWL8K_HIU_INT_CODE);
61562306a36Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_DOORBELL,
61662306a36Sopenharmony_ci		regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
61762306a36Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_DUMMY,
61862306a36Sopenharmony_ci		regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	loops = 1000;
62162306a36Sopenharmony_ci	do {
62262306a36Sopenharmony_ci		u32 int_code;
62362306a36Sopenharmony_ci		if (priv->is_8764) {
62462306a36Sopenharmony_ci			int_code = ioread32(regs +
62562306a36Sopenharmony_ci					    MWL8K_HIU_H2A_INTERRUPT_STATUS);
62662306a36Sopenharmony_ci			if (int_code == 0)
62762306a36Sopenharmony_ci				break;
62862306a36Sopenharmony_ci		} else {
62962306a36Sopenharmony_ci			int_code = ioread32(regs + MWL8K_HIU_INT_CODE);
63062306a36Sopenharmony_ci			if (int_code == MWL8K_INT_CODE_CMD_FINISHED) {
63162306a36Sopenharmony_ci				iowrite32(0, regs + MWL8K_HIU_INT_CODE);
63262306a36Sopenharmony_ci				break;
63362306a36Sopenharmony_ci			}
63462306a36Sopenharmony_ci		}
63562306a36Sopenharmony_ci		cond_resched();
63662306a36Sopenharmony_ci		udelay(1);
63762306a36Sopenharmony_ci	} while (--loops);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	dma_unmap_single(&priv->pdev->dev, dma_addr, length, DMA_TO_DEVICE);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return loops ? 0 : -ETIMEDOUT;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic int mwl8k_load_fw_image(struct mwl8k_priv *priv,
64562306a36Sopenharmony_ci				const u8 *data, size_t length)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	struct mwl8k_cmd_pkt *cmd;
64862306a36Sopenharmony_ci	int done;
64962306a36Sopenharmony_ci	int rc = 0;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	cmd = kmalloc(sizeof(*cmd) + 256, GFP_KERNEL);
65262306a36Sopenharmony_ci	if (cmd == NULL)
65362306a36Sopenharmony_ci		return -ENOMEM;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	cmd->code = cpu_to_le16(MWL8K_CMD_CODE_DNLD);
65662306a36Sopenharmony_ci	cmd->seq_num = 0;
65762306a36Sopenharmony_ci	cmd->macid = 0;
65862306a36Sopenharmony_ci	cmd->result = 0;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	done = 0;
66162306a36Sopenharmony_ci	while (length) {
66262306a36Sopenharmony_ci		int block_size = length > 256 ? 256 : length;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		memcpy(cmd->payload, data + done, block_size);
66562306a36Sopenharmony_ci		cmd->length = cpu_to_le16(block_size);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci		rc = mwl8k_send_fw_load_cmd(priv, cmd,
66862306a36Sopenharmony_ci						sizeof(*cmd) + block_size);
66962306a36Sopenharmony_ci		if (rc)
67062306a36Sopenharmony_ci			break;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci		done += block_size;
67362306a36Sopenharmony_ci		length -= block_size;
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	if (!rc) {
67762306a36Sopenharmony_ci		cmd->length = 0;
67862306a36Sopenharmony_ci		rc = mwl8k_send_fw_load_cmd(priv, cmd, sizeof(*cmd));
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	kfree(cmd);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	return rc;
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic int mwl8k_feed_fw_image(struct mwl8k_priv *priv,
68762306a36Sopenharmony_ci				const u8 *data, size_t length)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	unsigned char *buffer;
69062306a36Sopenharmony_ci	int may_continue, rc = 0;
69162306a36Sopenharmony_ci	u32 done, prev_block_size;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	buffer = kmalloc(1024, GFP_KERNEL);
69462306a36Sopenharmony_ci	if (buffer == NULL)
69562306a36Sopenharmony_ci		return -ENOMEM;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	done = 0;
69862306a36Sopenharmony_ci	prev_block_size = 0;
69962306a36Sopenharmony_ci	may_continue = 1000;
70062306a36Sopenharmony_ci	while (may_continue > 0) {
70162306a36Sopenharmony_ci		u32 block_size;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci		block_size = ioread32(priv->regs + MWL8K_HIU_SCRATCH);
70462306a36Sopenharmony_ci		if (block_size & 1) {
70562306a36Sopenharmony_ci			block_size &= ~1;
70662306a36Sopenharmony_ci			may_continue--;
70762306a36Sopenharmony_ci		} else {
70862306a36Sopenharmony_ci			done += prev_block_size;
70962306a36Sopenharmony_ci			length -= prev_block_size;
71062306a36Sopenharmony_ci		}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci		if (block_size > 1024 || block_size > length) {
71362306a36Sopenharmony_ci			rc = -EOVERFLOW;
71462306a36Sopenharmony_ci			break;
71562306a36Sopenharmony_ci		}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci		if (length == 0) {
71862306a36Sopenharmony_ci			rc = 0;
71962306a36Sopenharmony_ci			break;
72062306a36Sopenharmony_ci		}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci		if (block_size == 0) {
72362306a36Sopenharmony_ci			rc = -EPROTO;
72462306a36Sopenharmony_ci			may_continue--;
72562306a36Sopenharmony_ci			udelay(1);
72662306a36Sopenharmony_ci			continue;
72762306a36Sopenharmony_ci		}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci		prev_block_size = block_size;
73062306a36Sopenharmony_ci		memcpy(buffer, data + done, block_size);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci		rc = mwl8k_send_fw_load_cmd(priv, buffer, block_size);
73362306a36Sopenharmony_ci		if (rc)
73462306a36Sopenharmony_ci			break;
73562306a36Sopenharmony_ci	}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	if (!rc && length != 0)
73862306a36Sopenharmony_ci		rc = -EREMOTEIO;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	kfree(buffer);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	return rc;
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_cistatic int mwl8k_load_firmware(struct ieee80211_hw *hw)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
74862306a36Sopenharmony_ci	const struct firmware *fw = priv->fw_ucode;
74962306a36Sopenharmony_ci	int rc;
75062306a36Sopenharmony_ci	int loops;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	if (!memcmp(fw->data, "\x01\x00\x00\x00", 4) && !priv->is_8764) {
75362306a36Sopenharmony_ci		const struct firmware *helper = priv->fw_helper;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci		if (helper == NULL) {
75662306a36Sopenharmony_ci			printk(KERN_ERR "%s: helper image needed but none "
75762306a36Sopenharmony_ci			       "given\n", pci_name(priv->pdev));
75862306a36Sopenharmony_ci			return -EINVAL;
75962306a36Sopenharmony_ci		}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci		rc = mwl8k_load_fw_image(priv, helper->data, helper->size);
76262306a36Sopenharmony_ci		if (rc) {
76362306a36Sopenharmony_ci			printk(KERN_ERR "%s: unable to load firmware "
76462306a36Sopenharmony_ci			       "helper image\n", pci_name(priv->pdev));
76562306a36Sopenharmony_ci			return rc;
76662306a36Sopenharmony_ci		}
76762306a36Sopenharmony_ci		msleep(20);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci		rc = mwl8k_feed_fw_image(priv, fw->data, fw->size);
77062306a36Sopenharmony_ci	} else {
77162306a36Sopenharmony_ci		if (priv->is_8764)
77262306a36Sopenharmony_ci			rc = mwl8k_feed_fw_image(priv, fw->data, fw->size);
77362306a36Sopenharmony_ci		else
77462306a36Sopenharmony_ci			rc = mwl8k_load_fw_image(priv, fw->data, fw->size);
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	if (rc) {
77862306a36Sopenharmony_ci		printk(KERN_ERR "%s: unable to load firmware image\n",
77962306a36Sopenharmony_ci		       pci_name(priv->pdev));
78062306a36Sopenharmony_ci		return rc;
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	loops = 500000;
78662306a36Sopenharmony_ci	do {
78762306a36Sopenharmony_ci		u32 ready_code;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci		ready_code = ioread32(priv->regs + MWL8K_HIU_INT_CODE);
79062306a36Sopenharmony_ci		if (ready_code == MWL8K_FWAP_READY) {
79162306a36Sopenharmony_ci			priv->ap_fw = true;
79262306a36Sopenharmony_ci			break;
79362306a36Sopenharmony_ci		} else if (ready_code == MWL8K_FWSTA_READY) {
79462306a36Sopenharmony_ci			priv->ap_fw = false;
79562306a36Sopenharmony_ci			break;
79662306a36Sopenharmony_ci		}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci		cond_resched();
79962306a36Sopenharmony_ci		udelay(1);
80062306a36Sopenharmony_ci	} while (--loops);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	return loops ? 0 : -ETIMEDOUT;
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci/* DMA header used by firmware and hardware.  */
80762306a36Sopenharmony_cistruct mwl8k_dma_data {
80862306a36Sopenharmony_ci	__le16 fwlen;
80962306a36Sopenharmony_ci	struct ieee80211_hdr wh;
81062306a36Sopenharmony_ci	char data[];
81162306a36Sopenharmony_ci} __packed __aligned(2);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci/* Routines to add/remove DMA header from skb.  */
81462306a36Sopenharmony_cistatic inline void mwl8k_remove_dma_header(struct sk_buff *skb, __le16 qos)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	struct mwl8k_dma_data *tr;
81762306a36Sopenharmony_ci	int hdrlen;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	tr = (struct mwl8k_dma_data *)skb->data;
82062306a36Sopenharmony_ci	hdrlen = ieee80211_hdrlen(tr->wh.frame_control);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	if (hdrlen != sizeof(tr->wh)) {
82362306a36Sopenharmony_ci		if (ieee80211_is_data_qos(tr->wh.frame_control)) {
82462306a36Sopenharmony_ci			memmove(tr->data - hdrlen, &tr->wh, hdrlen - 2);
82562306a36Sopenharmony_ci			*((__le16 *)(tr->data - 2)) = qos;
82662306a36Sopenharmony_ci		} else {
82762306a36Sopenharmony_ci			memmove(tr->data - hdrlen, &tr->wh, hdrlen);
82862306a36Sopenharmony_ci		}
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	if (hdrlen != sizeof(*tr))
83262306a36Sopenharmony_ci		skb_pull(skb, sizeof(*tr) - hdrlen);
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci#define REDUCED_TX_HEADROOM	8
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_cistatic void
83862306a36Sopenharmony_cimwl8k_add_dma_header(struct mwl8k_priv *priv, struct sk_buff *skb,
83962306a36Sopenharmony_ci						int head_pad, int tail_pad)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	struct ieee80211_hdr *wh;
84262306a36Sopenharmony_ci	int hdrlen;
84362306a36Sopenharmony_ci	int reqd_hdrlen;
84462306a36Sopenharmony_ci	struct mwl8k_dma_data *tr;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	/*
84762306a36Sopenharmony_ci	 * Add a firmware DMA header; the firmware requires that we
84862306a36Sopenharmony_ci	 * present a 2-byte payload length followed by a 4-address
84962306a36Sopenharmony_ci	 * header (without QoS field), followed (optionally) by any
85062306a36Sopenharmony_ci	 * WEP/ExtIV header (but only filled in for CCMP).
85162306a36Sopenharmony_ci	 */
85262306a36Sopenharmony_ci	wh = (struct ieee80211_hdr *)skb->data;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	hdrlen = ieee80211_hdrlen(wh->frame_control);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	/*
85762306a36Sopenharmony_ci	 * Check if skb_resize is required because of
85862306a36Sopenharmony_ci	 * tx_headroom adjustment.
85962306a36Sopenharmony_ci	 */
86062306a36Sopenharmony_ci	if (priv->ap_fw && (hdrlen < (sizeof(struct ieee80211_cts)
86162306a36Sopenharmony_ci						+ REDUCED_TX_HEADROOM))) {
86262306a36Sopenharmony_ci		if (pskb_expand_head(skb, REDUCED_TX_HEADROOM, 0, GFP_ATOMIC)) {
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci			wiphy_err(priv->hw->wiphy,
86562306a36Sopenharmony_ci					"Failed to reallocate TX buffer\n");
86662306a36Sopenharmony_ci			return;
86762306a36Sopenharmony_ci		}
86862306a36Sopenharmony_ci		skb->truesize += REDUCED_TX_HEADROOM;
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	reqd_hdrlen = sizeof(*tr) + head_pad;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	if (hdrlen != reqd_hdrlen)
87462306a36Sopenharmony_ci		skb_push(skb, reqd_hdrlen - hdrlen);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	if (ieee80211_is_data_qos(wh->frame_control))
87762306a36Sopenharmony_ci		hdrlen -= IEEE80211_QOS_CTL_LEN;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	tr = (struct mwl8k_dma_data *)skb->data;
88062306a36Sopenharmony_ci	if (wh != &tr->wh)
88162306a36Sopenharmony_ci		memmove(&tr->wh, wh, hdrlen);
88262306a36Sopenharmony_ci	if (hdrlen != sizeof(tr->wh))
88362306a36Sopenharmony_ci		memset(((void *)&tr->wh) + hdrlen, 0, sizeof(tr->wh) - hdrlen);
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	/*
88662306a36Sopenharmony_ci	 * Firmware length is the length of the fully formed "802.11
88762306a36Sopenharmony_ci	 * payload".  That is, everything except for the 802.11 header.
88862306a36Sopenharmony_ci	 * This includes all crypto material including the MIC.
88962306a36Sopenharmony_ci	 */
89062306a36Sopenharmony_ci	tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr) + tail_pad);
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_cistatic void mwl8k_encapsulate_tx_frame(struct mwl8k_priv *priv,
89462306a36Sopenharmony_ci		struct sk_buff *skb)
89562306a36Sopenharmony_ci{
89662306a36Sopenharmony_ci	struct ieee80211_hdr *wh;
89762306a36Sopenharmony_ci	struct ieee80211_tx_info *tx_info;
89862306a36Sopenharmony_ci	struct ieee80211_key_conf *key_conf;
89962306a36Sopenharmony_ci	int data_pad;
90062306a36Sopenharmony_ci	int head_pad = 0;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	wh = (struct ieee80211_hdr *)skb->data;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	tx_info = IEEE80211_SKB_CB(skb);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	key_conf = NULL;
90762306a36Sopenharmony_ci	if (ieee80211_is_data(wh->frame_control))
90862306a36Sopenharmony_ci		key_conf = tx_info->control.hw_key;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	/*
91162306a36Sopenharmony_ci	 * Make sure the packet header is in the DMA header format (4-address
91262306a36Sopenharmony_ci	 * without QoS), and add head & tail padding when HW crypto is enabled.
91362306a36Sopenharmony_ci	 *
91462306a36Sopenharmony_ci	 * We have the following trailer padding requirements:
91562306a36Sopenharmony_ci	 * - WEP: 4 trailer bytes (ICV)
91662306a36Sopenharmony_ci	 * - TKIP: 12 trailer bytes (8 MIC + 4 ICV)
91762306a36Sopenharmony_ci	 * - CCMP: 8 trailer bytes (MIC)
91862306a36Sopenharmony_ci	 */
91962306a36Sopenharmony_ci	data_pad = 0;
92062306a36Sopenharmony_ci	if (key_conf != NULL) {
92162306a36Sopenharmony_ci		head_pad = key_conf->iv_len;
92262306a36Sopenharmony_ci		switch (key_conf->cipher) {
92362306a36Sopenharmony_ci		case WLAN_CIPHER_SUITE_WEP40:
92462306a36Sopenharmony_ci		case WLAN_CIPHER_SUITE_WEP104:
92562306a36Sopenharmony_ci			data_pad = 4;
92662306a36Sopenharmony_ci			break;
92762306a36Sopenharmony_ci		case WLAN_CIPHER_SUITE_TKIP:
92862306a36Sopenharmony_ci			data_pad = 12;
92962306a36Sopenharmony_ci			break;
93062306a36Sopenharmony_ci		case WLAN_CIPHER_SUITE_CCMP:
93162306a36Sopenharmony_ci			data_pad = 8;
93262306a36Sopenharmony_ci			break;
93362306a36Sopenharmony_ci		}
93462306a36Sopenharmony_ci	}
93562306a36Sopenharmony_ci	mwl8k_add_dma_header(priv, skb, head_pad, data_pad);
93662306a36Sopenharmony_ci}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci/*
93962306a36Sopenharmony_ci * Packet reception for 88w8366/88w8764 AP firmware.
94062306a36Sopenharmony_ci */
94162306a36Sopenharmony_cistruct mwl8k_rxd_ap {
94262306a36Sopenharmony_ci	__le16 pkt_len;
94362306a36Sopenharmony_ci	__u8 sq2;
94462306a36Sopenharmony_ci	__u8 rate;
94562306a36Sopenharmony_ci	__le32 pkt_phys_addr;
94662306a36Sopenharmony_ci	__le32 next_rxd_phys_addr;
94762306a36Sopenharmony_ci	__le16 qos_control;
94862306a36Sopenharmony_ci	__le16 htsig2;
94962306a36Sopenharmony_ci	__le32 hw_rssi_info;
95062306a36Sopenharmony_ci	__le32 hw_noise_floor_info;
95162306a36Sopenharmony_ci	__u8 noise_floor;
95262306a36Sopenharmony_ci	__u8 pad0[3];
95362306a36Sopenharmony_ci	__u8 rssi;
95462306a36Sopenharmony_ci	__u8 rx_status;
95562306a36Sopenharmony_ci	__u8 channel;
95662306a36Sopenharmony_ci	__u8 rx_ctrl;
95762306a36Sopenharmony_ci} __packed;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci#define MWL8K_AP_RATE_INFO_MCS_FORMAT		0x80
96062306a36Sopenharmony_ci#define MWL8K_AP_RATE_INFO_40MHZ		0x40
96162306a36Sopenharmony_ci#define MWL8K_AP_RATE_INFO_RATEID(x)		((x) & 0x3f)
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci#define MWL8K_AP_RX_CTRL_OWNED_BY_HOST		0x80
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci/* 8366/8764 AP rx_status bits */
96662306a36Sopenharmony_ci#define MWL8K_AP_RXSTAT_DECRYPT_ERR_MASK		0x80
96762306a36Sopenharmony_ci#define MWL8K_AP_RXSTAT_GENERAL_DECRYPT_ERR		0xFF
96862306a36Sopenharmony_ci#define MWL8K_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR		0x02
96962306a36Sopenharmony_ci#define MWL8K_AP_RXSTAT_WEP_DECRYPT_ICV_ERR		0x04
97062306a36Sopenharmony_ci#define MWL8K_AP_RXSTAT_TKIP_DECRYPT_ICV_ERR		0x08
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_cistatic void mwl8k_rxd_ap_init(void *_rxd, dma_addr_t next_dma_addr)
97362306a36Sopenharmony_ci{
97462306a36Sopenharmony_ci	struct mwl8k_rxd_ap *rxd = _rxd;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr);
97762306a36Sopenharmony_ci	rxd->rx_ctrl = MWL8K_AP_RX_CTRL_OWNED_BY_HOST;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_cistatic void mwl8k_rxd_ap_refill(void *_rxd, dma_addr_t addr, int len)
98162306a36Sopenharmony_ci{
98262306a36Sopenharmony_ci	struct mwl8k_rxd_ap *rxd = _rxd;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	rxd->pkt_len = cpu_to_le16(len);
98562306a36Sopenharmony_ci	rxd->pkt_phys_addr = cpu_to_le32(addr);
98662306a36Sopenharmony_ci	wmb();
98762306a36Sopenharmony_ci	rxd->rx_ctrl = 0;
98862306a36Sopenharmony_ci}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_cistatic int
99162306a36Sopenharmony_cimwl8k_rxd_ap_process(void *_rxd, struct ieee80211_rx_status *status,
99262306a36Sopenharmony_ci		     __le16 *qos, s8 *noise)
99362306a36Sopenharmony_ci{
99462306a36Sopenharmony_ci	struct mwl8k_rxd_ap *rxd = _rxd;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (!(rxd->rx_ctrl & MWL8K_AP_RX_CTRL_OWNED_BY_HOST))
99762306a36Sopenharmony_ci		return -1;
99862306a36Sopenharmony_ci	rmb();
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	memset(status, 0, sizeof(*status));
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	status->signal = -rxd->rssi;
100362306a36Sopenharmony_ci	*noise = -rxd->noise_floor;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	if (rxd->rate & MWL8K_AP_RATE_INFO_MCS_FORMAT) {
100662306a36Sopenharmony_ci		status->encoding = RX_ENC_HT;
100762306a36Sopenharmony_ci		if (rxd->rate & MWL8K_AP_RATE_INFO_40MHZ)
100862306a36Sopenharmony_ci			status->bw = RATE_INFO_BW_40;
100962306a36Sopenharmony_ci		status->rate_idx = MWL8K_AP_RATE_INFO_RATEID(rxd->rate);
101062306a36Sopenharmony_ci	} else {
101162306a36Sopenharmony_ci		int i;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(mwl8k_rates_24); i++) {
101462306a36Sopenharmony_ci			if (mwl8k_rates_24[i].hw_value == rxd->rate) {
101562306a36Sopenharmony_ci				status->rate_idx = i;
101662306a36Sopenharmony_ci				break;
101762306a36Sopenharmony_ci			}
101862306a36Sopenharmony_ci		}
101962306a36Sopenharmony_ci	}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	if (rxd->channel > 14) {
102262306a36Sopenharmony_ci		status->band = NL80211_BAND_5GHZ;
102362306a36Sopenharmony_ci		if (!(status->encoding == RX_ENC_HT) &&
102462306a36Sopenharmony_ci		    status->rate_idx >= MWL8K_LEGACY_5G_RATE_OFFSET)
102562306a36Sopenharmony_ci			status->rate_idx -= MWL8K_LEGACY_5G_RATE_OFFSET;
102662306a36Sopenharmony_ci	} else {
102762306a36Sopenharmony_ci		status->band = NL80211_BAND_2GHZ;
102862306a36Sopenharmony_ci	}
102962306a36Sopenharmony_ci	status->freq = ieee80211_channel_to_frequency(rxd->channel,
103062306a36Sopenharmony_ci						      status->band);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	*qos = rxd->qos_control;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	if ((rxd->rx_status != MWL8K_AP_RXSTAT_GENERAL_DECRYPT_ERR) &&
103562306a36Sopenharmony_ci	    (rxd->rx_status & MWL8K_AP_RXSTAT_DECRYPT_ERR_MASK) &&
103662306a36Sopenharmony_ci	    (rxd->rx_status & MWL8K_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR))
103762306a36Sopenharmony_ci		status->flag |= RX_FLAG_MMIC_ERROR;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	return le16_to_cpu(rxd->pkt_len);
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_cistatic struct rxd_ops rxd_ap_ops = {
104362306a36Sopenharmony_ci	.rxd_size	= sizeof(struct mwl8k_rxd_ap),
104462306a36Sopenharmony_ci	.rxd_init	= mwl8k_rxd_ap_init,
104562306a36Sopenharmony_ci	.rxd_refill	= mwl8k_rxd_ap_refill,
104662306a36Sopenharmony_ci	.rxd_process	= mwl8k_rxd_ap_process,
104762306a36Sopenharmony_ci};
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci/*
105062306a36Sopenharmony_ci * Packet reception for STA firmware.
105162306a36Sopenharmony_ci */
105262306a36Sopenharmony_cistruct mwl8k_rxd_sta {
105362306a36Sopenharmony_ci	__le16 pkt_len;
105462306a36Sopenharmony_ci	__u8 link_quality;
105562306a36Sopenharmony_ci	__u8 noise_level;
105662306a36Sopenharmony_ci	__le32 pkt_phys_addr;
105762306a36Sopenharmony_ci	__le32 next_rxd_phys_addr;
105862306a36Sopenharmony_ci	__le16 qos_control;
105962306a36Sopenharmony_ci	__le16 rate_info;
106062306a36Sopenharmony_ci	__le32 pad0[4];
106162306a36Sopenharmony_ci	__u8 rssi;
106262306a36Sopenharmony_ci	__u8 channel;
106362306a36Sopenharmony_ci	__le16 pad1;
106462306a36Sopenharmony_ci	__u8 rx_ctrl;
106562306a36Sopenharmony_ci	__u8 rx_status;
106662306a36Sopenharmony_ci	__u8 pad2[2];
106762306a36Sopenharmony_ci} __packed;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci#define MWL8K_STA_RATE_INFO_SHORTPRE		0x8000
107062306a36Sopenharmony_ci#define MWL8K_STA_RATE_INFO_ANTSELECT(x)	(((x) >> 11) & 0x3)
107162306a36Sopenharmony_ci#define MWL8K_STA_RATE_INFO_RATEID(x)		(((x) >> 3) & 0x3f)
107262306a36Sopenharmony_ci#define MWL8K_STA_RATE_INFO_40MHZ		0x0004
107362306a36Sopenharmony_ci#define MWL8K_STA_RATE_INFO_SHORTGI		0x0002
107462306a36Sopenharmony_ci#define MWL8K_STA_RATE_INFO_MCS_FORMAT		0x0001
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci#define MWL8K_STA_RX_CTRL_OWNED_BY_HOST		0x02
107762306a36Sopenharmony_ci#define MWL8K_STA_RX_CTRL_DECRYPT_ERROR		0x04
107862306a36Sopenharmony_ci/* ICV=0 or MIC=1 */
107962306a36Sopenharmony_ci#define MWL8K_STA_RX_CTRL_DEC_ERR_TYPE		0x08
108062306a36Sopenharmony_ci/* Key is uploaded only in failure case */
108162306a36Sopenharmony_ci#define MWL8K_STA_RX_CTRL_KEY_INDEX			0x30
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_cistatic void mwl8k_rxd_sta_init(void *_rxd, dma_addr_t next_dma_addr)
108462306a36Sopenharmony_ci{
108562306a36Sopenharmony_ci	struct mwl8k_rxd_sta *rxd = _rxd;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr);
108862306a36Sopenharmony_ci	rxd->rx_ctrl = MWL8K_STA_RX_CTRL_OWNED_BY_HOST;
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_cistatic void mwl8k_rxd_sta_refill(void *_rxd, dma_addr_t addr, int len)
109262306a36Sopenharmony_ci{
109362306a36Sopenharmony_ci	struct mwl8k_rxd_sta *rxd = _rxd;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	rxd->pkt_len = cpu_to_le16(len);
109662306a36Sopenharmony_ci	rxd->pkt_phys_addr = cpu_to_le32(addr);
109762306a36Sopenharmony_ci	wmb();
109862306a36Sopenharmony_ci	rxd->rx_ctrl = 0;
109962306a36Sopenharmony_ci}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_cistatic int
110262306a36Sopenharmony_cimwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status,
110362306a36Sopenharmony_ci		       __le16 *qos, s8 *noise)
110462306a36Sopenharmony_ci{
110562306a36Sopenharmony_ci	struct mwl8k_rxd_sta *rxd = _rxd;
110662306a36Sopenharmony_ci	u16 rate_info;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	if (!(rxd->rx_ctrl & MWL8K_STA_RX_CTRL_OWNED_BY_HOST))
110962306a36Sopenharmony_ci		return -1;
111062306a36Sopenharmony_ci	rmb();
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	rate_info = le16_to_cpu(rxd->rate_info);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	memset(status, 0, sizeof(*status));
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	status->signal = -rxd->rssi;
111762306a36Sopenharmony_ci	*noise = -rxd->noise_level;
111862306a36Sopenharmony_ci	status->antenna = MWL8K_STA_RATE_INFO_ANTSELECT(rate_info);
111962306a36Sopenharmony_ci	status->rate_idx = MWL8K_STA_RATE_INFO_RATEID(rate_info);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	if (rate_info & MWL8K_STA_RATE_INFO_SHORTPRE)
112262306a36Sopenharmony_ci		status->enc_flags |= RX_ENC_FLAG_SHORTPRE;
112362306a36Sopenharmony_ci	if (rate_info & MWL8K_STA_RATE_INFO_40MHZ)
112462306a36Sopenharmony_ci		status->bw = RATE_INFO_BW_40;
112562306a36Sopenharmony_ci	if (rate_info & MWL8K_STA_RATE_INFO_SHORTGI)
112662306a36Sopenharmony_ci		status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
112762306a36Sopenharmony_ci	if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT)
112862306a36Sopenharmony_ci		status->encoding = RX_ENC_HT;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	if (rxd->channel > 14) {
113162306a36Sopenharmony_ci		status->band = NL80211_BAND_5GHZ;
113262306a36Sopenharmony_ci		if (!(status->encoding == RX_ENC_HT) &&
113362306a36Sopenharmony_ci		    status->rate_idx >= MWL8K_LEGACY_5G_RATE_OFFSET)
113462306a36Sopenharmony_ci			status->rate_idx -= MWL8K_LEGACY_5G_RATE_OFFSET;
113562306a36Sopenharmony_ci	} else {
113662306a36Sopenharmony_ci		status->band = NL80211_BAND_2GHZ;
113762306a36Sopenharmony_ci	}
113862306a36Sopenharmony_ci	status->freq = ieee80211_channel_to_frequency(rxd->channel,
113962306a36Sopenharmony_ci						      status->band);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	*qos = rxd->qos_control;
114262306a36Sopenharmony_ci	if ((rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DECRYPT_ERROR) &&
114362306a36Sopenharmony_ci	    (rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DEC_ERR_TYPE))
114462306a36Sopenharmony_ci		status->flag |= RX_FLAG_MMIC_ERROR;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	return le16_to_cpu(rxd->pkt_len);
114762306a36Sopenharmony_ci}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cistatic struct rxd_ops rxd_sta_ops = {
115062306a36Sopenharmony_ci	.rxd_size	= sizeof(struct mwl8k_rxd_sta),
115162306a36Sopenharmony_ci	.rxd_init	= mwl8k_rxd_sta_init,
115262306a36Sopenharmony_ci	.rxd_refill	= mwl8k_rxd_sta_refill,
115362306a36Sopenharmony_ci	.rxd_process	= mwl8k_rxd_sta_process,
115462306a36Sopenharmony_ci};
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci#define MWL8K_RX_DESCS		256
115862306a36Sopenharmony_ci#define MWL8K_RX_MAXSZ		3800
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_cistatic int mwl8k_rxq_init(struct ieee80211_hw *hw, int index)
116162306a36Sopenharmony_ci{
116262306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
116362306a36Sopenharmony_ci	struct mwl8k_rx_queue *rxq = priv->rxq + index;
116462306a36Sopenharmony_ci	int size;
116562306a36Sopenharmony_ci	int i;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	rxq->rxd_count = 0;
116862306a36Sopenharmony_ci	rxq->head = 0;
116962306a36Sopenharmony_ci	rxq->tail = 0;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	size = MWL8K_RX_DESCS * priv->rxd_ops->rxd_size;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	rxq->rxd = dma_alloc_coherent(&priv->pdev->dev, size, &rxq->rxd_dma,
117462306a36Sopenharmony_ci				      GFP_KERNEL);
117562306a36Sopenharmony_ci	if (rxq->rxd == NULL) {
117662306a36Sopenharmony_ci		wiphy_err(hw->wiphy, "failed to alloc RX descriptors\n");
117762306a36Sopenharmony_ci		return -ENOMEM;
117862306a36Sopenharmony_ci	}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	rxq->buf = kcalloc(MWL8K_RX_DESCS, sizeof(*rxq->buf), GFP_KERNEL);
118162306a36Sopenharmony_ci	if (rxq->buf == NULL) {
118262306a36Sopenharmony_ci		dma_free_coherent(&priv->pdev->dev, size, rxq->rxd,
118362306a36Sopenharmony_ci				  rxq->rxd_dma);
118462306a36Sopenharmony_ci		return -ENOMEM;
118562306a36Sopenharmony_ci	}
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	for (i = 0; i < MWL8K_RX_DESCS; i++) {
118862306a36Sopenharmony_ci		int desc_size;
118962306a36Sopenharmony_ci		void *rxd;
119062306a36Sopenharmony_ci		int nexti;
119162306a36Sopenharmony_ci		dma_addr_t next_dma_addr;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci		desc_size = priv->rxd_ops->rxd_size;
119462306a36Sopenharmony_ci		rxd = rxq->rxd + (i * priv->rxd_ops->rxd_size);
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci		nexti = i + 1;
119762306a36Sopenharmony_ci		if (nexti == MWL8K_RX_DESCS)
119862306a36Sopenharmony_ci			nexti = 0;
119962306a36Sopenharmony_ci		next_dma_addr = rxq->rxd_dma + (nexti * desc_size);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci		priv->rxd_ops->rxd_init(rxd, next_dma_addr);
120262306a36Sopenharmony_ci	}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	return 0;
120562306a36Sopenharmony_ci}
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_cistatic int rxq_refill(struct ieee80211_hw *hw, int index, int limit)
120862306a36Sopenharmony_ci{
120962306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
121062306a36Sopenharmony_ci	struct mwl8k_rx_queue *rxq = priv->rxq + index;
121162306a36Sopenharmony_ci	int refilled = 0;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	while (rxq->rxd_count < MWL8K_RX_DESCS && limit--) {
121462306a36Sopenharmony_ci		struct sk_buff *skb;
121562306a36Sopenharmony_ci		dma_addr_t addr;
121662306a36Sopenharmony_ci		int rx;
121762306a36Sopenharmony_ci		void *rxd;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci		skb = dev_alloc_skb(MWL8K_RX_MAXSZ);
122062306a36Sopenharmony_ci		if (skb == NULL)
122162306a36Sopenharmony_ci			break;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci		addr = dma_map_single(&priv->pdev->dev, skb->data,
122462306a36Sopenharmony_ci				      MWL8K_RX_MAXSZ, DMA_FROM_DEVICE);
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci		rxq->rxd_count++;
122762306a36Sopenharmony_ci		rx = rxq->tail++;
122862306a36Sopenharmony_ci		if (rxq->tail == MWL8K_RX_DESCS)
122962306a36Sopenharmony_ci			rxq->tail = 0;
123062306a36Sopenharmony_ci		rxq->buf[rx].skb = skb;
123162306a36Sopenharmony_ci		dma_unmap_addr_set(&rxq->buf[rx], dma, addr);
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci		rxd = rxq->rxd + (rx * priv->rxd_ops->rxd_size);
123462306a36Sopenharmony_ci		priv->rxd_ops->rxd_refill(rxd, addr, MWL8K_RX_MAXSZ);
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci		refilled++;
123762306a36Sopenharmony_ci	}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	return refilled;
124062306a36Sopenharmony_ci}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci/* Must be called only when the card's reception is completely halted */
124362306a36Sopenharmony_cistatic void mwl8k_rxq_deinit(struct ieee80211_hw *hw, int index)
124462306a36Sopenharmony_ci{
124562306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
124662306a36Sopenharmony_ci	struct mwl8k_rx_queue *rxq = priv->rxq + index;
124762306a36Sopenharmony_ci	int i;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	if (rxq->rxd == NULL)
125062306a36Sopenharmony_ci		return;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	for (i = 0; i < MWL8K_RX_DESCS; i++) {
125362306a36Sopenharmony_ci		if (rxq->buf[i].skb != NULL) {
125462306a36Sopenharmony_ci			dma_unmap_single(&priv->pdev->dev,
125562306a36Sopenharmony_ci					 dma_unmap_addr(&rxq->buf[i], dma),
125662306a36Sopenharmony_ci					 MWL8K_RX_MAXSZ, DMA_FROM_DEVICE);
125762306a36Sopenharmony_ci			dma_unmap_addr_set(&rxq->buf[i], dma, 0);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci			kfree_skb(rxq->buf[i].skb);
126062306a36Sopenharmony_ci			rxq->buf[i].skb = NULL;
126162306a36Sopenharmony_ci		}
126262306a36Sopenharmony_ci	}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	kfree(rxq->buf);
126562306a36Sopenharmony_ci	rxq->buf = NULL;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
126862306a36Sopenharmony_ci			  MWL8K_RX_DESCS * priv->rxd_ops->rxd_size, rxq->rxd,
126962306a36Sopenharmony_ci			  rxq->rxd_dma);
127062306a36Sopenharmony_ci	rxq->rxd = NULL;
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci/*
127562306a36Sopenharmony_ci * Scan a list of BSSIDs to process for finalize join.
127662306a36Sopenharmony_ci * Allows for extension to process multiple BSSIDs.
127762306a36Sopenharmony_ci */
127862306a36Sopenharmony_cistatic inline int
127962306a36Sopenharmony_cimwl8k_capture_bssid(struct mwl8k_priv *priv, struct ieee80211_hdr *wh)
128062306a36Sopenharmony_ci{
128162306a36Sopenharmony_ci	return priv->capture_beacon &&
128262306a36Sopenharmony_ci		ieee80211_is_beacon(wh->frame_control) &&
128362306a36Sopenharmony_ci		ether_addr_equal_64bits(wh->addr3, priv->capture_bssid);
128462306a36Sopenharmony_ci}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_cistatic inline void mwl8k_save_beacon(struct ieee80211_hw *hw,
128762306a36Sopenharmony_ci				     struct sk_buff *skb)
128862306a36Sopenharmony_ci{
128962306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	priv->capture_beacon = false;
129262306a36Sopenharmony_ci	eth_zero_addr(priv->capture_bssid);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	/*
129562306a36Sopenharmony_ci	 * Use GFP_ATOMIC as rxq_process is called from
129662306a36Sopenharmony_ci	 * the primary interrupt handler, memory allocation call
129762306a36Sopenharmony_ci	 * must not sleep.
129862306a36Sopenharmony_ci	 */
129962306a36Sopenharmony_ci	priv->beacon_skb = skb_copy(skb, GFP_ATOMIC);
130062306a36Sopenharmony_ci	if (priv->beacon_skb != NULL)
130162306a36Sopenharmony_ci		ieee80211_queue_work(hw, &priv->finalize_join_worker);
130262306a36Sopenharmony_ci}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_cistatic inline struct mwl8k_vif *mwl8k_find_vif_bss(struct list_head *vif_list,
130562306a36Sopenharmony_ci						   u8 *bssid)
130662306a36Sopenharmony_ci{
130762306a36Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	list_for_each_entry(mwl8k_vif,
131062306a36Sopenharmony_ci			    vif_list, list) {
131162306a36Sopenharmony_ci		if (memcmp(bssid, mwl8k_vif->bssid,
131262306a36Sopenharmony_ci			   ETH_ALEN) == 0)
131362306a36Sopenharmony_ci			return mwl8k_vif;
131462306a36Sopenharmony_ci	}
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	return NULL;
131762306a36Sopenharmony_ci}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_cistatic int rxq_process(struct ieee80211_hw *hw, int index, int limit)
132062306a36Sopenharmony_ci{
132162306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
132262306a36Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = NULL;
132362306a36Sopenharmony_ci	struct mwl8k_rx_queue *rxq = priv->rxq + index;
132462306a36Sopenharmony_ci	int processed;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	processed = 0;
132762306a36Sopenharmony_ci	while (rxq->rxd_count && limit--) {
132862306a36Sopenharmony_ci		struct sk_buff *skb;
132962306a36Sopenharmony_ci		void *rxd;
133062306a36Sopenharmony_ci		int pkt_len;
133162306a36Sopenharmony_ci		struct ieee80211_rx_status status;
133262306a36Sopenharmony_ci		struct ieee80211_hdr *wh;
133362306a36Sopenharmony_ci		__le16 qos;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci		skb = rxq->buf[rxq->head].skb;
133662306a36Sopenharmony_ci		if (skb == NULL)
133762306a36Sopenharmony_ci			break;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci		rxd = rxq->rxd + (rxq->head * priv->rxd_ops->rxd_size);
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci		pkt_len = priv->rxd_ops->rxd_process(rxd, &status, &qos,
134262306a36Sopenharmony_ci							&priv->noise);
134362306a36Sopenharmony_ci		if (pkt_len < 0)
134462306a36Sopenharmony_ci			break;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci		rxq->buf[rxq->head].skb = NULL;
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci		dma_unmap_single(&priv->pdev->dev,
134962306a36Sopenharmony_ci				 dma_unmap_addr(&rxq->buf[rxq->head], dma),
135062306a36Sopenharmony_ci				 MWL8K_RX_MAXSZ, DMA_FROM_DEVICE);
135162306a36Sopenharmony_ci		dma_unmap_addr_set(&rxq->buf[rxq->head], dma, 0);
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci		rxq->head++;
135462306a36Sopenharmony_ci		if (rxq->head == MWL8K_RX_DESCS)
135562306a36Sopenharmony_ci			rxq->head = 0;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci		rxq->rxd_count--;
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci		wh = &((struct mwl8k_dma_data *)skb->data)->wh;
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci		/*
136262306a36Sopenharmony_ci		 * Check for a pending join operation.  Save a
136362306a36Sopenharmony_ci		 * copy of the beacon and schedule a tasklet to
136462306a36Sopenharmony_ci		 * send a FINALIZE_JOIN command to the firmware.
136562306a36Sopenharmony_ci		 */
136662306a36Sopenharmony_ci		if (mwl8k_capture_bssid(priv, (void *)skb->data))
136762306a36Sopenharmony_ci			mwl8k_save_beacon(hw, skb);
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci		if (ieee80211_has_protected(wh->frame_control)) {
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci			/* Check if hw crypto has been enabled for
137262306a36Sopenharmony_ci			 * this bss. If yes, set the status flags
137362306a36Sopenharmony_ci			 * accordingly
137462306a36Sopenharmony_ci			 */
137562306a36Sopenharmony_ci			mwl8k_vif = mwl8k_find_vif_bss(&priv->vif_list,
137662306a36Sopenharmony_ci								wh->addr1);
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci			if (mwl8k_vif != NULL &&
137962306a36Sopenharmony_ci			    mwl8k_vif->is_hw_crypto_enabled) {
138062306a36Sopenharmony_ci				/*
138162306a36Sopenharmony_ci				 * When MMIC ERROR is encountered
138262306a36Sopenharmony_ci				 * by the firmware, payload is
138362306a36Sopenharmony_ci				 * dropped and only 32 bytes of
138462306a36Sopenharmony_ci				 * mwl8k Firmware header is sent
138562306a36Sopenharmony_ci				 * to the host.
138662306a36Sopenharmony_ci				 *
138762306a36Sopenharmony_ci				 * We need to add four bytes of
138862306a36Sopenharmony_ci				 * key information.  In it
138962306a36Sopenharmony_ci				 * MAC80211 expects keyidx set to
139062306a36Sopenharmony_ci				 * 0 for triggering Counter
139162306a36Sopenharmony_ci				 * Measure of MMIC failure.
139262306a36Sopenharmony_ci				 */
139362306a36Sopenharmony_ci				if (status.flag & RX_FLAG_MMIC_ERROR) {
139462306a36Sopenharmony_ci					struct mwl8k_dma_data *tr;
139562306a36Sopenharmony_ci					tr = (struct mwl8k_dma_data *)skb->data;
139662306a36Sopenharmony_ci					memset((void *)&(tr->data), 0, 4);
139762306a36Sopenharmony_ci					pkt_len += 4;
139862306a36Sopenharmony_ci				}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci				if (!ieee80211_is_auth(wh->frame_control))
140162306a36Sopenharmony_ci					status.flag |= RX_FLAG_IV_STRIPPED |
140262306a36Sopenharmony_ci						       RX_FLAG_DECRYPTED |
140362306a36Sopenharmony_ci						       RX_FLAG_MMIC_STRIPPED;
140462306a36Sopenharmony_ci			}
140562306a36Sopenharmony_ci		}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci		skb_put(skb, pkt_len);
140862306a36Sopenharmony_ci		mwl8k_remove_dma_header(skb, qos);
140962306a36Sopenharmony_ci		memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
141062306a36Sopenharmony_ci		ieee80211_rx_irqsafe(hw, skb);
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci		processed++;
141362306a36Sopenharmony_ci	}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	return processed;
141662306a36Sopenharmony_ci}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci/*
142062306a36Sopenharmony_ci * Packet transmission.
142162306a36Sopenharmony_ci */
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci#define MWL8K_TXD_STATUS_OK			0x00000001
142462306a36Sopenharmony_ci#define MWL8K_TXD_STATUS_OK_RETRY		0x00000002
142562306a36Sopenharmony_ci#define MWL8K_TXD_STATUS_OK_MORE_RETRY		0x00000004
142662306a36Sopenharmony_ci#define MWL8K_TXD_STATUS_MULTICAST_TX		0x00000008
142762306a36Sopenharmony_ci#define MWL8K_TXD_STATUS_FW_OWNED		0x80000000
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci#define MWL8K_QOS_QLEN_UNSPEC			0xff00
143062306a36Sopenharmony_ci#define MWL8K_QOS_ACK_POLICY_MASK		0x0060
143162306a36Sopenharmony_ci#define MWL8K_QOS_ACK_POLICY_NORMAL		0x0000
143262306a36Sopenharmony_ci#define MWL8K_QOS_ACK_POLICY_BLOCKACK		0x0060
143362306a36Sopenharmony_ci#define MWL8K_QOS_EOSP				0x0010
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_cistruct mwl8k_tx_desc {
143662306a36Sopenharmony_ci	__le32 status;
143762306a36Sopenharmony_ci	__u8 data_rate;
143862306a36Sopenharmony_ci	__u8 tx_priority;
143962306a36Sopenharmony_ci	__le16 qos_control;
144062306a36Sopenharmony_ci	__le32 pkt_phys_addr;
144162306a36Sopenharmony_ci	__le16 pkt_len;
144262306a36Sopenharmony_ci	__u8 dest_MAC_addr[ETH_ALEN];
144362306a36Sopenharmony_ci	__le32 next_txd_phys_addr;
144462306a36Sopenharmony_ci	__le32 timestamp;
144562306a36Sopenharmony_ci	__le16 rate_info;
144662306a36Sopenharmony_ci	__u8 peer_id;
144762306a36Sopenharmony_ci	__u8 tx_frag_cnt;
144862306a36Sopenharmony_ci} __packed;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci#define MWL8K_TX_DESCS		128
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_cistatic int mwl8k_txq_init(struct ieee80211_hw *hw, int index)
145362306a36Sopenharmony_ci{
145462306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
145562306a36Sopenharmony_ci	struct mwl8k_tx_queue *txq = priv->txq + index;
145662306a36Sopenharmony_ci	int size;
145762306a36Sopenharmony_ci	int i;
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	txq->len = 0;
146062306a36Sopenharmony_ci	txq->head = 0;
146162306a36Sopenharmony_ci	txq->tail = 0;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	size = MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	txq->txd = dma_alloc_coherent(&priv->pdev->dev, size, &txq->txd_dma,
146662306a36Sopenharmony_ci				      GFP_KERNEL);
146762306a36Sopenharmony_ci	if (txq->txd == NULL) {
146862306a36Sopenharmony_ci		wiphy_err(hw->wiphy, "failed to alloc TX descriptors\n");
146962306a36Sopenharmony_ci		return -ENOMEM;
147062306a36Sopenharmony_ci	}
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	txq->skb = kcalloc(MWL8K_TX_DESCS, sizeof(*txq->skb), GFP_KERNEL);
147362306a36Sopenharmony_ci	if (txq->skb == NULL) {
147462306a36Sopenharmony_ci		dma_free_coherent(&priv->pdev->dev, size, txq->txd,
147562306a36Sopenharmony_ci				  txq->txd_dma);
147662306a36Sopenharmony_ci		txq->txd = NULL;
147762306a36Sopenharmony_ci		return -ENOMEM;
147862306a36Sopenharmony_ci	}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	for (i = 0; i < MWL8K_TX_DESCS; i++) {
148162306a36Sopenharmony_ci		struct mwl8k_tx_desc *tx_desc;
148262306a36Sopenharmony_ci		int nexti;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci		tx_desc = txq->txd + i;
148562306a36Sopenharmony_ci		nexti = (i + 1) % MWL8K_TX_DESCS;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci		tx_desc->status = 0;
148862306a36Sopenharmony_ci		tx_desc->next_txd_phys_addr =
148962306a36Sopenharmony_ci			cpu_to_le32(txq->txd_dma + nexti * sizeof(*tx_desc));
149062306a36Sopenharmony_ci	}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	return 0;
149362306a36Sopenharmony_ci}
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_cistatic inline void mwl8k_tx_start(struct mwl8k_priv *priv)
149662306a36Sopenharmony_ci{
149762306a36Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_PPA_READY,
149862306a36Sopenharmony_ci		priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
149962306a36Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_DUMMY,
150062306a36Sopenharmony_ci		priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
150162306a36Sopenharmony_ci	ioread32(priv->regs + MWL8K_HIU_INT_CODE);
150262306a36Sopenharmony_ci}
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_cistatic void mwl8k_dump_tx_rings(struct ieee80211_hw *hw)
150562306a36Sopenharmony_ci{
150662306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
150762306a36Sopenharmony_ci	int i;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++) {
151062306a36Sopenharmony_ci		struct mwl8k_tx_queue *txq = priv->txq + i;
151162306a36Sopenharmony_ci		int fw_owned = 0;
151262306a36Sopenharmony_ci		int drv_owned = 0;
151362306a36Sopenharmony_ci		int unused = 0;
151462306a36Sopenharmony_ci		int desc;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci		for (desc = 0; desc < MWL8K_TX_DESCS; desc++) {
151762306a36Sopenharmony_ci			struct mwl8k_tx_desc *tx_desc = txq->txd + desc;
151862306a36Sopenharmony_ci			u32 status;
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci			status = le32_to_cpu(tx_desc->status);
152162306a36Sopenharmony_ci			if (status & MWL8K_TXD_STATUS_FW_OWNED)
152262306a36Sopenharmony_ci				fw_owned++;
152362306a36Sopenharmony_ci			else
152462306a36Sopenharmony_ci				drv_owned++;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci			if (tx_desc->pkt_len == 0)
152762306a36Sopenharmony_ci				unused++;
152862306a36Sopenharmony_ci		}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci		wiphy_err(hw->wiphy,
153162306a36Sopenharmony_ci			  "txq[%d] len=%d head=%d tail=%d "
153262306a36Sopenharmony_ci			  "fw_owned=%d drv_owned=%d unused=%d\n",
153362306a36Sopenharmony_ci			  i,
153462306a36Sopenharmony_ci			  txq->len, txq->head, txq->tail,
153562306a36Sopenharmony_ci			  fw_owned, drv_owned, unused);
153662306a36Sopenharmony_ci	}
153762306a36Sopenharmony_ci}
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci/*
154062306a36Sopenharmony_ci * Must be called with priv->fw_mutex held and tx queues stopped.
154162306a36Sopenharmony_ci */
154262306a36Sopenharmony_ci#define MWL8K_TX_WAIT_TIMEOUT_MS	5000
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_cistatic int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
154562306a36Sopenharmony_ci{
154662306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
154762306a36Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(tx_wait);
154862306a36Sopenharmony_ci	int retry;
154962306a36Sopenharmony_ci	int rc;
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	might_sleep();
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	/* Since fw restart is in progress, allow only the firmware
155462306a36Sopenharmony_ci	 * commands from the restart code and block the other
155562306a36Sopenharmony_ci	 * commands since they are going to fail in any case since
155662306a36Sopenharmony_ci	 * the firmware has crashed
155762306a36Sopenharmony_ci	 */
155862306a36Sopenharmony_ci	if (priv->hw_restart_in_progress) {
155962306a36Sopenharmony_ci		if (priv->hw_restart_owner == current)
156062306a36Sopenharmony_ci			return 0;
156162306a36Sopenharmony_ci		else
156262306a36Sopenharmony_ci			return -EBUSY;
156362306a36Sopenharmony_ci	}
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	if (atomic_read(&priv->watchdog_event_pending))
156662306a36Sopenharmony_ci		return 0;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	/*
156962306a36Sopenharmony_ci	 * The TX queues are stopped at this point, so this test
157062306a36Sopenharmony_ci	 * doesn't need to take ->tx_lock.
157162306a36Sopenharmony_ci	 */
157262306a36Sopenharmony_ci	if (!priv->pending_tx_pkts)
157362306a36Sopenharmony_ci		return 0;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	retry = 1;
157662306a36Sopenharmony_ci	rc = 0;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	spin_lock_bh(&priv->tx_lock);
157962306a36Sopenharmony_ci	priv->tx_wait = &tx_wait;
158062306a36Sopenharmony_ci	while (!rc) {
158162306a36Sopenharmony_ci		int oldcount;
158262306a36Sopenharmony_ci		unsigned long timeout;
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci		oldcount = priv->pending_tx_pkts;
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci		spin_unlock_bh(&priv->tx_lock);
158762306a36Sopenharmony_ci		timeout = wait_for_completion_timeout(&tx_wait,
158862306a36Sopenharmony_ci			    msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS));
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci		if (atomic_read(&priv->watchdog_event_pending)) {
159162306a36Sopenharmony_ci			spin_lock_bh(&priv->tx_lock);
159262306a36Sopenharmony_ci			priv->tx_wait = NULL;
159362306a36Sopenharmony_ci			spin_unlock_bh(&priv->tx_lock);
159462306a36Sopenharmony_ci			return 0;
159562306a36Sopenharmony_ci		}
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci		spin_lock_bh(&priv->tx_lock);
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci		if (timeout || !priv->pending_tx_pkts) {
160062306a36Sopenharmony_ci			WARN_ON(priv->pending_tx_pkts);
160162306a36Sopenharmony_ci			if (retry)
160262306a36Sopenharmony_ci				wiphy_notice(hw->wiphy, "tx rings drained\n");
160362306a36Sopenharmony_ci			break;
160462306a36Sopenharmony_ci		}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci		if (retry) {
160762306a36Sopenharmony_ci			mwl8k_tx_start(priv);
160862306a36Sopenharmony_ci			retry = 0;
160962306a36Sopenharmony_ci			continue;
161062306a36Sopenharmony_ci		}
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci		if (priv->pending_tx_pkts < oldcount) {
161362306a36Sopenharmony_ci			wiphy_notice(hw->wiphy,
161462306a36Sopenharmony_ci				     "waiting for tx rings to drain (%d -> %d pkts)\n",
161562306a36Sopenharmony_ci				     oldcount, priv->pending_tx_pkts);
161662306a36Sopenharmony_ci			retry = 1;
161762306a36Sopenharmony_ci			continue;
161862306a36Sopenharmony_ci		}
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci		priv->tx_wait = NULL;
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci		wiphy_err(hw->wiphy, "tx rings stuck for %d ms\n",
162362306a36Sopenharmony_ci			  MWL8K_TX_WAIT_TIMEOUT_MS);
162462306a36Sopenharmony_ci		mwl8k_dump_tx_rings(hw);
162562306a36Sopenharmony_ci		priv->hw_restart_in_progress = true;
162662306a36Sopenharmony_ci		ieee80211_queue_work(hw, &priv->fw_reload);
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci		rc = -ETIMEDOUT;
162962306a36Sopenharmony_ci	}
163062306a36Sopenharmony_ci	priv->tx_wait = NULL;
163162306a36Sopenharmony_ci	spin_unlock_bh(&priv->tx_lock);
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	return rc;
163462306a36Sopenharmony_ci}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci#define MWL8K_TXD_SUCCESS(status)				\
163762306a36Sopenharmony_ci	((status) & (MWL8K_TXD_STATUS_OK |			\
163862306a36Sopenharmony_ci		     MWL8K_TXD_STATUS_OK_RETRY |		\
163962306a36Sopenharmony_ci		     MWL8K_TXD_STATUS_OK_MORE_RETRY))
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_cistatic int mwl8k_tid_queue_mapping(u8 tid)
164262306a36Sopenharmony_ci{
164362306a36Sopenharmony_ci	BUG_ON(tid > 7);
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	switch (tid) {
164662306a36Sopenharmony_ci	case 0:
164762306a36Sopenharmony_ci	case 3:
164862306a36Sopenharmony_ci		return IEEE80211_AC_BE;
164962306a36Sopenharmony_ci	case 1:
165062306a36Sopenharmony_ci	case 2:
165162306a36Sopenharmony_ci		return IEEE80211_AC_BK;
165262306a36Sopenharmony_ci	case 4:
165362306a36Sopenharmony_ci	case 5:
165462306a36Sopenharmony_ci		return IEEE80211_AC_VI;
165562306a36Sopenharmony_ci	case 6:
165662306a36Sopenharmony_ci	case 7:
165762306a36Sopenharmony_ci		return IEEE80211_AC_VO;
165862306a36Sopenharmony_ci	default:
165962306a36Sopenharmony_ci		return -1;
166062306a36Sopenharmony_ci	}
166162306a36Sopenharmony_ci}
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci/* The firmware will fill in the rate information
166462306a36Sopenharmony_ci * for each packet that gets queued in the hardware
166562306a36Sopenharmony_ci * and these macros will interpret that info.
166662306a36Sopenharmony_ci */
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci#define RI_FORMAT(a)		  (a & 0x0001)
166962306a36Sopenharmony_ci#define RI_RATE_ID_MCS(a)	 ((a & 0x01f8) >> 3)
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_cistatic int
167262306a36Sopenharmony_cimwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force)
167362306a36Sopenharmony_ci{
167462306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
167562306a36Sopenharmony_ci	struct mwl8k_tx_queue *txq = priv->txq + index;
167662306a36Sopenharmony_ci	int processed;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	processed = 0;
167962306a36Sopenharmony_ci	while (txq->len > 0 && limit--) {
168062306a36Sopenharmony_ci		int tx;
168162306a36Sopenharmony_ci		struct mwl8k_tx_desc *tx_desc;
168262306a36Sopenharmony_ci		unsigned long addr;
168362306a36Sopenharmony_ci		int size;
168462306a36Sopenharmony_ci		struct sk_buff *skb;
168562306a36Sopenharmony_ci		struct ieee80211_tx_info *info;
168662306a36Sopenharmony_ci		u32 status;
168762306a36Sopenharmony_ci		struct ieee80211_sta *sta;
168862306a36Sopenharmony_ci		struct mwl8k_sta *sta_info = NULL;
168962306a36Sopenharmony_ci		u16 rate_info;
169062306a36Sopenharmony_ci		struct ieee80211_hdr *wh;
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci		tx = txq->head;
169362306a36Sopenharmony_ci		tx_desc = txq->txd + tx;
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci		status = le32_to_cpu(tx_desc->status);
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci		if (status & MWL8K_TXD_STATUS_FW_OWNED) {
169862306a36Sopenharmony_ci			if (!force)
169962306a36Sopenharmony_ci				break;
170062306a36Sopenharmony_ci			tx_desc->status &=
170162306a36Sopenharmony_ci				~cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED);
170262306a36Sopenharmony_ci		}
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci		txq->head = (tx + 1) % MWL8K_TX_DESCS;
170562306a36Sopenharmony_ci		BUG_ON(txq->len == 0);
170662306a36Sopenharmony_ci		txq->len--;
170762306a36Sopenharmony_ci		priv->pending_tx_pkts--;
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci		addr = le32_to_cpu(tx_desc->pkt_phys_addr);
171062306a36Sopenharmony_ci		size = le16_to_cpu(tx_desc->pkt_len);
171162306a36Sopenharmony_ci		skb = txq->skb[tx];
171262306a36Sopenharmony_ci		txq->skb[tx] = NULL;
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci		BUG_ON(skb == NULL);
171562306a36Sopenharmony_ci		dma_unmap_single(&priv->pdev->dev, addr, size, DMA_TO_DEVICE);
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci		mwl8k_remove_dma_header(skb, tx_desc->qos_control);
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci		wh = (struct ieee80211_hdr *) skb->data;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci		/* Mark descriptor as unused */
172262306a36Sopenharmony_ci		tx_desc->pkt_phys_addr = 0;
172362306a36Sopenharmony_ci		tx_desc->pkt_len = 0;
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci		info = IEEE80211_SKB_CB(skb);
172662306a36Sopenharmony_ci		if (ieee80211_is_data(wh->frame_control)) {
172762306a36Sopenharmony_ci			rcu_read_lock();
172862306a36Sopenharmony_ci			sta = ieee80211_find_sta_by_ifaddr(hw, wh->addr1,
172962306a36Sopenharmony_ci							   wh->addr2);
173062306a36Sopenharmony_ci			if (sta) {
173162306a36Sopenharmony_ci				sta_info = MWL8K_STA(sta);
173262306a36Sopenharmony_ci				BUG_ON(sta_info == NULL);
173362306a36Sopenharmony_ci				rate_info = le16_to_cpu(tx_desc->rate_info);
173462306a36Sopenharmony_ci				/* If rate is < 6.5 Mpbs for an ht station
173562306a36Sopenharmony_ci				 * do not form an ampdu. If the station is a
173662306a36Sopenharmony_ci				 * legacy station (format = 0), do not form an
173762306a36Sopenharmony_ci				 * ampdu
173862306a36Sopenharmony_ci				 */
173962306a36Sopenharmony_ci				if (RI_RATE_ID_MCS(rate_info) < 1 ||
174062306a36Sopenharmony_ci				    RI_FORMAT(rate_info) == 0) {
174162306a36Sopenharmony_ci					sta_info->is_ampdu_allowed = false;
174262306a36Sopenharmony_ci				} else {
174362306a36Sopenharmony_ci					sta_info->is_ampdu_allowed = true;
174462306a36Sopenharmony_ci				}
174562306a36Sopenharmony_ci			}
174662306a36Sopenharmony_ci			rcu_read_unlock();
174762306a36Sopenharmony_ci		}
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci		ieee80211_tx_info_clear_status(info);
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci		/* Rate control is happening in the firmware.
175262306a36Sopenharmony_ci		 * Ensure no tx rate is being reported.
175362306a36Sopenharmony_ci		 */
175462306a36Sopenharmony_ci		info->status.rates[0].idx = -1;
175562306a36Sopenharmony_ci		info->status.rates[0].count = 1;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci		if (MWL8K_TXD_SUCCESS(status))
175862306a36Sopenharmony_ci			info->flags |= IEEE80211_TX_STAT_ACK;
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci		ieee80211_tx_status_irqsafe(hw, skb);
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci		processed++;
176362306a36Sopenharmony_ci	}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	return processed;
176662306a36Sopenharmony_ci}
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci/* must be called only when the card's transmit is completely halted */
176962306a36Sopenharmony_cistatic void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index)
177062306a36Sopenharmony_ci{
177162306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
177262306a36Sopenharmony_ci	struct mwl8k_tx_queue *txq = priv->txq + index;
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	if (txq->txd == NULL)
177562306a36Sopenharmony_ci		return;
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	mwl8k_txq_reclaim(hw, index, INT_MAX, 1);
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	kfree(txq->skb);
178062306a36Sopenharmony_ci	txq->skb = NULL;
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
178362306a36Sopenharmony_ci			  MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc),
178462306a36Sopenharmony_ci			  txq->txd, txq->txd_dma);
178562306a36Sopenharmony_ci	txq->txd = NULL;
178662306a36Sopenharmony_ci}
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci/* caller must hold priv->stream_lock when calling the stream functions */
178962306a36Sopenharmony_cistatic struct mwl8k_ampdu_stream *
179062306a36Sopenharmony_cimwl8k_add_stream(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 tid)
179162306a36Sopenharmony_ci{
179262306a36Sopenharmony_ci	struct mwl8k_ampdu_stream *stream;
179362306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
179462306a36Sopenharmony_ci	int i;
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) {
179762306a36Sopenharmony_ci		stream = &priv->ampdu[i];
179862306a36Sopenharmony_ci		if (stream->state == AMPDU_NO_STREAM) {
179962306a36Sopenharmony_ci			stream->sta = sta;
180062306a36Sopenharmony_ci			stream->state = AMPDU_STREAM_NEW;
180162306a36Sopenharmony_ci			stream->tid = tid;
180262306a36Sopenharmony_ci			stream->idx = i;
180362306a36Sopenharmony_ci			wiphy_debug(hw->wiphy, "Added a new stream for %pM %d",
180462306a36Sopenharmony_ci				    sta->addr, tid);
180562306a36Sopenharmony_ci			return stream;
180662306a36Sopenharmony_ci		}
180762306a36Sopenharmony_ci	}
180862306a36Sopenharmony_ci	return NULL;
180962306a36Sopenharmony_ci}
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_cistatic int
181262306a36Sopenharmony_cimwl8k_start_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream)
181362306a36Sopenharmony_ci{
181462306a36Sopenharmony_ci	int ret;
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	/* if the stream has already been started, don't start it again */
181762306a36Sopenharmony_ci	if (stream->state != AMPDU_STREAM_NEW)
181862306a36Sopenharmony_ci		return 0;
181962306a36Sopenharmony_ci	ret = ieee80211_start_tx_ba_session(stream->sta, stream->tid, 0);
182062306a36Sopenharmony_ci	if (ret)
182162306a36Sopenharmony_ci		wiphy_debug(hw->wiphy, "Failed to start stream for %pM %d: "
182262306a36Sopenharmony_ci			    "%d\n", stream->sta->addr, stream->tid, ret);
182362306a36Sopenharmony_ci	else
182462306a36Sopenharmony_ci		wiphy_debug(hw->wiphy, "Started stream for %pM %d\n",
182562306a36Sopenharmony_ci			    stream->sta->addr, stream->tid);
182662306a36Sopenharmony_ci	return ret;
182762306a36Sopenharmony_ci}
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_cistatic void
183062306a36Sopenharmony_cimwl8k_remove_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream)
183162306a36Sopenharmony_ci{
183262306a36Sopenharmony_ci	wiphy_debug(hw->wiphy, "Remove stream for %pM %d\n", stream->sta->addr,
183362306a36Sopenharmony_ci		    stream->tid);
183462306a36Sopenharmony_ci	memset(stream, 0, sizeof(*stream));
183562306a36Sopenharmony_ci}
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_cistatic struct mwl8k_ampdu_stream *
183862306a36Sopenharmony_cimwl8k_lookup_stream(struct ieee80211_hw *hw, u8 *addr, u8 tid)
183962306a36Sopenharmony_ci{
184062306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
184162306a36Sopenharmony_ci	int i;
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) {
184462306a36Sopenharmony_ci		struct mwl8k_ampdu_stream *stream;
184562306a36Sopenharmony_ci		stream = &priv->ampdu[i];
184662306a36Sopenharmony_ci		if (stream->state == AMPDU_NO_STREAM)
184762306a36Sopenharmony_ci			continue;
184862306a36Sopenharmony_ci		if (!memcmp(stream->sta->addr, addr, ETH_ALEN) &&
184962306a36Sopenharmony_ci		    stream->tid == tid)
185062306a36Sopenharmony_ci			return stream;
185162306a36Sopenharmony_ci	}
185262306a36Sopenharmony_ci	return NULL;
185362306a36Sopenharmony_ci}
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci#define MWL8K_AMPDU_PACKET_THRESHOLD 64
185662306a36Sopenharmony_cistatic inline bool mwl8k_ampdu_allowed(struct ieee80211_sta *sta, u8 tid)
185762306a36Sopenharmony_ci{
185862306a36Sopenharmony_ci	struct mwl8k_sta *sta_info = MWL8K_STA(sta);
185962306a36Sopenharmony_ci	struct tx_traffic_info *tx_stats;
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	BUG_ON(tid >= MWL8K_MAX_TID);
186262306a36Sopenharmony_ci	tx_stats = &sta_info->tx_stats[tid];
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	return sta_info->is_ampdu_allowed &&
186562306a36Sopenharmony_ci		tx_stats->pkts > MWL8K_AMPDU_PACKET_THRESHOLD;
186662306a36Sopenharmony_ci}
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_cistatic inline void mwl8k_tx_count_packet(struct ieee80211_sta *sta, u8 tid)
186962306a36Sopenharmony_ci{
187062306a36Sopenharmony_ci	struct mwl8k_sta *sta_info = MWL8K_STA(sta);
187162306a36Sopenharmony_ci	struct tx_traffic_info *tx_stats;
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	BUG_ON(tid >= MWL8K_MAX_TID);
187462306a36Sopenharmony_ci	tx_stats = &sta_info->tx_stats[tid];
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	if (tx_stats->start_time == 0)
187762306a36Sopenharmony_ci		tx_stats->start_time = jiffies;
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	/* reset the packet count after each second elapses.  If the number of
188062306a36Sopenharmony_ci	 * packets ever exceeds the ampdu_min_traffic threshold, we will allow
188162306a36Sopenharmony_ci	 * an ampdu stream to be started.
188262306a36Sopenharmony_ci	 */
188362306a36Sopenharmony_ci	if (time_after(jiffies, (unsigned long)tx_stats->start_time + HZ)) {
188462306a36Sopenharmony_ci		tx_stats->pkts = 0;
188562306a36Sopenharmony_ci		tx_stats->start_time = 0;
188662306a36Sopenharmony_ci	} else
188762306a36Sopenharmony_ci		tx_stats->pkts++;
188862306a36Sopenharmony_ci}
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci/* The hardware ampdu queues start from 5.
189162306a36Sopenharmony_ci * txpriorities for ampdu queues are
189262306a36Sopenharmony_ci * 5 6 7 0 1 2 3 4 ie., queue 5 is highest
189362306a36Sopenharmony_ci * and queue 3 is lowest (queue 4 is reserved)
189462306a36Sopenharmony_ci */
189562306a36Sopenharmony_ci#define BA_QUEUE		5
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_cistatic void
189862306a36Sopenharmony_cimwl8k_txq_xmit(struct ieee80211_hw *hw,
189962306a36Sopenharmony_ci	       int index,
190062306a36Sopenharmony_ci	       struct ieee80211_sta *sta,
190162306a36Sopenharmony_ci	       struct sk_buff *skb)
190262306a36Sopenharmony_ci{
190362306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
190462306a36Sopenharmony_ci	struct ieee80211_tx_info *tx_info;
190562306a36Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif;
190662306a36Sopenharmony_ci	struct ieee80211_hdr *wh;
190762306a36Sopenharmony_ci	struct mwl8k_tx_queue *txq;
190862306a36Sopenharmony_ci	struct mwl8k_tx_desc *tx;
190962306a36Sopenharmony_ci	dma_addr_t dma;
191062306a36Sopenharmony_ci	u32 txstatus;
191162306a36Sopenharmony_ci	u8 txdatarate;
191262306a36Sopenharmony_ci	u16 qos;
191362306a36Sopenharmony_ci	int txpriority;
191462306a36Sopenharmony_ci	u8 tid = 0;
191562306a36Sopenharmony_ci	struct mwl8k_ampdu_stream *stream = NULL;
191662306a36Sopenharmony_ci	bool start_ba_session = false;
191762306a36Sopenharmony_ci	bool mgmtframe = false;
191862306a36Sopenharmony_ci	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
191962306a36Sopenharmony_ci	bool eapol_frame = false;
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	wh = (struct ieee80211_hdr *)skb->data;
192262306a36Sopenharmony_ci	if (ieee80211_is_data_qos(wh->frame_control))
192362306a36Sopenharmony_ci		qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh)));
192462306a36Sopenharmony_ci	else
192562306a36Sopenharmony_ci		qos = 0;
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	if (skb->protocol == cpu_to_be16(ETH_P_PAE))
192862306a36Sopenharmony_ci		eapol_frame = true;
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	if (ieee80211_is_mgmt(wh->frame_control))
193162306a36Sopenharmony_ci		mgmtframe = true;
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	if (priv->ap_fw)
193462306a36Sopenharmony_ci		mwl8k_encapsulate_tx_frame(priv, skb);
193562306a36Sopenharmony_ci	else
193662306a36Sopenharmony_ci		mwl8k_add_dma_header(priv, skb, 0, 0);
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci	wh = &((struct mwl8k_dma_data *)skb->data)->wh;
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	tx_info = IEEE80211_SKB_CB(skb);
194162306a36Sopenharmony_ci	mwl8k_vif = MWL8K_VIF(tx_info->control.vif);
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
194462306a36Sopenharmony_ci		wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
194562306a36Sopenharmony_ci		wh->seq_ctrl |= cpu_to_le16(mwl8k_vif->seqno);
194662306a36Sopenharmony_ci		mwl8k_vif->seqno += 0x10;
194762306a36Sopenharmony_ci	}
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	/* Setup firmware control bit fields for each frame type.  */
195062306a36Sopenharmony_ci	txstatus = 0;
195162306a36Sopenharmony_ci	txdatarate = 0;
195262306a36Sopenharmony_ci	if (ieee80211_is_mgmt(wh->frame_control) ||
195362306a36Sopenharmony_ci	    ieee80211_is_ctl(wh->frame_control)) {
195462306a36Sopenharmony_ci		txdatarate = 0;
195562306a36Sopenharmony_ci		qos |= MWL8K_QOS_QLEN_UNSPEC | MWL8K_QOS_EOSP;
195662306a36Sopenharmony_ci	} else if (ieee80211_is_data(wh->frame_control)) {
195762306a36Sopenharmony_ci		txdatarate = 1;
195862306a36Sopenharmony_ci		if (is_multicast_ether_addr(wh->addr1))
195962306a36Sopenharmony_ci			txstatus |= MWL8K_TXD_STATUS_MULTICAST_TX;
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci		qos &= ~MWL8K_QOS_ACK_POLICY_MASK;
196262306a36Sopenharmony_ci		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
196362306a36Sopenharmony_ci			qos |= MWL8K_QOS_ACK_POLICY_BLOCKACK;
196462306a36Sopenharmony_ci		else
196562306a36Sopenharmony_ci			qos |= MWL8K_QOS_ACK_POLICY_NORMAL;
196662306a36Sopenharmony_ci	}
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_ci	/* Queue ADDBA request in the respective data queue.  While setting up
196962306a36Sopenharmony_ci	 * the ampdu stream, mac80211 queues further packets for that
197062306a36Sopenharmony_ci	 * particular ra/tid pair.  However, packets piled up in the hardware
197162306a36Sopenharmony_ci	 * for that ra/tid pair will still go out. ADDBA request and the
197262306a36Sopenharmony_ci	 * related data packets going out from different queues asynchronously
197362306a36Sopenharmony_ci	 * will cause a shift in the receiver window which might result in
197462306a36Sopenharmony_ci	 * ampdu packets getting dropped at the receiver after the stream has
197562306a36Sopenharmony_ci	 * been setup.
197662306a36Sopenharmony_ci	 */
197762306a36Sopenharmony_ci	if (unlikely(ieee80211_is_action(wh->frame_control) &&
197862306a36Sopenharmony_ci	    mgmt->u.action.category == WLAN_CATEGORY_BACK &&
197962306a36Sopenharmony_ci	    mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ &&
198062306a36Sopenharmony_ci	    priv->ap_fw)) {
198162306a36Sopenharmony_ci		u16 capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
198262306a36Sopenharmony_ci		tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
198362306a36Sopenharmony_ci		index = mwl8k_tid_queue_mapping(tid);
198462306a36Sopenharmony_ci	}
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci	txpriority = index;
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	if (priv->ap_fw && sta && sta->deflink.ht_cap.ht_supported && !eapol_frame &&
198962306a36Sopenharmony_ci	    ieee80211_is_data_qos(wh->frame_control)) {
199062306a36Sopenharmony_ci		tid = qos & 0xf;
199162306a36Sopenharmony_ci		mwl8k_tx_count_packet(sta, tid);
199262306a36Sopenharmony_ci		spin_lock(&priv->stream_lock);
199362306a36Sopenharmony_ci		stream = mwl8k_lookup_stream(hw, sta->addr, tid);
199462306a36Sopenharmony_ci		if (stream != NULL) {
199562306a36Sopenharmony_ci			if (stream->state == AMPDU_STREAM_ACTIVE) {
199662306a36Sopenharmony_ci				WARN_ON(!(qos & MWL8K_QOS_ACK_POLICY_BLOCKACK));
199762306a36Sopenharmony_ci				txpriority = (BA_QUEUE + stream->idx) %
199862306a36Sopenharmony_ci					     TOTAL_HW_TX_QUEUES;
199962306a36Sopenharmony_ci				if (stream->idx <= 1)
200062306a36Sopenharmony_ci					index = stream->idx +
200162306a36Sopenharmony_ci						MWL8K_TX_WMM_QUEUES;
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci			} else if (stream->state == AMPDU_STREAM_NEW) {
200462306a36Sopenharmony_ci				/* We get here if the driver sends us packets
200562306a36Sopenharmony_ci				 * after we've initiated a stream, but before
200662306a36Sopenharmony_ci				 * our ampdu_action routine has been called
200762306a36Sopenharmony_ci				 * with IEEE80211_AMPDU_TX_START to get the SSN
200862306a36Sopenharmony_ci				 * for the ADDBA request.  So this packet can
200962306a36Sopenharmony_ci				 * go out with no risk of sequence number
201062306a36Sopenharmony_ci				 * mismatch.  No special handling is required.
201162306a36Sopenharmony_ci				 */
201262306a36Sopenharmony_ci			} else {
201362306a36Sopenharmony_ci				/* Drop packets that would go out after the
201462306a36Sopenharmony_ci				 * ADDBA request was sent but before the ADDBA
201562306a36Sopenharmony_ci				 * response is received.  If we don't do this,
201662306a36Sopenharmony_ci				 * the recipient would probably receive it
201762306a36Sopenharmony_ci				 * after the ADDBA request with SSN 0.  This
201862306a36Sopenharmony_ci				 * will cause the recipient's BA receive window
201962306a36Sopenharmony_ci				 * to shift, which would cause the subsequent
202062306a36Sopenharmony_ci				 * packets in the BA stream to be discarded.
202162306a36Sopenharmony_ci				 * mac80211 queues our packets for us in this
202262306a36Sopenharmony_ci				 * case, so this is really just a safety check.
202362306a36Sopenharmony_ci				 */
202462306a36Sopenharmony_ci				wiphy_warn(hw->wiphy,
202562306a36Sopenharmony_ci					   "Cannot send packet while ADDBA "
202662306a36Sopenharmony_ci					   "dialog is underway.\n");
202762306a36Sopenharmony_ci				spin_unlock(&priv->stream_lock);
202862306a36Sopenharmony_ci				dev_kfree_skb(skb);
202962306a36Sopenharmony_ci				return;
203062306a36Sopenharmony_ci			}
203162306a36Sopenharmony_ci		} else {
203262306a36Sopenharmony_ci			/* Defer calling mwl8k_start_stream so that the current
203362306a36Sopenharmony_ci			 * skb can go out before the ADDBA request.  This
203462306a36Sopenharmony_ci			 * prevents sequence number mismatch at the recepient
203562306a36Sopenharmony_ci			 * as described above.
203662306a36Sopenharmony_ci			 */
203762306a36Sopenharmony_ci			if (mwl8k_ampdu_allowed(sta, tid)) {
203862306a36Sopenharmony_ci				stream = mwl8k_add_stream(hw, sta, tid);
203962306a36Sopenharmony_ci				if (stream != NULL)
204062306a36Sopenharmony_ci					start_ba_session = true;
204162306a36Sopenharmony_ci			}
204262306a36Sopenharmony_ci		}
204362306a36Sopenharmony_ci		spin_unlock(&priv->stream_lock);
204462306a36Sopenharmony_ci	} else {
204562306a36Sopenharmony_ci		qos &= ~MWL8K_QOS_ACK_POLICY_MASK;
204662306a36Sopenharmony_ci		qos |= MWL8K_QOS_ACK_POLICY_NORMAL;
204762306a36Sopenharmony_ci	}
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_ci	dma = dma_map_single(&priv->pdev->dev, skb->data, skb->len,
205062306a36Sopenharmony_ci			     DMA_TO_DEVICE);
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci	if (dma_mapping_error(&priv->pdev->dev, dma)) {
205362306a36Sopenharmony_ci		wiphy_debug(hw->wiphy,
205462306a36Sopenharmony_ci			    "failed to dma map skb, dropping TX frame.\n");
205562306a36Sopenharmony_ci		if (start_ba_session) {
205662306a36Sopenharmony_ci			spin_lock(&priv->stream_lock);
205762306a36Sopenharmony_ci			mwl8k_remove_stream(hw, stream);
205862306a36Sopenharmony_ci			spin_unlock(&priv->stream_lock);
205962306a36Sopenharmony_ci		}
206062306a36Sopenharmony_ci		dev_kfree_skb(skb);
206162306a36Sopenharmony_ci		return;
206262306a36Sopenharmony_ci	}
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci	spin_lock_bh(&priv->tx_lock);
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	txq = priv->txq + index;
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	/* Mgmt frames that go out frequently are probe
206962306a36Sopenharmony_ci	 * responses. Other mgmt frames got out relatively
207062306a36Sopenharmony_ci	 * infrequently. Hence reserve 2 buffers so that
207162306a36Sopenharmony_ci	 * other mgmt frames do not get dropped due to an
207262306a36Sopenharmony_ci	 * already queued probe response in one of the
207362306a36Sopenharmony_ci	 * reserved buffers.
207462306a36Sopenharmony_ci	 */
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	if (txq->len >= MWL8K_TX_DESCS - 2) {
207762306a36Sopenharmony_ci		if (!mgmtframe || txq->len == MWL8K_TX_DESCS) {
207862306a36Sopenharmony_ci			if (start_ba_session) {
207962306a36Sopenharmony_ci				spin_lock(&priv->stream_lock);
208062306a36Sopenharmony_ci				mwl8k_remove_stream(hw, stream);
208162306a36Sopenharmony_ci				spin_unlock(&priv->stream_lock);
208262306a36Sopenharmony_ci			}
208362306a36Sopenharmony_ci			mwl8k_tx_start(priv);
208462306a36Sopenharmony_ci			spin_unlock_bh(&priv->tx_lock);
208562306a36Sopenharmony_ci			dma_unmap_single(&priv->pdev->dev, dma, skb->len,
208662306a36Sopenharmony_ci					 DMA_TO_DEVICE);
208762306a36Sopenharmony_ci			dev_kfree_skb(skb);
208862306a36Sopenharmony_ci			return;
208962306a36Sopenharmony_ci		}
209062306a36Sopenharmony_ci	}
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_ci	BUG_ON(txq->skb[txq->tail] != NULL);
209362306a36Sopenharmony_ci	txq->skb[txq->tail] = skb;
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci	tx = txq->txd + txq->tail;
209662306a36Sopenharmony_ci	tx->data_rate = txdatarate;
209762306a36Sopenharmony_ci	tx->tx_priority = txpriority;
209862306a36Sopenharmony_ci	tx->qos_control = cpu_to_le16(qos);
209962306a36Sopenharmony_ci	tx->pkt_phys_addr = cpu_to_le32(dma);
210062306a36Sopenharmony_ci	tx->pkt_len = cpu_to_le16(skb->len);
210162306a36Sopenharmony_ci	tx->rate_info = 0;
210262306a36Sopenharmony_ci	if (!priv->ap_fw && sta != NULL)
210362306a36Sopenharmony_ci		tx->peer_id = MWL8K_STA(sta)->peer_id;
210462306a36Sopenharmony_ci	else
210562306a36Sopenharmony_ci		tx->peer_id = 0;
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci	if (priv->ap_fw && ieee80211_is_data(wh->frame_control) && !eapol_frame)
210862306a36Sopenharmony_ci		tx->timestamp = cpu_to_le32(ioread32(priv->regs +
210962306a36Sopenharmony_ci						MWL8K_HW_TIMER_REGISTER));
211062306a36Sopenharmony_ci	else
211162306a36Sopenharmony_ci		tx->timestamp = 0;
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	wmb();
211462306a36Sopenharmony_ci	tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus);
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	txq->len++;
211762306a36Sopenharmony_ci	priv->pending_tx_pkts++;
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci	txq->tail++;
212062306a36Sopenharmony_ci	if (txq->tail == MWL8K_TX_DESCS)
212162306a36Sopenharmony_ci		txq->tail = 0;
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci	mwl8k_tx_start(priv);
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	spin_unlock_bh(&priv->tx_lock);
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci	/* Initiate the ampdu session here */
212862306a36Sopenharmony_ci	if (start_ba_session) {
212962306a36Sopenharmony_ci		spin_lock(&priv->stream_lock);
213062306a36Sopenharmony_ci		if (mwl8k_start_stream(hw, stream))
213162306a36Sopenharmony_ci			mwl8k_remove_stream(hw, stream);
213262306a36Sopenharmony_ci		spin_unlock(&priv->stream_lock);
213362306a36Sopenharmony_ci	}
213462306a36Sopenharmony_ci}
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci/*
213862306a36Sopenharmony_ci * Firmware access.
213962306a36Sopenharmony_ci *
214062306a36Sopenharmony_ci * We have the following requirements for issuing firmware commands:
214162306a36Sopenharmony_ci * - Some commands require that the packet transmit path is idle when
214262306a36Sopenharmony_ci *   the command is issued.  (For simplicity, we'll just quiesce the
214362306a36Sopenharmony_ci *   transmit path for every command.)
214462306a36Sopenharmony_ci * - There are certain sequences of commands that need to be issued to
214562306a36Sopenharmony_ci *   the hardware sequentially, with no other intervening commands.
214662306a36Sopenharmony_ci *
214762306a36Sopenharmony_ci * This leads to an implementation of a "firmware lock" as a mutex that
214862306a36Sopenharmony_ci * can be taken recursively, and which is taken by both the low-level
214962306a36Sopenharmony_ci * command submission function (mwl8k_post_cmd) as well as any users of
215062306a36Sopenharmony_ci * that function that require issuing of an atomic sequence of commands,
215162306a36Sopenharmony_ci * and quiesces the transmit path whenever it's taken.
215262306a36Sopenharmony_ci */
215362306a36Sopenharmony_cistatic int mwl8k_fw_lock(struct ieee80211_hw *hw)
215462306a36Sopenharmony_ci{
215562306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci	if (priv->fw_mutex_owner != current) {
215862306a36Sopenharmony_ci		int rc;
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci		mutex_lock(&priv->fw_mutex);
216162306a36Sopenharmony_ci		ieee80211_stop_queues(hw);
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci		rc = mwl8k_tx_wait_empty(hw);
216462306a36Sopenharmony_ci		if (rc) {
216562306a36Sopenharmony_ci			if (!priv->hw_restart_in_progress)
216662306a36Sopenharmony_ci				ieee80211_wake_queues(hw);
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci			mutex_unlock(&priv->fw_mutex);
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci			return rc;
217162306a36Sopenharmony_ci		}
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci		priv->fw_mutex_owner = current;
217462306a36Sopenharmony_ci	}
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	priv->fw_mutex_depth++;
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	return 0;
217962306a36Sopenharmony_ci}
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_cistatic void mwl8k_fw_unlock(struct ieee80211_hw *hw)
218262306a36Sopenharmony_ci{
218362306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci	if (!--priv->fw_mutex_depth) {
218662306a36Sopenharmony_ci		if (!priv->hw_restart_in_progress)
218762306a36Sopenharmony_ci			ieee80211_wake_queues(hw);
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci		priv->fw_mutex_owner = NULL;
219062306a36Sopenharmony_ci		mutex_unlock(&priv->fw_mutex);
219162306a36Sopenharmony_ci	}
219262306a36Sopenharmony_ci}
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_cistatic void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable,
219562306a36Sopenharmony_ci			       u32 bitmap);
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci/*
219862306a36Sopenharmony_ci * Command processing.
219962306a36Sopenharmony_ci */
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci/* Timeout firmware commands after 10s */
220262306a36Sopenharmony_ci#define MWL8K_CMD_TIMEOUT_MS	10000
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_cistatic int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
220562306a36Sopenharmony_ci{
220662306a36Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(cmd_wait);
220762306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
220862306a36Sopenharmony_ci	void __iomem *regs = priv->regs;
220962306a36Sopenharmony_ci	dma_addr_t dma_addr;
221062306a36Sopenharmony_ci	unsigned int dma_size;
221162306a36Sopenharmony_ci	int rc;
221262306a36Sopenharmony_ci	unsigned long timeout = 0;
221362306a36Sopenharmony_ci	u8 buf[32];
221462306a36Sopenharmony_ci	u32 bitmap = 0;
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	wiphy_dbg(hw->wiphy, "Posting %s [%d]\n",
221762306a36Sopenharmony_ci		  mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), cmd->macid);
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	/* Before posting firmware commands that could change the hardware
222062306a36Sopenharmony_ci	 * characteristics, make sure that all BSSes are stopped temporary.
222162306a36Sopenharmony_ci	 * Enable these stopped BSSes after completion of the commands
222262306a36Sopenharmony_ci	 */
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci	rc = mwl8k_fw_lock(hw);
222562306a36Sopenharmony_ci	if (rc)
222662306a36Sopenharmony_ci		return rc;
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci	if (priv->ap_fw && priv->running_bsses) {
222962306a36Sopenharmony_ci		switch (le16_to_cpu(cmd->code)) {
223062306a36Sopenharmony_ci		case MWL8K_CMD_SET_RF_CHANNEL:
223162306a36Sopenharmony_ci		case MWL8K_CMD_RADIO_CONTROL:
223262306a36Sopenharmony_ci		case MWL8K_CMD_RF_TX_POWER:
223362306a36Sopenharmony_ci		case MWL8K_CMD_TX_POWER:
223462306a36Sopenharmony_ci		case MWL8K_CMD_RF_ANTENNA:
223562306a36Sopenharmony_ci		case MWL8K_CMD_RTS_THRESHOLD:
223662306a36Sopenharmony_ci		case MWL8K_CMD_MIMO_CONFIG:
223762306a36Sopenharmony_ci			bitmap = priv->running_bsses;
223862306a36Sopenharmony_ci			mwl8k_enable_bsses(hw, false, bitmap);
223962306a36Sopenharmony_ci			break;
224062306a36Sopenharmony_ci		}
224162306a36Sopenharmony_ci	}
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	cmd->result = (__force __le16) 0xffff;
224462306a36Sopenharmony_ci	dma_size = le16_to_cpu(cmd->length);
224562306a36Sopenharmony_ci	dma_addr = dma_map_single(&priv->pdev->dev, cmd, dma_size,
224662306a36Sopenharmony_ci				  DMA_BIDIRECTIONAL);
224762306a36Sopenharmony_ci	if (dma_mapping_error(&priv->pdev->dev, dma_addr)) {
224862306a36Sopenharmony_ci		rc = -ENOMEM;
224962306a36Sopenharmony_ci		goto exit;
225062306a36Sopenharmony_ci	}
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci	priv->hostcmd_wait = &cmd_wait;
225362306a36Sopenharmony_ci	iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR);
225462306a36Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_DOORBELL,
225562306a36Sopenharmony_ci		regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
225662306a36Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_DUMMY,
225762306a36Sopenharmony_ci		regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	timeout = wait_for_completion_timeout(&cmd_wait,
226062306a36Sopenharmony_ci				msecs_to_jiffies(MWL8K_CMD_TIMEOUT_MS));
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	priv->hostcmd_wait = NULL;
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci	dma_unmap_single(&priv->pdev->dev, dma_addr, dma_size,
226662306a36Sopenharmony_ci			 DMA_BIDIRECTIONAL);
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	if (!timeout) {
226962306a36Sopenharmony_ci		wiphy_err(hw->wiphy, "Command %s timeout after %u ms\n",
227062306a36Sopenharmony_ci			  mwl8k_cmd_name(cmd->code, buf, sizeof(buf)),
227162306a36Sopenharmony_ci			  MWL8K_CMD_TIMEOUT_MS);
227262306a36Sopenharmony_ci		rc = -ETIMEDOUT;
227362306a36Sopenharmony_ci	} else {
227462306a36Sopenharmony_ci		int ms;
227562306a36Sopenharmony_ci
227662306a36Sopenharmony_ci		ms = MWL8K_CMD_TIMEOUT_MS - jiffies_to_msecs(timeout);
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci		rc = cmd->result ? -EINVAL : 0;
227962306a36Sopenharmony_ci		if (rc)
228062306a36Sopenharmony_ci			wiphy_err(hw->wiphy, "Command %s error 0x%x\n",
228162306a36Sopenharmony_ci				  mwl8k_cmd_name(cmd->code, buf, sizeof(buf)),
228262306a36Sopenharmony_ci				  le16_to_cpu(cmd->result));
228362306a36Sopenharmony_ci		else if (ms > 2000)
228462306a36Sopenharmony_ci			wiphy_notice(hw->wiphy, "Command %s took %d ms\n",
228562306a36Sopenharmony_ci				     mwl8k_cmd_name(cmd->code,
228662306a36Sopenharmony_ci						    buf, sizeof(buf)),
228762306a36Sopenharmony_ci				     ms);
228862306a36Sopenharmony_ci	}
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ciexit:
229162306a36Sopenharmony_ci	if (bitmap)
229262306a36Sopenharmony_ci		mwl8k_enable_bsses(hw, true, bitmap);
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	mwl8k_fw_unlock(hw);
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	return rc;
229762306a36Sopenharmony_ci}
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_cistatic int mwl8k_post_pervif_cmd(struct ieee80211_hw *hw,
230062306a36Sopenharmony_ci				 struct ieee80211_vif *vif,
230162306a36Sopenharmony_ci				 struct mwl8k_cmd_pkt *cmd)
230262306a36Sopenharmony_ci{
230362306a36Sopenharmony_ci	if (vif != NULL)
230462306a36Sopenharmony_ci		cmd->macid = MWL8K_VIF(vif)->macid;
230562306a36Sopenharmony_ci	return mwl8k_post_cmd(hw, cmd);
230662306a36Sopenharmony_ci}
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci/*
230962306a36Sopenharmony_ci * Setup code shared between STA and AP firmware images.
231062306a36Sopenharmony_ci */
231162306a36Sopenharmony_cistatic void mwl8k_setup_2ghz_band(struct ieee80211_hw *hw)
231262306a36Sopenharmony_ci{
231362306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(priv->channels_24) != sizeof(mwl8k_channels_24));
231662306a36Sopenharmony_ci	memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24));
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24));
231962306a36Sopenharmony_ci	memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24));
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	priv->band_24.band = NL80211_BAND_2GHZ;
232262306a36Sopenharmony_ci	priv->band_24.channels = priv->channels_24;
232362306a36Sopenharmony_ci	priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24);
232462306a36Sopenharmony_ci	priv->band_24.bitrates = priv->rates_24;
232562306a36Sopenharmony_ci	priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24);
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci	hw->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band_24;
232862306a36Sopenharmony_ci}
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_cistatic void mwl8k_setup_5ghz_band(struct ieee80211_hw *hw)
233162306a36Sopenharmony_ci{
233262306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(priv->channels_50) != sizeof(mwl8k_channels_50));
233562306a36Sopenharmony_ci	memcpy(priv->channels_50, mwl8k_channels_50, sizeof(mwl8k_channels_50));
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl8k_rates_50));
233862306a36Sopenharmony_ci	memcpy(priv->rates_50, mwl8k_rates_50, sizeof(mwl8k_rates_50));
233962306a36Sopenharmony_ci
234062306a36Sopenharmony_ci	priv->band_50.band = NL80211_BAND_5GHZ;
234162306a36Sopenharmony_ci	priv->band_50.channels = priv->channels_50;
234262306a36Sopenharmony_ci	priv->band_50.n_channels = ARRAY_SIZE(mwl8k_channels_50);
234362306a36Sopenharmony_ci	priv->band_50.bitrates = priv->rates_50;
234462306a36Sopenharmony_ci	priv->band_50.n_bitrates = ARRAY_SIZE(mwl8k_rates_50);
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci	hw->wiphy->bands[NL80211_BAND_5GHZ] = &priv->band_50;
234762306a36Sopenharmony_ci}
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_ci/*
235062306a36Sopenharmony_ci * CMD_GET_HW_SPEC (STA version).
235162306a36Sopenharmony_ci */
235262306a36Sopenharmony_cistruct mwl8k_cmd_get_hw_spec_sta {
235362306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
235462306a36Sopenharmony_ci	__u8 hw_rev;
235562306a36Sopenharmony_ci	__u8 host_interface;
235662306a36Sopenharmony_ci	__le16 num_mcaddrs;
235762306a36Sopenharmony_ci	__u8 perm_addr[ETH_ALEN];
235862306a36Sopenharmony_ci	__le16 region_code;
235962306a36Sopenharmony_ci	__le32 fw_rev;
236062306a36Sopenharmony_ci	__le32 ps_cookie;
236162306a36Sopenharmony_ci	__le32 caps;
236262306a36Sopenharmony_ci	__u8 mcs_bitmap[16];
236362306a36Sopenharmony_ci	__le32 rx_queue_ptr;
236462306a36Sopenharmony_ci	__le32 num_tx_queues;
236562306a36Sopenharmony_ci	__le32 tx_queue_ptrs[MWL8K_TX_WMM_QUEUES];
236662306a36Sopenharmony_ci	__le32 caps2;
236762306a36Sopenharmony_ci	__le32 num_tx_desc_per_queue;
236862306a36Sopenharmony_ci	__le32 total_rxd;
236962306a36Sopenharmony_ci} __packed;
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci#define MWL8K_CAP_MAX_AMSDU		0x20000000
237262306a36Sopenharmony_ci#define MWL8K_CAP_GREENFIELD		0x08000000
237362306a36Sopenharmony_ci#define MWL8K_CAP_AMPDU			0x04000000
237462306a36Sopenharmony_ci#define MWL8K_CAP_RX_STBC		0x01000000
237562306a36Sopenharmony_ci#define MWL8K_CAP_TX_STBC		0x00800000
237662306a36Sopenharmony_ci#define MWL8K_CAP_SHORTGI_40MHZ		0x00400000
237762306a36Sopenharmony_ci#define MWL8K_CAP_SHORTGI_20MHZ		0x00200000
237862306a36Sopenharmony_ci#define MWL8K_CAP_RX_ANTENNA_MASK	0x000e0000
237962306a36Sopenharmony_ci#define MWL8K_CAP_TX_ANTENNA_MASK	0x0001c000
238062306a36Sopenharmony_ci#define MWL8K_CAP_DELAY_BA		0x00003000
238162306a36Sopenharmony_ci#define MWL8K_CAP_MIMO			0x00000200
238262306a36Sopenharmony_ci#define MWL8K_CAP_40MHZ			0x00000100
238362306a36Sopenharmony_ci#define MWL8K_CAP_BAND_MASK		0x00000007
238462306a36Sopenharmony_ci#define MWL8K_CAP_5GHZ			0x00000004
238562306a36Sopenharmony_ci#define MWL8K_CAP_2GHZ4			0x00000001
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_cistatic void
238862306a36Sopenharmony_cimwl8k_set_ht_caps(struct ieee80211_hw *hw,
238962306a36Sopenharmony_ci		  struct ieee80211_supported_band *band, u32 cap)
239062306a36Sopenharmony_ci{
239162306a36Sopenharmony_ci	int rx_streams;
239262306a36Sopenharmony_ci	int tx_streams;
239362306a36Sopenharmony_ci
239462306a36Sopenharmony_ci	band->ht_cap.ht_supported = 1;
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_ci	if (cap & MWL8K_CAP_MAX_AMSDU)
239762306a36Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
239862306a36Sopenharmony_ci	if (cap & MWL8K_CAP_GREENFIELD)
239962306a36Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD;
240062306a36Sopenharmony_ci	if (cap & MWL8K_CAP_AMPDU) {
240162306a36Sopenharmony_ci		ieee80211_hw_set(hw, AMPDU_AGGREGATION);
240262306a36Sopenharmony_ci		band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
240362306a36Sopenharmony_ci		band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
240462306a36Sopenharmony_ci	}
240562306a36Sopenharmony_ci	if (cap & MWL8K_CAP_RX_STBC)
240662306a36Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC;
240762306a36Sopenharmony_ci	if (cap & MWL8K_CAP_TX_STBC)
240862306a36Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
240962306a36Sopenharmony_ci	if (cap & MWL8K_CAP_SHORTGI_40MHZ)
241062306a36Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
241162306a36Sopenharmony_ci	if (cap & MWL8K_CAP_SHORTGI_20MHZ)
241262306a36Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
241362306a36Sopenharmony_ci	if (cap & MWL8K_CAP_DELAY_BA)
241462306a36Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA;
241562306a36Sopenharmony_ci	if (cap & MWL8K_CAP_40MHZ)
241662306a36Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	rx_streams = hweight32(cap & MWL8K_CAP_RX_ANTENNA_MASK);
241962306a36Sopenharmony_ci	tx_streams = hweight32(cap & MWL8K_CAP_TX_ANTENNA_MASK);
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci	band->ht_cap.mcs.rx_mask[0] = 0xff;
242262306a36Sopenharmony_ci	if (rx_streams >= 2)
242362306a36Sopenharmony_ci		band->ht_cap.mcs.rx_mask[1] = 0xff;
242462306a36Sopenharmony_ci	if (rx_streams >= 3)
242562306a36Sopenharmony_ci		band->ht_cap.mcs.rx_mask[2] = 0xff;
242662306a36Sopenharmony_ci	band->ht_cap.mcs.rx_mask[4] = 0x01;
242762306a36Sopenharmony_ci	band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
242862306a36Sopenharmony_ci
242962306a36Sopenharmony_ci	if (rx_streams != tx_streams) {
243062306a36Sopenharmony_ci		band->ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
243162306a36Sopenharmony_ci		band->ht_cap.mcs.tx_params |= (tx_streams - 1) <<
243262306a36Sopenharmony_ci				IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
243362306a36Sopenharmony_ci	}
243462306a36Sopenharmony_ci}
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_cistatic void
243762306a36Sopenharmony_cimwl8k_set_caps(struct ieee80211_hw *hw, u32 caps)
243862306a36Sopenharmony_ci{
243962306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	if (priv->caps)
244262306a36Sopenharmony_ci		return;
244362306a36Sopenharmony_ci
244462306a36Sopenharmony_ci	if ((caps & MWL8K_CAP_2GHZ4) || !(caps & MWL8K_CAP_BAND_MASK)) {
244562306a36Sopenharmony_ci		mwl8k_setup_2ghz_band(hw);
244662306a36Sopenharmony_ci		if (caps & MWL8K_CAP_MIMO)
244762306a36Sopenharmony_ci			mwl8k_set_ht_caps(hw, &priv->band_24, caps);
244862306a36Sopenharmony_ci	}
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	if (caps & MWL8K_CAP_5GHZ) {
245162306a36Sopenharmony_ci		mwl8k_setup_5ghz_band(hw);
245262306a36Sopenharmony_ci		if (caps & MWL8K_CAP_MIMO)
245362306a36Sopenharmony_ci			mwl8k_set_ht_caps(hw, &priv->band_50, caps);
245462306a36Sopenharmony_ci	}
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci	priv->caps = caps;
245762306a36Sopenharmony_ci}
245862306a36Sopenharmony_ci
245962306a36Sopenharmony_cistatic int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw)
246062306a36Sopenharmony_ci{
246162306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
246262306a36Sopenharmony_ci	struct mwl8k_cmd_get_hw_spec_sta *cmd;
246362306a36Sopenharmony_ci	int rc;
246462306a36Sopenharmony_ci	int i;
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
246762306a36Sopenharmony_ci	if (cmd == NULL)
246862306a36Sopenharmony_ci		return -ENOMEM;
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC);
247162306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci	memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr));
247462306a36Sopenharmony_ci	cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
247562306a36Sopenharmony_ci	cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma);
247662306a36Sopenharmony_ci	cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv));
247762306a36Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
247862306a36Sopenharmony_ci		cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma);
247962306a36Sopenharmony_ci	cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);
248062306a36Sopenharmony_ci	cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS);
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
248362306a36Sopenharmony_ci
248462306a36Sopenharmony_ci	if (!rc) {
248562306a36Sopenharmony_ci		SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr);
248662306a36Sopenharmony_ci		priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
248762306a36Sopenharmony_ci		priv->fw_rev = le32_to_cpu(cmd->fw_rev);
248862306a36Sopenharmony_ci		priv->hw_rev = cmd->hw_rev;
248962306a36Sopenharmony_ci		mwl8k_set_caps(hw, le32_to_cpu(cmd->caps));
249062306a36Sopenharmony_ci		priv->ap_macids_supported = 0x00000000;
249162306a36Sopenharmony_ci		priv->sta_macids_supported = 0x00000001;
249262306a36Sopenharmony_ci	}
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ci	kfree(cmd);
249562306a36Sopenharmony_ci	return rc;
249662306a36Sopenharmony_ci}
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci/*
249962306a36Sopenharmony_ci * CMD_GET_HW_SPEC (AP version).
250062306a36Sopenharmony_ci */
250162306a36Sopenharmony_cistruct mwl8k_cmd_get_hw_spec_ap {
250262306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
250362306a36Sopenharmony_ci	__u8 hw_rev;
250462306a36Sopenharmony_ci	__u8 host_interface;
250562306a36Sopenharmony_ci	__le16 num_wcb;
250662306a36Sopenharmony_ci	__le16 num_mcaddrs;
250762306a36Sopenharmony_ci	__u8 perm_addr[ETH_ALEN];
250862306a36Sopenharmony_ci	__le16 region_code;
250962306a36Sopenharmony_ci	__le16 num_antenna;
251062306a36Sopenharmony_ci	__le32 fw_rev;
251162306a36Sopenharmony_ci	__le32 wcbbase0;
251262306a36Sopenharmony_ci	__le32 rxwrptr;
251362306a36Sopenharmony_ci	__le32 rxrdptr;
251462306a36Sopenharmony_ci	__le32 ps_cookie;
251562306a36Sopenharmony_ci	__le32 wcbbase1;
251662306a36Sopenharmony_ci	__le32 wcbbase2;
251762306a36Sopenharmony_ci	__le32 wcbbase3;
251862306a36Sopenharmony_ci	__le32 fw_api_version;
251962306a36Sopenharmony_ci	__le32 caps;
252062306a36Sopenharmony_ci	__le32 num_of_ampdu_queues;
252162306a36Sopenharmony_ci	__le32 wcbbase_ampdu[MWL8K_MAX_AMPDU_QUEUES];
252262306a36Sopenharmony_ci} __packed;
252362306a36Sopenharmony_ci
252462306a36Sopenharmony_cistatic int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw)
252562306a36Sopenharmony_ci{
252662306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
252762306a36Sopenharmony_ci	struct mwl8k_cmd_get_hw_spec_ap *cmd;
252862306a36Sopenharmony_ci	int rc, i;
252962306a36Sopenharmony_ci	u32 api_version;
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
253262306a36Sopenharmony_ci	if (cmd == NULL)
253362306a36Sopenharmony_ci		return -ENOMEM;
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC);
253662306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr));
253962306a36Sopenharmony_ci	cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
254262306a36Sopenharmony_ci
254362306a36Sopenharmony_ci	if (!rc) {
254462306a36Sopenharmony_ci		int off;
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci		api_version = le32_to_cpu(cmd->fw_api_version);
254762306a36Sopenharmony_ci		if (priv->device_info->fw_api_ap != api_version) {
254862306a36Sopenharmony_ci			printk(KERN_ERR "%s: Unsupported fw API version for %s."
254962306a36Sopenharmony_ci			       "  Expected %d got %d.\n", MWL8K_NAME,
255062306a36Sopenharmony_ci			       priv->device_info->part_name,
255162306a36Sopenharmony_ci			       priv->device_info->fw_api_ap,
255262306a36Sopenharmony_ci			       api_version);
255362306a36Sopenharmony_ci			rc = -EINVAL;
255462306a36Sopenharmony_ci			goto done;
255562306a36Sopenharmony_ci		}
255662306a36Sopenharmony_ci		SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr);
255762306a36Sopenharmony_ci		priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
255862306a36Sopenharmony_ci		priv->fw_rev = le32_to_cpu(cmd->fw_rev);
255962306a36Sopenharmony_ci		priv->hw_rev = cmd->hw_rev;
256062306a36Sopenharmony_ci		mwl8k_set_caps(hw, le32_to_cpu(cmd->caps));
256162306a36Sopenharmony_ci		priv->ap_macids_supported = 0x000000ff;
256262306a36Sopenharmony_ci		priv->sta_macids_supported = 0x00000100;
256362306a36Sopenharmony_ci		priv->num_ampdu_queues = le32_to_cpu(cmd->num_of_ampdu_queues);
256462306a36Sopenharmony_ci		if (priv->num_ampdu_queues > MWL8K_MAX_AMPDU_QUEUES) {
256562306a36Sopenharmony_ci			wiphy_warn(hw->wiphy, "fw reported %d ampdu queues"
256662306a36Sopenharmony_ci				   " but we only support %d.\n",
256762306a36Sopenharmony_ci				   priv->num_ampdu_queues,
256862306a36Sopenharmony_ci				   MWL8K_MAX_AMPDU_QUEUES);
256962306a36Sopenharmony_ci			priv->num_ampdu_queues = MWL8K_MAX_AMPDU_QUEUES;
257062306a36Sopenharmony_ci		}
257162306a36Sopenharmony_ci		off = le32_to_cpu(cmd->rxwrptr) & 0xffff;
257262306a36Sopenharmony_ci		iowrite32(priv->rxq[0].rxd_dma, priv->sram + off);
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci		off = le32_to_cpu(cmd->rxrdptr) & 0xffff;
257562306a36Sopenharmony_ci		iowrite32(priv->rxq[0].rxd_dma, priv->sram + off);
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci		priv->txq_offset[0] = le32_to_cpu(cmd->wcbbase0) & 0xffff;
257862306a36Sopenharmony_ci		priv->txq_offset[1] = le32_to_cpu(cmd->wcbbase1) & 0xffff;
257962306a36Sopenharmony_ci		priv->txq_offset[2] = le32_to_cpu(cmd->wcbbase2) & 0xffff;
258062306a36Sopenharmony_ci		priv->txq_offset[3] = le32_to_cpu(cmd->wcbbase3) & 0xffff;
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci		for (i = 0; i < priv->num_ampdu_queues; i++)
258362306a36Sopenharmony_ci			priv->txq_offset[i + MWL8K_TX_WMM_QUEUES] =
258462306a36Sopenharmony_ci				le32_to_cpu(cmd->wcbbase_ampdu[i]) & 0xffff;
258562306a36Sopenharmony_ci	}
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_cidone:
258862306a36Sopenharmony_ci	kfree(cmd);
258962306a36Sopenharmony_ci	return rc;
259062306a36Sopenharmony_ci}
259162306a36Sopenharmony_ci
259262306a36Sopenharmony_ci/*
259362306a36Sopenharmony_ci * CMD_SET_HW_SPEC.
259462306a36Sopenharmony_ci */
259562306a36Sopenharmony_cistruct mwl8k_cmd_set_hw_spec {
259662306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
259762306a36Sopenharmony_ci	__u8 hw_rev;
259862306a36Sopenharmony_ci	__u8 host_interface;
259962306a36Sopenharmony_ci	__le16 num_mcaddrs;
260062306a36Sopenharmony_ci	__u8 perm_addr[ETH_ALEN];
260162306a36Sopenharmony_ci	__le16 region_code;
260262306a36Sopenharmony_ci	__le32 fw_rev;
260362306a36Sopenharmony_ci	__le32 ps_cookie;
260462306a36Sopenharmony_ci	__le32 caps;
260562306a36Sopenharmony_ci	__le32 rx_queue_ptr;
260662306a36Sopenharmony_ci	__le32 num_tx_queues;
260762306a36Sopenharmony_ci	__le32 tx_queue_ptrs[MWL8K_MAX_TX_QUEUES];
260862306a36Sopenharmony_ci	__le32 flags;
260962306a36Sopenharmony_ci	__le32 num_tx_desc_per_queue;
261062306a36Sopenharmony_ci	__le32 total_rxd;
261162306a36Sopenharmony_ci} __packed;
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci/* If enabled, MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY will cause
261462306a36Sopenharmony_ci * packets to expire 500 ms after the timestamp in the tx descriptor.  That is,
261562306a36Sopenharmony_ci * the packets that are queued for more than 500ms, will be dropped in the
261662306a36Sopenharmony_ci * hardware. This helps minimizing the issues caused due to head-of-line
261762306a36Sopenharmony_ci * blocking where a slow client can hog the bandwidth and affect traffic to a
261862306a36Sopenharmony_ci * faster client.
261962306a36Sopenharmony_ci */
262062306a36Sopenharmony_ci#define MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY	0x00000400
262162306a36Sopenharmony_ci#define MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR	0x00000200
262262306a36Sopenharmony_ci#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT		0x00000080
262362306a36Sopenharmony_ci#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP	0x00000020
262462306a36Sopenharmony_ci#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON		0x00000010
262562306a36Sopenharmony_ci
262662306a36Sopenharmony_cistatic int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw)
262762306a36Sopenharmony_ci{
262862306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
262962306a36Sopenharmony_ci	struct mwl8k_cmd_set_hw_spec *cmd;
263062306a36Sopenharmony_ci	int rc;
263162306a36Sopenharmony_ci	int i;
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
263462306a36Sopenharmony_ci	if (cmd == NULL)
263562306a36Sopenharmony_ci		return -ENOMEM;
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_HW_SPEC);
263862306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
263962306a36Sopenharmony_ci
264062306a36Sopenharmony_ci	cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
264162306a36Sopenharmony_ci	cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma);
264262306a36Sopenharmony_ci	cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv));
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci	/*
264562306a36Sopenharmony_ci	 * Mac80211 stack has Q0 as highest priority and Q3 as lowest in
264662306a36Sopenharmony_ci	 * that order. Firmware has Q3 as highest priority and Q0 as lowest
264762306a36Sopenharmony_ci	 * in that order. Map Q3 of mac80211 to Q0 of firmware so that the
264862306a36Sopenharmony_ci	 * priority is interpreted the right way in firmware.
264962306a36Sopenharmony_ci	 */
265062306a36Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++) {
265162306a36Sopenharmony_ci		int j = mwl8k_tx_queues(priv) - 1 - i;
265262306a36Sopenharmony_ci		cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[j].txd_dma);
265362306a36Sopenharmony_ci	}
265462306a36Sopenharmony_ci
265562306a36Sopenharmony_ci	cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT |
265662306a36Sopenharmony_ci				 MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP |
265762306a36Sopenharmony_ci				 MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON |
265862306a36Sopenharmony_ci				 MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY |
265962306a36Sopenharmony_ci				 MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR);
266062306a36Sopenharmony_ci	cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);
266162306a36Sopenharmony_ci	cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS);
266262306a36Sopenharmony_ci
266362306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
266462306a36Sopenharmony_ci	kfree(cmd);
266562306a36Sopenharmony_ci
266662306a36Sopenharmony_ci	return rc;
266762306a36Sopenharmony_ci}
266862306a36Sopenharmony_ci
266962306a36Sopenharmony_ci/*
267062306a36Sopenharmony_ci * CMD_MAC_MULTICAST_ADR.
267162306a36Sopenharmony_ci */
267262306a36Sopenharmony_cistruct mwl8k_cmd_mac_multicast_adr {
267362306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
267462306a36Sopenharmony_ci	__le16 action;
267562306a36Sopenharmony_ci	__le16 numaddr;
267662306a36Sopenharmony_ci	__u8 addr[][ETH_ALEN];
267762306a36Sopenharmony_ci};
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci#define MWL8K_ENABLE_RX_DIRECTED	0x0001
268062306a36Sopenharmony_ci#define MWL8K_ENABLE_RX_MULTICAST	0x0002
268162306a36Sopenharmony_ci#define MWL8K_ENABLE_RX_ALL_MULTICAST	0x0004
268262306a36Sopenharmony_ci#define MWL8K_ENABLE_RX_BROADCAST	0x0008
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_cistatic struct mwl8k_cmd_pkt *
268562306a36Sopenharmony_ci__mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti,
268662306a36Sopenharmony_ci			      struct netdev_hw_addr_list *mc_list)
268762306a36Sopenharmony_ci{
268862306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
268962306a36Sopenharmony_ci	struct mwl8k_cmd_mac_multicast_adr *cmd;
269062306a36Sopenharmony_ci	int size;
269162306a36Sopenharmony_ci	int mc_count = 0;
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_ci	if (mc_list)
269462306a36Sopenharmony_ci		mc_count = netdev_hw_addr_list_count(mc_list);
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci	if (allmulti || mc_count > priv->num_mcaddrs) {
269762306a36Sopenharmony_ci		allmulti = 1;
269862306a36Sopenharmony_ci		mc_count = 0;
269962306a36Sopenharmony_ci	}
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci	size = sizeof(*cmd) + mc_count * ETH_ALEN;
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci	cmd = kzalloc(size, GFP_ATOMIC);
270462306a36Sopenharmony_ci	if (cmd == NULL)
270562306a36Sopenharmony_ci		return NULL;
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_MAC_MULTICAST_ADR);
270862306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(size);
270962306a36Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_ENABLE_RX_DIRECTED |
271062306a36Sopenharmony_ci				  MWL8K_ENABLE_RX_BROADCAST);
271162306a36Sopenharmony_ci
271262306a36Sopenharmony_ci	if (allmulti) {
271362306a36Sopenharmony_ci		cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_ALL_MULTICAST);
271462306a36Sopenharmony_ci	} else if (mc_count) {
271562306a36Sopenharmony_ci		struct netdev_hw_addr *ha;
271662306a36Sopenharmony_ci		int i = 0;
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci		cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST);
271962306a36Sopenharmony_ci		cmd->numaddr = cpu_to_le16(mc_count);
272062306a36Sopenharmony_ci		netdev_hw_addr_list_for_each(ha, mc_list) {
272162306a36Sopenharmony_ci			memcpy(cmd->addr[i], ha->addr, ETH_ALEN);
272262306a36Sopenharmony_ci		}
272362306a36Sopenharmony_ci	}
272462306a36Sopenharmony_ci
272562306a36Sopenharmony_ci	return &cmd->header;
272662306a36Sopenharmony_ci}
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci/*
272962306a36Sopenharmony_ci * CMD_GET_STAT.
273062306a36Sopenharmony_ci */
273162306a36Sopenharmony_cistruct mwl8k_cmd_get_stat {
273262306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
273362306a36Sopenharmony_ci	__le32 stats[64];
273462306a36Sopenharmony_ci} __packed;
273562306a36Sopenharmony_ci
273662306a36Sopenharmony_ci#define MWL8K_STAT_ACK_FAILURE	9
273762306a36Sopenharmony_ci#define MWL8K_STAT_RTS_FAILURE	12
273862306a36Sopenharmony_ci#define MWL8K_STAT_FCS_ERROR	24
273962306a36Sopenharmony_ci#define MWL8K_STAT_RTS_SUCCESS	11
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_cistatic int mwl8k_cmd_get_stat(struct ieee80211_hw *hw,
274262306a36Sopenharmony_ci			      struct ieee80211_low_level_stats *stats)
274362306a36Sopenharmony_ci{
274462306a36Sopenharmony_ci	struct mwl8k_cmd_get_stat *cmd;
274562306a36Sopenharmony_ci	int rc;
274662306a36Sopenharmony_ci
274762306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
274862306a36Sopenharmony_ci	if (cmd == NULL)
274962306a36Sopenharmony_ci		return -ENOMEM;
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_STAT);
275262306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
275362306a36Sopenharmony_ci
275462306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
275562306a36Sopenharmony_ci	if (!rc) {
275662306a36Sopenharmony_ci		stats->dot11ACKFailureCount =
275762306a36Sopenharmony_ci			le32_to_cpu(cmd->stats[MWL8K_STAT_ACK_FAILURE]);
275862306a36Sopenharmony_ci		stats->dot11RTSFailureCount =
275962306a36Sopenharmony_ci			le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_FAILURE]);
276062306a36Sopenharmony_ci		stats->dot11FCSErrorCount =
276162306a36Sopenharmony_ci			le32_to_cpu(cmd->stats[MWL8K_STAT_FCS_ERROR]);
276262306a36Sopenharmony_ci		stats->dot11RTSSuccessCount =
276362306a36Sopenharmony_ci			le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_SUCCESS]);
276462306a36Sopenharmony_ci	}
276562306a36Sopenharmony_ci	kfree(cmd);
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci	return rc;
276862306a36Sopenharmony_ci}
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci/*
277162306a36Sopenharmony_ci * CMD_RADIO_CONTROL.
277262306a36Sopenharmony_ci */
277362306a36Sopenharmony_cistruct mwl8k_cmd_radio_control {
277462306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
277562306a36Sopenharmony_ci	__le16 action;
277662306a36Sopenharmony_ci	__le16 control;
277762306a36Sopenharmony_ci	__le16 radio_on;
277862306a36Sopenharmony_ci} __packed;
277962306a36Sopenharmony_ci
278062306a36Sopenharmony_cistatic int
278162306a36Sopenharmony_cimwl8k_cmd_radio_control(struct ieee80211_hw *hw, bool enable, bool force)
278262306a36Sopenharmony_ci{
278362306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
278462306a36Sopenharmony_ci	struct mwl8k_cmd_radio_control *cmd;
278562306a36Sopenharmony_ci	int rc;
278662306a36Sopenharmony_ci
278762306a36Sopenharmony_ci	if (enable == priv->radio_on && !force)
278862306a36Sopenharmony_ci		return 0;
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
279162306a36Sopenharmony_ci	if (cmd == NULL)
279262306a36Sopenharmony_ci		return -ENOMEM;
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_RADIO_CONTROL);
279562306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
279662306a36Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
279762306a36Sopenharmony_ci	cmd->control = cpu_to_le16(priv->radio_short_preamble ? 3 : 1);
279862306a36Sopenharmony_ci	cmd->radio_on = cpu_to_le16(enable ? 0x0001 : 0x0000);
279962306a36Sopenharmony_ci
280062306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
280162306a36Sopenharmony_ci	kfree(cmd);
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_ci	if (!rc)
280462306a36Sopenharmony_ci		priv->radio_on = enable;
280562306a36Sopenharmony_ci
280662306a36Sopenharmony_ci	return rc;
280762306a36Sopenharmony_ci}
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_cistatic int mwl8k_cmd_radio_disable(struct ieee80211_hw *hw)
281062306a36Sopenharmony_ci{
281162306a36Sopenharmony_ci	return mwl8k_cmd_radio_control(hw, 0, 0);
281262306a36Sopenharmony_ci}
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_cistatic int mwl8k_cmd_radio_enable(struct ieee80211_hw *hw)
281562306a36Sopenharmony_ci{
281662306a36Sopenharmony_ci	return mwl8k_cmd_radio_control(hw, 1, 0);
281762306a36Sopenharmony_ci}
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_cistatic int
282062306a36Sopenharmony_cimwl8k_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble)
282162306a36Sopenharmony_ci{
282262306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
282362306a36Sopenharmony_ci
282462306a36Sopenharmony_ci	priv->radio_short_preamble = short_preamble;
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_ci	return mwl8k_cmd_radio_control(hw, 1, 1);
282762306a36Sopenharmony_ci}
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci/*
283062306a36Sopenharmony_ci * CMD_RF_TX_POWER.
283162306a36Sopenharmony_ci */
283262306a36Sopenharmony_ci#define MWL8K_RF_TX_POWER_LEVEL_TOTAL	8
283362306a36Sopenharmony_ci
283462306a36Sopenharmony_cistruct mwl8k_cmd_rf_tx_power {
283562306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
283662306a36Sopenharmony_ci	__le16 action;
283762306a36Sopenharmony_ci	__le16 support_level;
283862306a36Sopenharmony_ci	__le16 current_level;
283962306a36Sopenharmony_ci	__le16 reserved;
284062306a36Sopenharmony_ci	__le16 power_level_list[MWL8K_RF_TX_POWER_LEVEL_TOTAL];
284162306a36Sopenharmony_ci} __packed;
284262306a36Sopenharmony_ci
284362306a36Sopenharmony_cistatic int mwl8k_cmd_rf_tx_power(struct ieee80211_hw *hw, int dBm)
284462306a36Sopenharmony_ci{
284562306a36Sopenharmony_ci	struct mwl8k_cmd_rf_tx_power *cmd;
284662306a36Sopenharmony_ci	int rc;
284762306a36Sopenharmony_ci
284862306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
284962306a36Sopenharmony_ci	if (cmd == NULL)
285062306a36Sopenharmony_ci		return -ENOMEM;
285162306a36Sopenharmony_ci
285262306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_TX_POWER);
285362306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
285462306a36Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
285562306a36Sopenharmony_ci	cmd->support_level = cpu_to_le16(dBm);
285662306a36Sopenharmony_ci
285762306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
285862306a36Sopenharmony_ci	kfree(cmd);
285962306a36Sopenharmony_ci
286062306a36Sopenharmony_ci	return rc;
286162306a36Sopenharmony_ci}
286262306a36Sopenharmony_ci
286362306a36Sopenharmony_ci/*
286462306a36Sopenharmony_ci * CMD_TX_POWER.
286562306a36Sopenharmony_ci */
286662306a36Sopenharmony_ci#define MWL8K_TX_POWER_LEVEL_TOTAL      12
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_cistruct mwl8k_cmd_tx_power {
286962306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
287062306a36Sopenharmony_ci	__le16 action;
287162306a36Sopenharmony_ci	__le16 band;
287262306a36Sopenharmony_ci	__le16 channel;
287362306a36Sopenharmony_ci	__le16 bw;
287462306a36Sopenharmony_ci	__le16 sub_ch;
287562306a36Sopenharmony_ci	__le16 power_level_list[MWL8K_TX_POWER_LEVEL_TOTAL];
287662306a36Sopenharmony_ci} __packed;
287762306a36Sopenharmony_ci
287862306a36Sopenharmony_cistatic int mwl8k_cmd_tx_power(struct ieee80211_hw *hw,
287962306a36Sopenharmony_ci				     struct ieee80211_conf *conf,
288062306a36Sopenharmony_ci				     unsigned short pwr)
288162306a36Sopenharmony_ci{
288262306a36Sopenharmony_ci	struct ieee80211_channel *channel = conf->chandef.chan;
288362306a36Sopenharmony_ci	enum nl80211_channel_type channel_type =
288462306a36Sopenharmony_ci		cfg80211_get_chandef_type(&conf->chandef);
288562306a36Sopenharmony_ci	struct mwl8k_cmd_tx_power *cmd;
288662306a36Sopenharmony_ci	int rc;
288762306a36Sopenharmony_ci	int i;
288862306a36Sopenharmony_ci
288962306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
289062306a36Sopenharmony_ci	if (cmd == NULL)
289162306a36Sopenharmony_ci		return -ENOMEM;
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_TX_POWER);
289462306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
289562306a36Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_CMD_SET_LIST);
289662306a36Sopenharmony_ci
289762306a36Sopenharmony_ci	if (channel->band == NL80211_BAND_2GHZ)
289862306a36Sopenharmony_ci		cmd->band = cpu_to_le16(0x1);
289962306a36Sopenharmony_ci	else if (channel->band == NL80211_BAND_5GHZ)
290062306a36Sopenharmony_ci		cmd->band = cpu_to_le16(0x4);
290162306a36Sopenharmony_ci
290262306a36Sopenharmony_ci	cmd->channel = cpu_to_le16(channel->hw_value);
290362306a36Sopenharmony_ci
290462306a36Sopenharmony_ci	if (channel_type == NL80211_CHAN_NO_HT ||
290562306a36Sopenharmony_ci	    channel_type == NL80211_CHAN_HT20) {
290662306a36Sopenharmony_ci		cmd->bw = cpu_to_le16(0x2);
290762306a36Sopenharmony_ci	} else {
290862306a36Sopenharmony_ci		cmd->bw = cpu_to_le16(0x4);
290962306a36Sopenharmony_ci		if (channel_type == NL80211_CHAN_HT40MINUS)
291062306a36Sopenharmony_ci			cmd->sub_ch = cpu_to_le16(0x3);
291162306a36Sopenharmony_ci		else if (channel_type == NL80211_CHAN_HT40PLUS)
291262306a36Sopenharmony_ci			cmd->sub_ch = cpu_to_le16(0x1);
291362306a36Sopenharmony_ci	}
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_ci	for (i = 0; i < MWL8K_TX_POWER_LEVEL_TOTAL; i++)
291662306a36Sopenharmony_ci		cmd->power_level_list[i] = cpu_to_le16(pwr);
291762306a36Sopenharmony_ci
291862306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
291962306a36Sopenharmony_ci	kfree(cmd);
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_ci	return rc;
292262306a36Sopenharmony_ci}
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_ci/*
292562306a36Sopenharmony_ci * CMD_RF_ANTENNA.
292662306a36Sopenharmony_ci */
292762306a36Sopenharmony_cistruct mwl8k_cmd_rf_antenna {
292862306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
292962306a36Sopenharmony_ci	__le16 antenna;
293062306a36Sopenharmony_ci	__le16 mode;
293162306a36Sopenharmony_ci} __packed;
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci#define MWL8K_RF_ANTENNA_RX		1
293462306a36Sopenharmony_ci#define MWL8K_RF_ANTENNA_TX		2
293562306a36Sopenharmony_ci
293662306a36Sopenharmony_cistatic int
293762306a36Sopenharmony_cimwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask)
293862306a36Sopenharmony_ci{
293962306a36Sopenharmony_ci	struct mwl8k_cmd_rf_antenna *cmd;
294062306a36Sopenharmony_ci	int rc;
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
294362306a36Sopenharmony_ci	if (cmd == NULL)
294462306a36Sopenharmony_ci		return -ENOMEM;
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_ANTENNA);
294762306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
294862306a36Sopenharmony_ci	cmd->antenna = cpu_to_le16(antenna);
294962306a36Sopenharmony_ci	cmd->mode = cpu_to_le16(mask);
295062306a36Sopenharmony_ci
295162306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
295262306a36Sopenharmony_ci	kfree(cmd);
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci	return rc;
295562306a36Sopenharmony_ci}
295662306a36Sopenharmony_ci
295762306a36Sopenharmony_ci/*
295862306a36Sopenharmony_ci * CMD_SET_BEACON.
295962306a36Sopenharmony_ci */
296062306a36Sopenharmony_cistruct mwl8k_cmd_set_beacon {
296162306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
296262306a36Sopenharmony_ci	__le16 beacon_len;
296362306a36Sopenharmony_ci	__u8 beacon[];
296462306a36Sopenharmony_ci};
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_cistatic int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw,
296762306a36Sopenharmony_ci				struct ieee80211_vif *vif, u8 *beacon, int len)
296862306a36Sopenharmony_ci{
296962306a36Sopenharmony_ci	struct mwl8k_cmd_set_beacon *cmd;
297062306a36Sopenharmony_ci	int rc;
297162306a36Sopenharmony_ci
297262306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd) + len, GFP_KERNEL);
297362306a36Sopenharmony_ci	if (cmd == NULL)
297462306a36Sopenharmony_ci		return -ENOMEM;
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_BEACON);
297762306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd) + len);
297862306a36Sopenharmony_ci	cmd->beacon_len = cpu_to_le16(len);
297962306a36Sopenharmony_ci	memcpy(cmd->beacon, beacon, len);
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
298262306a36Sopenharmony_ci	kfree(cmd);
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_ci	return rc;
298562306a36Sopenharmony_ci}
298662306a36Sopenharmony_ci
298762306a36Sopenharmony_ci/*
298862306a36Sopenharmony_ci * CMD_SET_PRE_SCAN.
298962306a36Sopenharmony_ci */
299062306a36Sopenharmony_cistruct mwl8k_cmd_set_pre_scan {
299162306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
299262306a36Sopenharmony_ci} __packed;
299362306a36Sopenharmony_ci
299462306a36Sopenharmony_cistatic int mwl8k_cmd_set_pre_scan(struct ieee80211_hw *hw)
299562306a36Sopenharmony_ci{
299662306a36Sopenharmony_ci	struct mwl8k_cmd_set_pre_scan *cmd;
299762306a36Sopenharmony_ci	int rc;
299862306a36Sopenharmony_ci
299962306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
300062306a36Sopenharmony_ci	if (cmd == NULL)
300162306a36Sopenharmony_ci		return -ENOMEM;
300262306a36Sopenharmony_ci
300362306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_PRE_SCAN);
300462306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
300562306a36Sopenharmony_ci
300662306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
300762306a36Sopenharmony_ci	kfree(cmd);
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_ci	return rc;
301062306a36Sopenharmony_ci}
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci/*
301362306a36Sopenharmony_ci * CMD_BBP_REG_ACCESS.
301462306a36Sopenharmony_ci */
301562306a36Sopenharmony_cistruct mwl8k_cmd_bbp_reg_access {
301662306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
301762306a36Sopenharmony_ci	__le16 action;
301862306a36Sopenharmony_ci	__le16 offset;
301962306a36Sopenharmony_ci	u8 value;
302062306a36Sopenharmony_ci	u8 rsrv[3];
302162306a36Sopenharmony_ci} __packed;
302262306a36Sopenharmony_ci
302362306a36Sopenharmony_cistatic int
302462306a36Sopenharmony_cimwl8k_cmd_bbp_reg_access(struct ieee80211_hw *hw,
302562306a36Sopenharmony_ci			 u16 action,
302662306a36Sopenharmony_ci			 u16 offset,
302762306a36Sopenharmony_ci			 u8 *value)
302862306a36Sopenharmony_ci{
302962306a36Sopenharmony_ci	struct mwl8k_cmd_bbp_reg_access *cmd;
303062306a36Sopenharmony_ci	int rc;
303162306a36Sopenharmony_ci
303262306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
303362306a36Sopenharmony_ci	if (cmd == NULL)
303462306a36Sopenharmony_ci		return -ENOMEM;
303562306a36Sopenharmony_ci
303662306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_BBP_REG_ACCESS);
303762306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
303862306a36Sopenharmony_ci	cmd->action = cpu_to_le16(action);
303962306a36Sopenharmony_ci	cmd->offset = cpu_to_le16(offset);
304062306a36Sopenharmony_ci
304162306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
304262306a36Sopenharmony_ci
304362306a36Sopenharmony_ci	if (!rc)
304462306a36Sopenharmony_ci		*value = cmd->value;
304562306a36Sopenharmony_ci	else
304662306a36Sopenharmony_ci		*value = 0;
304762306a36Sopenharmony_ci
304862306a36Sopenharmony_ci	kfree(cmd);
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci	return rc;
305162306a36Sopenharmony_ci}
305262306a36Sopenharmony_ci
305362306a36Sopenharmony_ci/*
305462306a36Sopenharmony_ci * CMD_SET_POST_SCAN.
305562306a36Sopenharmony_ci */
305662306a36Sopenharmony_cistruct mwl8k_cmd_set_post_scan {
305762306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
305862306a36Sopenharmony_ci	__le32 isibss;
305962306a36Sopenharmony_ci	__u8 bssid[ETH_ALEN];
306062306a36Sopenharmony_ci} __packed;
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_cistatic int
306362306a36Sopenharmony_cimwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac)
306462306a36Sopenharmony_ci{
306562306a36Sopenharmony_ci	struct mwl8k_cmd_set_post_scan *cmd;
306662306a36Sopenharmony_ci	int rc;
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
306962306a36Sopenharmony_ci	if (cmd == NULL)
307062306a36Sopenharmony_ci		return -ENOMEM;
307162306a36Sopenharmony_ci
307262306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_POST_SCAN);
307362306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
307462306a36Sopenharmony_ci	cmd->isibss = 0;
307562306a36Sopenharmony_ci	memcpy(cmd->bssid, mac, ETH_ALEN);
307662306a36Sopenharmony_ci
307762306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
307862306a36Sopenharmony_ci	kfree(cmd);
307962306a36Sopenharmony_ci
308062306a36Sopenharmony_ci	return rc;
308162306a36Sopenharmony_ci}
308262306a36Sopenharmony_ci
308362306a36Sopenharmony_cistatic int freq_to_idx(struct mwl8k_priv *priv, int freq)
308462306a36Sopenharmony_ci{
308562306a36Sopenharmony_ci	struct ieee80211_supported_band *sband;
308662306a36Sopenharmony_ci	int band, ch, idx = 0;
308762306a36Sopenharmony_ci
308862306a36Sopenharmony_ci	for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
308962306a36Sopenharmony_ci		sband = priv->hw->wiphy->bands[band];
309062306a36Sopenharmony_ci		if (!sband)
309162306a36Sopenharmony_ci			continue;
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci		for (ch = 0; ch < sband->n_channels; ch++, idx++)
309462306a36Sopenharmony_ci			if (sband->channels[ch].center_freq == freq)
309562306a36Sopenharmony_ci				goto exit;
309662306a36Sopenharmony_ci	}
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ciexit:
309962306a36Sopenharmony_ci	return idx;
310062306a36Sopenharmony_ci}
310162306a36Sopenharmony_ci
310262306a36Sopenharmony_cistatic void mwl8k_update_survey(struct mwl8k_priv *priv,
310362306a36Sopenharmony_ci				struct ieee80211_channel *channel)
310462306a36Sopenharmony_ci{
310562306a36Sopenharmony_ci	u32 cca_cnt, rx_rdy;
310662306a36Sopenharmony_ci	s8 nf = 0, idx;
310762306a36Sopenharmony_ci	struct survey_info *survey;
310862306a36Sopenharmony_ci
310962306a36Sopenharmony_ci	idx = freq_to_idx(priv, priv->acs_chan->center_freq);
311062306a36Sopenharmony_ci	if (idx >= MWL8K_NUM_CHANS) {
311162306a36Sopenharmony_ci		wiphy_err(priv->hw->wiphy, "Failed to update survey\n");
311262306a36Sopenharmony_ci		return;
311362306a36Sopenharmony_ci	}
311462306a36Sopenharmony_ci
311562306a36Sopenharmony_ci	survey = &priv->survey[idx];
311662306a36Sopenharmony_ci
311762306a36Sopenharmony_ci	cca_cnt = ioread32(priv->regs + NOK_CCA_CNT_REG);
311862306a36Sopenharmony_ci	cca_cnt /= 1000; /* uSecs to mSecs */
311962306a36Sopenharmony_ci	survey->time_busy = (u64) cca_cnt;
312062306a36Sopenharmony_ci
312162306a36Sopenharmony_ci	rx_rdy = ioread32(priv->regs + BBU_RXRDY_CNT_REG);
312262306a36Sopenharmony_ci	rx_rdy /= 1000; /* uSecs to mSecs */
312362306a36Sopenharmony_ci	survey->time_rx = (u64) rx_rdy;
312462306a36Sopenharmony_ci
312562306a36Sopenharmony_ci	priv->channel_time = jiffies - priv->channel_time;
312662306a36Sopenharmony_ci	survey->time = jiffies_to_msecs(priv->channel_time);
312762306a36Sopenharmony_ci
312862306a36Sopenharmony_ci	survey->channel = channel;
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci	mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &nf);
313162306a36Sopenharmony_ci
313262306a36Sopenharmony_ci	/* Make sure sign is negative else ACS  at hostapd fails */
313362306a36Sopenharmony_ci	survey->noise = nf * -1;
313462306a36Sopenharmony_ci
313562306a36Sopenharmony_ci	survey->filled = SURVEY_INFO_NOISE_DBM |
313662306a36Sopenharmony_ci			 SURVEY_INFO_TIME |
313762306a36Sopenharmony_ci			 SURVEY_INFO_TIME_BUSY |
313862306a36Sopenharmony_ci			 SURVEY_INFO_TIME_RX;
313962306a36Sopenharmony_ci}
314062306a36Sopenharmony_ci
314162306a36Sopenharmony_ci/*
314262306a36Sopenharmony_ci * CMD_SET_RF_CHANNEL.
314362306a36Sopenharmony_ci */
314462306a36Sopenharmony_cistruct mwl8k_cmd_set_rf_channel {
314562306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
314662306a36Sopenharmony_ci	__le16 action;
314762306a36Sopenharmony_ci	__u8 current_channel;
314862306a36Sopenharmony_ci	__le32 channel_flags;
314962306a36Sopenharmony_ci} __packed;
315062306a36Sopenharmony_ci
315162306a36Sopenharmony_cistatic int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw,
315262306a36Sopenharmony_ci				    struct ieee80211_conf *conf)
315362306a36Sopenharmony_ci{
315462306a36Sopenharmony_ci	struct ieee80211_channel *channel = conf->chandef.chan;
315562306a36Sopenharmony_ci	enum nl80211_channel_type channel_type =
315662306a36Sopenharmony_ci		cfg80211_get_chandef_type(&conf->chandef);
315762306a36Sopenharmony_ci	struct mwl8k_cmd_set_rf_channel *cmd;
315862306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
315962306a36Sopenharmony_ci	int rc;
316062306a36Sopenharmony_ci
316162306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
316262306a36Sopenharmony_ci	if (cmd == NULL)
316362306a36Sopenharmony_ci		return -ENOMEM;
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RF_CHANNEL);
316662306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
316762306a36Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
316862306a36Sopenharmony_ci	cmd->current_channel = channel->hw_value;
316962306a36Sopenharmony_ci
317062306a36Sopenharmony_ci	if (channel->band == NL80211_BAND_2GHZ)
317162306a36Sopenharmony_ci		cmd->channel_flags |= cpu_to_le32(0x00000001);
317262306a36Sopenharmony_ci	else if (channel->band == NL80211_BAND_5GHZ)
317362306a36Sopenharmony_ci		cmd->channel_flags |= cpu_to_le32(0x00000004);
317462306a36Sopenharmony_ci
317562306a36Sopenharmony_ci	if (!priv->sw_scan_start) {
317662306a36Sopenharmony_ci		if (channel_type == NL80211_CHAN_NO_HT ||
317762306a36Sopenharmony_ci		    channel_type == NL80211_CHAN_HT20)
317862306a36Sopenharmony_ci			cmd->channel_flags |= cpu_to_le32(0x00000080);
317962306a36Sopenharmony_ci		else if (channel_type == NL80211_CHAN_HT40MINUS)
318062306a36Sopenharmony_ci			cmd->channel_flags |= cpu_to_le32(0x000001900);
318162306a36Sopenharmony_ci		else if (channel_type == NL80211_CHAN_HT40PLUS)
318262306a36Sopenharmony_ci			cmd->channel_flags |= cpu_to_le32(0x000000900);
318362306a36Sopenharmony_ci	} else {
318462306a36Sopenharmony_ci		cmd->channel_flags |= cpu_to_le32(0x00000080);
318562306a36Sopenharmony_ci	}
318662306a36Sopenharmony_ci
318762306a36Sopenharmony_ci	if (priv->sw_scan_start) {
318862306a36Sopenharmony_ci		/* Store current channel stats
318962306a36Sopenharmony_ci		 * before switching to newer one.
319062306a36Sopenharmony_ci		 * This will be processed only for AP fw.
319162306a36Sopenharmony_ci		 */
319262306a36Sopenharmony_ci		if (priv->channel_time != 0)
319362306a36Sopenharmony_ci			mwl8k_update_survey(priv, priv->acs_chan);
319462306a36Sopenharmony_ci
319562306a36Sopenharmony_ci		priv->channel_time = jiffies;
319662306a36Sopenharmony_ci		priv->acs_chan =  channel;
319762306a36Sopenharmony_ci	}
319862306a36Sopenharmony_ci
319962306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
320062306a36Sopenharmony_ci	kfree(cmd);
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_ci	return rc;
320362306a36Sopenharmony_ci}
320462306a36Sopenharmony_ci
320562306a36Sopenharmony_ci/*
320662306a36Sopenharmony_ci * CMD_SET_AID.
320762306a36Sopenharmony_ci */
320862306a36Sopenharmony_ci#define MWL8K_FRAME_PROT_DISABLED			0x00
320962306a36Sopenharmony_ci#define MWL8K_FRAME_PROT_11G				0x07
321062306a36Sopenharmony_ci#define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY		0x02
321162306a36Sopenharmony_ci#define MWL8K_FRAME_PROT_11N_HT_ALL			0x06
321262306a36Sopenharmony_ci
321362306a36Sopenharmony_cistruct mwl8k_cmd_update_set_aid {
321462306a36Sopenharmony_ci	struct	mwl8k_cmd_pkt header;
321562306a36Sopenharmony_ci	__le16	aid;
321662306a36Sopenharmony_ci
321762306a36Sopenharmony_ci	 /* AP's MAC address (BSSID) */
321862306a36Sopenharmony_ci	__u8	bssid[ETH_ALEN];
321962306a36Sopenharmony_ci	__le16	protection_mode;
322062306a36Sopenharmony_ci	__u8	supp_rates[14];
322162306a36Sopenharmony_ci} __packed;
322262306a36Sopenharmony_ci
322362306a36Sopenharmony_cistatic void legacy_rate_mask_to_array(u8 *rates, u32 mask)
322462306a36Sopenharmony_ci{
322562306a36Sopenharmony_ci	int i;
322662306a36Sopenharmony_ci	int j;
322762306a36Sopenharmony_ci
322862306a36Sopenharmony_ci	/*
322962306a36Sopenharmony_ci	 * Clear nonstandard rate 4.
323062306a36Sopenharmony_ci	 */
323162306a36Sopenharmony_ci	mask &= 0x1fef;
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci	for (i = 0, j = 0; i < 13; i++) {
323462306a36Sopenharmony_ci		if (mask & (1 << i))
323562306a36Sopenharmony_ci			rates[j++] = mwl8k_rates_24[i].hw_value;
323662306a36Sopenharmony_ci	}
323762306a36Sopenharmony_ci}
323862306a36Sopenharmony_ci
323962306a36Sopenharmony_cistatic int
324062306a36Sopenharmony_cimwl8k_cmd_set_aid(struct ieee80211_hw *hw,
324162306a36Sopenharmony_ci		  struct ieee80211_vif *vif, u32 legacy_rate_mask)
324262306a36Sopenharmony_ci{
324362306a36Sopenharmony_ci	struct mwl8k_cmd_update_set_aid *cmd;
324462306a36Sopenharmony_ci	u16 prot_mode;
324562306a36Sopenharmony_ci	int rc;
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
324862306a36Sopenharmony_ci	if (cmd == NULL)
324962306a36Sopenharmony_ci		return -ENOMEM;
325062306a36Sopenharmony_ci
325162306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID);
325262306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
325362306a36Sopenharmony_ci	cmd->aid = cpu_to_le16(vif->cfg.aid);
325462306a36Sopenharmony_ci	memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN);
325562306a36Sopenharmony_ci
325662306a36Sopenharmony_ci	if (vif->bss_conf.use_cts_prot) {
325762306a36Sopenharmony_ci		prot_mode = MWL8K_FRAME_PROT_11G;
325862306a36Sopenharmony_ci	} else {
325962306a36Sopenharmony_ci		switch (vif->bss_conf.ht_operation_mode &
326062306a36Sopenharmony_ci			IEEE80211_HT_OP_MODE_PROTECTION) {
326162306a36Sopenharmony_ci		case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
326262306a36Sopenharmony_ci			prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY;
326362306a36Sopenharmony_ci			break;
326462306a36Sopenharmony_ci		case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
326562306a36Sopenharmony_ci			prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL;
326662306a36Sopenharmony_ci			break;
326762306a36Sopenharmony_ci		default:
326862306a36Sopenharmony_ci			prot_mode = MWL8K_FRAME_PROT_DISABLED;
326962306a36Sopenharmony_ci			break;
327062306a36Sopenharmony_ci		}
327162306a36Sopenharmony_ci	}
327262306a36Sopenharmony_ci	cmd->protection_mode = cpu_to_le16(prot_mode);
327362306a36Sopenharmony_ci
327462306a36Sopenharmony_ci	legacy_rate_mask_to_array(cmd->supp_rates, legacy_rate_mask);
327562306a36Sopenharmony_ci
327662306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
327762306a36Sopenharmony_ci	kfree(cmd);
327862306a36Sopenharmony_ci
327962306a36Sopenharmony_ci	return rc;
328062306a36Sopenharmony_ci}
328162306a36Sopenharmony_ci
328262306a36Sopenharmony_ci/*
328362306a36Sopenharmony_ci * CMD_SET_RATE.
328462306a36Sopenharmony_ci */
328562306a36Sopenharmony_cistruct mwl8k_cmd_set_rate {
328662306a36Sopenharmony_ci	struct	mwl8k_cmd_pkt header;
328762306a36Sopenharmony_ci	__u8	legacy_rates[14];
328862306a36Sopenharmony_ci
328962306a36Sopenharmony_ci	/* Bitmap for supported MCS codes.  */
329062306a36Sopenharmony_ci	__u8	mcs_set[16];
329162306a36Sopenharmony_ci	__u8	reserved[16];
329262306a36Sopenharmony_ci} __packed;
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_cistatic int
329562306a36Sopenharmony_cimwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
329662306a36Sopenharmony_ci		   u32 legacy_rate_mask, u8 *mcs_rates)
329762306a36Sopenharmony_ci{
329862306a36Sopenharmony_ci	struct mwl8k_cmd_set_rate *cmd;
329962306a36Sopenharmony_ci	int rc;
330062306a36Sopenharmony_ci
330162306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
330262306a36Sopenharmony_ci	if (cmd == NULL)
330362306a36Sopenharmony_ci		return -ENOMEM;
330462306a36Sopenharmony_ci
330562306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE);
330662306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
330762306a36Sopenharmony_ci	legacy_rate_mask_to_array(cmd->legacy_rates, legacy_rate_mask);
330862306a36Sopenharmony_ci	memcpy(cmd->mcs_set, mcs_rates, 16);
330962306a36Sopenharmony_ci
331062306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
331162306a36Sopenharmony_ci	kfree(cmd);
331262306a36Sopenharmony_ci
331362306a36Sopenharmony_ci	return rc;
331462306a36Sopenharmony_ci}
331562306a36Sopenharmony_ci
331662306a36Sopenharmony_ci/*
331762306a36Sopenharmony_ci * CMD_FINALIZE_JOIN.
331862306a36Sopenharmony_ci */
331962306a36Sopenharmony_ci#define MWL8K_FJ_BEACON_MAXLEN	128
332062306a36Sopenharmony_ci
332162306a36Sopenharmony_cistruct mwl8k_cmd_finalize_join {
332262306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
332362306a36Sopenharmony_ci	__le32 sleep_interval;	/* Number of beacon periods to sleep */
332462306a36Sopenharmony_ci	__u8 beacon_data[MWL8K_FJ_BEACON_MAXLEN];
332562306a36Sopenharmony_ci} __packed;
332662306a36Sopenharmony_ci
332762306a36Sopenharmony_cistatic int mwl8k_cmd_finalize_join(struct ieee80211_hw *hw, void *frame,
332862306a36Sopenharmony_ci				   int framelen, int dtim)
332962306a36Sopenharmony_ci{
333062306a36Sopenharmony_ci	struct mwl8k_cmd_finalize_join *cmd;
333162306a36Sopenharmony_ci	struct ieee80211_mgmt *payload = frame;
333262306a36Sopenharmony_ci	int payload_len;
333362306a36Sopenharmony_ci	int rc;
333462306a36Sopenharmony_ci
333562306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
333662306a36Sopenharmony_ci	if (cmd == NULL)
333762306a36Sopenharmony_ci		return -ENOMEM;
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN);
334062306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
334162306a36Sopenharmony_ci	cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1);
334262306a36Sopenharmony_ci
334362306a36Sopenharmony_ci	payload_len = framelen - ieee80211_hdrlen(payload->frame_control);
334462306a36Sopenharmony_ci	if (payload_len < 0)
334562306a36Sopenharmony_ci		payload_len = 0;
334662306a36Sopenharmony_ci	else if (payload_len > MWL8K_FJ_BEACON_MAXLEN)
334762306a36Sopenharmony_ci		payload_len = MWL8K_FJ_BEACON_MAXLEN;
334862306a36Sopenharmony_ci
334962306a36Sopenharmony_ci	memcpy(cmd->beacon_data, &payload->u.beacon, payload_len);
335062306a36Sopenharmony_ci
335162306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
335262306a36Sopenharmony_ci	kfree(cmd);
335362306a36Sopenharmony_ci
335462306a36Sopenharmony_ci	return rc;
335562306a36Sopenharmony_ci}
335662306a36Sopenharmony_ci
335762306a36Sopenharmony_ci/*
335862306a36Sopenharmony_ci * CMD_SET_RTS_THRESHOLD.
335962306a36Sopenharmony_ci */
336062306a36Sopenharmony_cistruct mwl8k_cmd_set_rts_threshold {
336162306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
336262306a36Sopenharmony_ci	__le16 action;
336362306a36Sopenharmony_ci	__le16 threshold;
336462306a36Sopenharmony_ci} __packed;
336562306a36Sopenharmony_ci
336662306a36Sopenharmony_cistatic int
336762306a36Sopenharmony_cimwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, int rts_thresh)
336862306a36Sopenharmony_ci{
336962306a36Sopenharmony_ci	struct mwl8k_cmd_set_rts_threshold *cmd;
337062306a36Sopenharmony_ci	int rc;
337162306a36Sopenharmony_ci
337262306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
337362306a36Sopenharmony_ci	if (cmd == NULL)
337462306a36Sopenharmony_ci		return -ENOMEM;
337562306a36Sopenharmony_ci
337662306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD);
337762306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
337862306a36Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
337962306a36Sopenharmony_ci	cmd->threshold = cpu_to_le16(rts_thresh);
338062306a36Sopenharmony_ci
338162306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
338262306a36Sopenharmony_ci	kfree(cmd);
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_ci	return rc;
338562306a36Sopenharmony_ci}
338662306a36Sopenharmony_ci
338762306a36Sopenharmony_ci/*
338862306a36Sopenharmony_ci * CMD_SET_SLOT.
338962306a36Sopenharmony_ci */
339062306a36Sopenharmony_cistruct mwl8k_cmd_set_slot {
339162306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
339262306a36Sopenharmony_ci	__le16 action;
339362306a36Sopenharmony_ci	__u8 short_slot;
339462306a36Sopenharmony_ci} __packed;
339562306a36Sopenharmony_ci
339662306a36Sopenharmony_cistatic int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time)
339762306a36Sopenharmony_ci{
339862306a36Sopenharmony_ci	struct mwl8k_cmd_set_slot *cmd;
339962306a36Sopenharmony_ci	int rc;
340062306a36Sopenharmony_ci
340162306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
340262306a36Sopenharmony_ci	if (cmd == NULL)
340362306a36Sopenharmony_ci		return -ENOMEM;
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT);
340662306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
340762306a36Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
340862306a36Sopenharmony_ci	cmd->short_slot = short_slot_time;
340962306a36Sopenharmony_ci
341062306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
341162306a36Sopenharmony_ci	kfree(cmd);
341262306a36Sopenharmony_ci
341362306a36Sopenharmony_ci	return rc;
341462306a36Sopenharmony_ci}
341562306a36Sopenharmony_ci
341662306a36Sopenharmony_ci/*
341762306a36Sopenharmony_ci * CMD_SET_EDCA_PARAMS.
341862306a36Sopenharmony_ci */
341962306a36Sopenharmony_cistruct mwl8k_cmd_set_edca_params {
342062306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
342162306a36Sopenharmony_ci
342262306a36Sopenharmony_ci	/* See MWL8K_SET_EDCA_XXX below */
342362306a36Sopenharmony_ci	__le16 action;
342462306a36Sopenharmony_ci
342562306a36Sopenharmony_ci	/* TX opportunity in units of 32 us */
342662306a36Sopenharmony_ci	__le16 txop;
342762306a36Sopenharmony_ci
342862306a36Sopenharmony_ci	union {
342962306a36Sopenharmony_ci		struct {
343062306a36Sopenharmony_ci			/* Log exponent of max contention period: 0...15 */
343162306a36Sopenharmony_ci			__le32 log_cw_max;
343262306a36Sopenharmony_ci
343362306a36Sopenharmony_ci			/* Log exponent of min contention period: 0...15 */
343462306a36Sopenharmony_ci			__le32 log_cw_min;
343562306a36Sopenharmony_ci
343662306a36Sopenharmony_ci			/* Adaptive interframe spacing in units of 32us */
343762306a36Sopenharmony_ci			__u8 aifs;
343862306a36Sopenharmony_ci
343962306a36Sopenharmony_ci			/* TX queue to configure */
344062306a36Sopenharmony_ci			__u8 txq;
344162306a36Sopenharmony_ci		} ap;
344262306a36Sopenharmony_ci		struct {
344362306a36Sopenharmony_ci			/* Log exponent of max contention period: 0...15 */
344462306a36Sopenharmony_ci			__u8 log_cw_max;
344562306a36Sopenharmony_ci
344662306a36Sopenharmony_ci			/* Log exponent of min contention period: 0...15 */
344762306a36Sopenharmony_ci			__u8 log_cw_min;
344862306a36Sopenharmony_ci
344962306a36Sopenharmony_ci			/* Adaptive interframe spacing in units of 32us */
345062306a36Sopenharmony_ci			__u8 aifs;
345162306a36Sopenharmony_ci
345262306a36Sopenharmony_ci			/* TX queue to configure */
345362306a36Sopenharmony_ci			__u8 txq;
345462306a36Sopenharmony_ci		} sta;
345562306a36Sopenharmony_ci	};
345662306a36Sopenharmony_ci} __packed;
345762306a36Sopenharmony_ci
345862306a36Sopenharmony_ci#define MWL8K_SET_EDCA_CW	0x01
345962306a36Sopenharmony_ci#define MWL8K_SET_EDCA_TXOP	0x02
346062306a36Sopenharmony_ci#define MWL8K_SET_EDCA_AIFS	0x04
346162306a36Sopenharmony_ci
346262306a36Sopenharmony_ci#define MWL8K_SET_EDCA_ALL	(MWL8K_SET_EDCA_CW | \
346362306a36Sopenharmony_ci				 MWL8K_SET_EDCA_TXOP | \
346462306a36Sopenharmony_ci				 MWL8K_SET_EDCA_AIFS)
346562306a36Sopenharmony_ci
346662306a36Sopenharmony_cistatic int
346762306a36Sopenharmony_cimwl8k_cmd_set_edca_params(struct ieee80211_hw *hw, __u8 qnum,
346862306a36Sopenharmony_ci			  __u16 cw_min, __u16 cw_max,
346962306a36Sopenharmony_ci			  __u8 aifs, __u16 txop)
347062306a36Sopenharmony_ci{
347162306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
347262306a36Sopenharmony_ci	struct mwl8k_cmd_set_edca_params *cmd;
347362306a36Sopenharmony_ci	int rc;
347462306a36Sopenharmony_ci
347562306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
347662306a36Sopenharmony_ci	if (cmd == NULL)
347762306a36Sopenharmony_ci		return -ENOMEM;
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS);
348062306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
348162306a36Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL);
348262306a36Sopenharmony_ci	cmd->txop = cpu_to_le16(txop);
348362306a36Sopenharmony_ci	if (priv->ap_fw) {
348462306a36Sopenharmony_ci		cmd->ap.log_cw_max = cpu_to_le32(ilog2(cw_max + 1));
348562306a36Sopenharmony_ci		cmd->ap.log_cw_min = cpu_to_le32(ilog2(cw_min + 1));
348662306a36Sopenharmony_ci		cmd->ap.aifs = aifs;
348762306a36Sopenharmony_ci		cmd->ap.txq = qnum;
348862306a36Sopenharmony_ci	} else {
348962306a36Sopenharmony_ci		cmd->sta.log_cw_max = (u8)ilog2(cw_max + 1);
349062306a36Sopenharmony_ci		cmd->sta.log_cw_min = (u8)ilog2(cw_min + 1);
349162306a36Sopenharmony_ci		cmd->sta.aifs = aifs;
349262306a36Sopenharmony_ci		cmd->sta.txq = qnum;
349362306a36Sopenharmony_ci	}
349462306a36Sopenharmony_ci
349562306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
349662306a36Sopenharmony_ci	kfree(cmd);
349762306a36Sopenharmony_ci
349862306a36Sopenharmony_ci	return rc;
349962306a36Sopenharmony_ci}
350062306a36Sopenharmony_ci
350162306a36Sopenharmony_ci/*
350262306a36Sopenharmony_ci * CMD_SET_WMM_MODE.
350362306a36Sopenharmony_ci */
350462306a36Sopenharmony_cistruct mwl8k_cmd_set_wmm_mode {
350562306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
350662306a36Sopenharmony_ci	__le16 action;
350762306a36Sopenharmony_ci} __packed;
350862306a36Sopenharmony_ci
350962306a36Sopenharmony_cistatic int mwl8k_cmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable)
351062306a36Sopenharmony_ci{
351162306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
351262306a36Sopenharmony_ci	struct mwl8k_cmd_set_wmm_mode *cmd;
351362306a36Sopenharmony_ci	int rc;
351462306a36Sopenharmony_ci
351562306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
351662306a36Sopenharmony_ci	if (cmd == NULL)
351762306a36Sopenharmony_ci		return -ENOMEM;
351862306a36Sopenharmony_ci
351962306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_WMM_MODE);
352062306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
352162306a36Sopenharmony_ci	cmd->action = cpu_to_le16(!!enable);
352262306a36Sopenharmony_ci
352362306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
352462306a36Sopenharmony_ci	kfree(cmd);
352562306a36Sopenharmony_ci
352662306a36Sopenharmony_ci	if (!rc)
352762306a36Sopenharmony_ci		priv->wmm_enabled = enable;
352862306a36Sopenharmony_ci
352962306a36Sopenharmony_ci	return rc;
353062306a36Sopenharmony_ci}
353162306a36Sopenharmony_ci
353262306a36Sopenharmony_ci/*
353362306a36Sopenharmony_ci * CMD_MIMO_CONFIG.
353462306a36Sopenharmony_ci */
353562306a36Sopenharmony_cistruct mwl8k_cmd_mimo_config {
353662306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
353762306a36Sopenharmony_ci	__le32 action;
353862306a36Sopenharmony_ci	__u8 rx_antenna_map;
353962306a36Sopenharmony_ci	__u8 tx_antenna_map;
354062306a36Sopenharmony_ci} __packed;
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_cistatic int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx)
354362306a36Sopenharmony_ci{
354462306a36Sopenharmony_ci	struct mwl8k_cmd_mimo_config *cmd;
354562306a36Sopenharmony_ci	int rc;
354662306a36Sopenharmony_ci
354762306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
354862306a36Sopenharmony_ci	if (cmd == NULL)
354962306a36Sopenharmony_ci		return -ENOMEM;
355062306a36Sopenharmony_ci
355162306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_MIMO_CONFIG);
355262306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
355362306a36Sopenharmony_ci	cmd->action = cpu_to_le32((u32)MWL8K_CMD_SET);
355462306a36Sopenharmony_ci	cmd->rx_antenna_map = rx;
355562306a36Sopenharmony_ci	cmd->tx_antenna_map = tx;
355662306a36Sopenharmony_ci
355762306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
355862306a36Sopenharmony_ci	kfree(cmd);
355962306a36Sopenharmony_ci
356062306a36Sopenharmony_ci	return rc;
356162306a36Sopenharmony_ci}
356262306a36Sopenharmony_ci
356362306a36Sopenharmony_ci/*
356462306a36Sopenharmony_ci * CMD_USE_FIXED_RATE (STA version).
356562306a36Sopenharmony_ci */
356662306a36Sopenharmony_cistruct mwl8k_cmd_use_fixed_rate_sta {
356762306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
356862306a36Sopenharmony_ci	__le32 action;
356962306a36Sopenharmony_ci	__le32 allow_rate_drop;
357062306a36Sopenharmony_ci	__le32 num_rates;
357162306a36Sopenharmony_ci	struct {
357262306a36Sopenharmony_ci		__le32 is_ht_rate;
357362306a36Sopenharmony_ci		__le32 enable_retry;
357462306a36Sopenharmony_ci		__le32 rate;
357562306a36Sopenharmony_ci		__le32 retry_count;
357662306a36Sopenharmony_ci	} rate_entry[8];
357762306a36Sopenharmony_ci	__le32 rate_type;
357862306a36Sopenharmony_ci	__le32 reserved1;
357962306a36Sopenharmony_ci	__le32 reserved2;
358062306a36Sopenharmony_ci} __packed;
358162306a36Sopenharmony_ci
358262306a36Sopenharmony_ci#define MWL8K_USE_AUTO_RATE	0x0002
358362306a36Sopenharmony_ci#define MWL8K_UCAST_RATE	0
358462306a36Sopenharmony_ci
358562306a36Sopenharmony_cistatic int mwl8k_cmd_use_fixed_rate_sta(struct ieee80211_hw *hw)
358662306a36Sopenharmony_ci{
358762306a36Sopenharmony_ci	struct mwl8k_cmd_use_fixed_rate_sta *cmd;
358862306a36Sopenharmony_ci	int rc;
358962306a36Sopenharmony_ci
359062306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
359162306a36Sopenharmony_ci	if (cmd == NULL)
359262306a36Sopenharmony_ci		return -ENOMEM;
359362306a36Sopenharmony_ci
359462306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE);
359562306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
359662306a36Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE);
359762306a36Sopenharmony_ci	cmd->rate_type = cpu_to_le32(MWL8K_UCAST_RATE);
359862306a36Sopenharmony_ci
359962306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
360062306a36Sopenharmony_ci	kfree(cmd);
360162306a36Sopenharmony_ci
360262306a36Sopenharmony_ci	return rc;
360362306a36Sopenharmony_ci}
360462306a36Sopenharmony_ci
360562306a36Sopenharmony_ci/*
360662306a36Sopenharmony_ci * CMD_USE_FIXED_RATE (AP version).
360762306a36Sopenharmony_ci */
360862306a36Sopenharmony_cistruct mwl8k_cmd_use_fixed_rate_ap {
360962306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
361062306a36Sopenharmony_ci	__le32 action;
361162306a36Sopenharmony_ci	__le32 allow_rate_drop;
361262306a36Sopenharmony_ci	__le32 num_rates;
361362306a36Sopenharmony_ci	struct mwl8k_rate_entry_ap {
361462306a36Sopenharmony_ci		__le32 is_ht_rate;
361562306a36Sopenharmony_ci		__le32 enable_retry;
361662306a36Sopenharmony_ci		__le32 rate;
361762306a36Sopenharmony_ci		__le32 retry_count;
361862306a36Sopenharmony_ci	} rate_entry[4];
361962306a36Sopenharmony_ci	u8 multicast_rate;
362062306a36Sopenharmony_ci	u8 multicast_rate_type;
362162306a36Sopenharmony_ci	u8 management_rate;
362262306a36Sopenharmony_ci} __packed;
362362306a36Sopenharmony_ci
362462306a36Sopenharmony_cistatic int
362562306a36Sopenharmony_cimwl8k_cmd_use_fixed_rate_ap(struct ieee80211_hw *hw, int mcast, int mgmt)
362662306a36Sopenharmony_ci{
362762306a36Sopenharmony_ci	struct mwl8k_cmd_use_fixed_rate_ap *cmd;
362862306a36Sopenharmony_ci	int rc;
362962306a36Sopenharmony_ci
363062306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
363162306a36Sopenharmony_ci	if (cmd == NULL)
363262306a36Sopenharmony_ci		return -ENOMEM;
363362306a36Sopenharmony_ci
363462306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE);
363562306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
363662306a36Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE);
363762306a36Sopenharmony_ci	cmd->multicast_rate = mcast;
363862306a36Sopenharmony_ci	cmd->management_rate = mgmt;
363962306a36Sopenharmony_ci
364062306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
364162306a36Sopenharmony_ci	kfree(cmd);
364262306a36Sopenharmony_ci
364362306a36Sopenharmony_ci	return rc;
364462306a36Sopenharmony_ci}
364562306a36Sopenharmony_ci
364662306a36Sopenharmony_ci/*
364762306a36Sopenharmony_ci * CMD_ENABLE_SNIFFER.
364862306a36Sopenharmony_ci */
364962306a36Sopenharmony_cistruct mwl8k_cmd_enable_sniffer {
365062306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
365162306a36Sopenharmony_ci	__le32 action;
365262306a36Sopenharmony_ci} __packed;
365362306a36Sopenharmony_ci
365462306a36Sopenharmony_cistatic int mwl8k_cmd_enable_sniffer(struct ieee80211_hw *hw, bool enable)
365562306a36Sopenharmony_ci{
365662306a36Sopenharmony_ci	struct mwl8k_cmd_enable_sniffer *cmd;
365762306a36Sopenharmony_ci	int rc;
365862306a36Sopenharmony_ci
365962306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
366062306a36Sopenharmony_ci	if (cmd == NULL)
366162306a36Sopenharmony_ci		return -ENOMEM;
366262306a36Sopenharmony_ci
366362306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER);
366462306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
366562306a36Sopenharmony_ci	cmd->action = cpu_to_le32(!!enable);
366662306a36Sopenharmony_ci
366762306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
366862306a36Sopenharmony_ci	kfree(cmd);
366962306a36Sopenharmony_ci
367062306a36Sopenharmony_ci	return rc;
367162306a36Sopenharmony_ci}
367262306a36Sopenharmony_ci
367362306a36Sopenharmony_cistruct mwl8k_cmd_update_mac_addr {
367462306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
367562306a36Sopenharmony_ci	union {
367662306a36Sopenharmony_ci		struct {
367762306a36Sopenharmony_ci			__le16 mac_type;
367862306a36Sopenharmony_ci			__u8 mac_addr[ETH_ALEN];
367962306a36Sopenharmony_ci		} mbss;
368062306a36Sopenharmony_ci		__u8 mac_addr[ETH_ALEN];
368162306a36Sopenharmony_ci	};
368262306a36Sopenharmony_ci} __packed;
368362306a36Sopenharmony_ci
368462306a36Sopenharmony_ci#define MWL8K_MAC_TYPE_PRIMARY_CLIENT		0
368562306a36Sopenharmony_ci#define MWL8K_MAC_TYPE_SECONDARY_CLIENT		1
368662306a36Sopenharmony_ci#define MWL8K_MAC_TYPE_PRIMARY_AP		2
368762306a36Sopenharmony_ci#define MWL8K_MAC_TYPE_SECONDARY_AP		3
368862306a36Sopenharmony_ci
368962306a36Sopenharmony_cistatic int mwl8k_cmd_update_mac_addr(struct ieee80211_hw *hw,
369062306a36Sopenharmony_ci				  struct ieee80211_vif *vif, u8 *mac, bool set)
369162306a36Sopenharmony_ci{
369262306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
369362306a36Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
369462306a36Sopenharmony_ci	struct mwl8k_cmd_update_mac_addr *cmd;
369562306a36Sopenharmony_ci	int mac_type;
369662306a36Sopenharmony_ci	int rc;
369762306a36Sopenharmony_ci
369862306a36Sopenharmony_ci	mac_type = MWL8K_MAC_TYPE_PRIMARY_AP;
369962306a36Sopenharmony_ci	if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) {
370062306a36Sopenharmony_ci		if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported))
370162306a36Sopenharmony_ci			if (priv->ap_fw)
370262306a36Sopenharmony_ci				mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT;
370362306a36Sopenharmony_ci			else
370462306a36Sopenharmony_ci				mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT;
370562306a36Sopenharmony_ci		else
370662306a36Sopenharmony_ci			mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT;
370762306a36Sopenharmony_ci	} else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) {
370862306a36Sopenharmony_ci		if (mwl8k_vif->macid + 1 == ffs(priv->ap_macids_supported))
370962306a36Sopenharmony_ci			mac_type = MWL8K_MAC_TYPE_PRIMARY_AP;
371062306a36Sopenharmony_ci		else
371162306a36Sopenharmony_ci			mac_type = MWL8K_MAC_TYPE_SECONDARY_AP;
371262306a36Sopenharmony_ci	}
371362306a36Sopenharmony_ci
371462306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
371562306a36Sopenharmony_ci	if (cmd == NULL)
371662306a36Sopenharmony_ci		return -ENOMEM;
371762306a36Sopenharmony_ci
371862306a36Sopenharmony_ci	if (set)
371962306a36Sopenharmony_ci		cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR);
372062306a36Sopenharmony_ci	else
372162306a36Sopenharmony_ci		cmd->header.code = cpu_to_le16(MWL8K_CMD_DEL_MAC_ADDR);
372262306a36Sopenharmony_ci
372362306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
372462306a36Sopenharmony_ci	if (priv->ap_fw) {
372562306a36Sopenharmony_ci		cmd->mbss.mac_type = cpu_to_le16(mac_type);
372662306a36Sopenharmony_ci		memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN);
372762306a36Sopenharmony_ci	} else {
372862306a36Sopenharmony_ci		memcpy(cmd->mac_addr, mac, ETH_ALEN);
372962306a36Sopenharmony_ci	}
373062306a36Sopenharmony_ci
373162306a36Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
373262306a36Sopenharmony_ci	kfree(cmd);
373362306a36Sopenharmony_ci
373462306a36Sopenharmony_ci	return rc;
373562306a36Sopenharmony_ci}
373662306a36Sopenharmony_ci
373762306a36Sopenharmony_ci/*
373862306a36Sopenharmony_ci * MWL8K_CMD_SET_MAC_ADDR.
373962306a36Sopenharmony_ci */
374062306a36Sopenharmony_cistatic inline int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw,
374162306a36Sopenharmony_ci				  struct ieee80211_vif *vif, u8 *mac)
374262306a36Sopenharmony_ci{
374362306a36Sopenharmony_ci	return mwl8k_cmd_update_mac_addr(hw, vif, mac, true);
374462306a36Sopenharmony_ci}
374562306a36Sopenharmony_ci
374662306a36Sopenharmony_ci/*
374762306a36Sopenharmony_ci * MWL8K_CMD_DEL_MAC_ADDR.
374862306a36Sopenharmony_ci */
374962306a36Sopenharmony_cistatic inline int mwl8k_cmd_del_mac_addr(struct ieee80211_hw *hw,
375062306a36Sopenharmony_ci				  struct ieee80211_vif *vif, u8 *mac)
375162306a36Sopenharmony_ci{
375262306a36Sopenharmony_ci	return mwl8k_cmd_update_mac_addr(hw, vif, mac, false);
375362306a36Sopenharmony_ci}
375462306a36Sopenharmony_ci
375562306a36Sopenharmony_ci/*
375662306a36Sopenharmony_ci * CMD_SET_RATEADAPT_MODE.
375762306a36Sopenharmony_ci */
375862306a36Sopenharmony_cistruct mwl8k_cmd_set_rate_adapt_mode {
375962306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
376062306a36Sopenharmony_ci	__le16 action;
376162306a36Sopenharmony_ci	__le16 mode;
376262306a36Sopenharmony_ci} __packed;
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_cistatic int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode)
376562306a36Sopenharmony_ci{
376662306a36Sopenharmony_ci	struct mwl8k_cmd_set_rate_adapt_mode *cmd;
376762306a36Sopenharmony_ci	int rc;
376862306a36Sopenharmony_ci
376962306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
377062306a36Sopenharmony_ci	if (cmd == NULL)
377162306a36Sopenharmony_ci		return -ENOMEM;
377262306a36Sopenharmony_ci
377362306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATEADAPT_MODE);
377462306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
377562306a36Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
377662306a36Sopenharmony_ci	cmd->mode = cpu_to_le16(mode);
377762306a36Sopenharmony_ci
377862306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
377962306a36Sopenharmony_ci	kfree(cmd);
378062306a36Sopenharmony_ci
378162306a36Sopenharmony_ci	return rc;
378262306a36Sopenharmony_ci}
378362306a36Sopenharmony_ci
378462306a36Sopenharmony_ci/*
378562306a36Sopenharmony_ci * CMD_GET_WATCHDOG_BITMAP.
378662306a36Sopenharmony_ci */
378762306a36Sopenharmony_cistruct mwl8k_cmd_get_watchdog_bitmap {
378862306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
378962306a36Sopenharmony_ci	u8	bitmap;
379062306a36Sopenharmony_ci} __packed;
379162306a36Sopenharmony_ci
379262306a36Sopenharmony_cistatic int mwl8k_cmd_get_watchdog_bitmap(struct ieee80211_hw *hw, u8 *bitmap)
379362306a36Sopenharmony_ci{
379462306a36Sopenharmony_ci	struct mwl8k_cmd_get_watchdog_bitmap *cmd;
379562306a36Sopenharmony_ci	int rc;
379662306a36Sopenharmony_ci
379762306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
379862306a36Sopenharmony_ci	if (cmd == NULL)
379962306a36Sopenharmony_ci		return -ENOMEM;
380062306a36Sopenharmony_ci
380162306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_WATCHDOG_BITMAP);
380262306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
380362306a36Sopenharmony_ci
380462306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
380562306a36Sopenharmony_ci	if (!rc)
380662306a36Sopenharmony_ci		*bitmap = cmd->bitmap;
380762306a36Sopenharmony_ci
380862306a36Sopenharmony_ci	kfree(cmd);
380962306a36Sopenharmony_ci
381062306a36Sopenharmony_ci	return rc;
381162306a36Sopenharmony_ci}
381262306a36Sopenharmony_ci
381362306a36Sopenharmony_ci#define MWL8K_WMM_QUEUE_NUMBER	3
381462306a36Sopenharmony_ci
381562306a36Sopenharmony_cistatic void mwl8k_destroy_ba(struct ieee80211_hw *hw,
381662306a36Sopenharmony_ci			     u8 idx);
381762306a36Sopenharmony_ci
381862306a36Sopenharmony_cistatic void mwl8k_watchdog_ba_events(struct work_struct *work)
381962306a36Sopenharmony_ci{
382062306a36Sopenharmony_ci	int rc;
382162306a36Sopenharmony_ci	u8 bitmap = 0, stream_index;
382262306a36Sopenharmony_ci	struct mwl8k_ampdu_stream *streams;
382362306a36Sopenharmony_ci	struct mwl8k_priv *priv =
382462306a36Sopenharmony_ci		container_of(work, struct mwl8k_priv, watchdog_ba_handle);
382562306a36Sopenharmony_ci	struct ieee80211_hw *hw = priv->hw;
382662306a36Sopenharmony_ci	int i;
382762306a36Sopenharmony_ci	u32 status = 0;
382862306a36Sopenharmony_ci
382962306a36Sopenharmony_ci	mwl8k_fw_lock(hw);
383062306a36Sopenharmony_ci
383162306a36Sopenharmony_ci	rc = mwl8k_cmd_get_watchdog_bitmap(priv->hw, &bitmap);
383262306a36Sopenharmony_ci	if (rc)
383362306a36Sopenharmony_ci		goto done;
383462306a36Sopenharmony_ci
383562306a36Sopenharmony_ci	spin_lock(&priv->stream_lock);
383662306a36Sopenharmony_ci
383762306a36Sopenharmony_ci	/* the bitmap is the hw queue number.  Map it to the ampdu queue. */
383862306a36Sopenharmony_ci	for (i = 0; i < TOTAL_HW_TX_QUEUES; i++) {
383962306a36Sopenharmony_ci		if (bitmap & (1 << i)) {
384062306a36Sopenharmony_ci			stream_index = (i + MWL8K_WMM_QUEUE_NUMBER) %
384162306a36Sopenharmony_ci				       TOTAL_HW_TX_QUEUES;
384262306a36Sopenharmony_ci			streams = &priv->ampdu[stream_index];
384362306a36Sopenharmony_ci			if (streams->state == AMPDU_STREAM_ACTIVE) {
384462306a36Sopenharmony_ci				ieee80211_stop_tx_ba_session(streams->sta,
384562306a36Sopenharmony_ci							     streams->tid);
384662306a36Sopenharmony_ci				spin_unlock(&priv->stream_lock);
384762306a36Sopenharmony_ci				mwl8k_destroy_ba(hw, stream_index);
384862306a36Sopenharmony_ci				spin_lock(&priv->stream_lock);
384962306a36Sopenharmony_ci			}
385062306a36Sopenharmony_ci		}
385162306a36Sopenharmony_ci	}
385262306a36Sopenharmony_ci
385362306a36Sopenharmony_ci	spin_unlock(&priv->stream_lock);
385462306a36Sopenharmony_cidone:
385562306a36Sopenharmony_ci	atomic_dec(&priv->watchdog_event_pending);
385662306a36Sopenharmony_ci	status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
385762306a36Sopenharmony_ci	iowrite32((status | MWL8K_A2H_INT_BA_WATCHDOG),
385862306a36Sopenharmony_ci		  priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
385962306a36Sopenharmony_ci	mwl8k_fw_unlock(hw);
386062306a36Sopenharmony_ci	return;
386162306a36Sopenharmony_ci}
386262306a36Sopenharmony_ci
386362306a36Sopenharmony_ci
386462306a36Sopenharmony_ci/*
386562306a36Sopenharmony_ci * CMD_BSS_START.
386662306a36Sopenharmony_ci */
386762306a36Sopenharmony_cistruct mwl8k_cmd_bss_start {
386862306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
386962306a36Sopenharmony_ci	__le32 enable;
387062306a36Sopenharmony_ci} __packed;
387162306a36Sopenharmony_ci
387262306a36Sopenharmony_cistatic int mwl8k_cmd_bss_start(struct ieee80211_hw *hw,
387362306a36Sopenharmony_ci			       struct ieee80211_vif *vif, int enable)
387462306a36Sopenharmony_ci{
387562306a36Sopenharmony_ci	struct mwl8k_cmd_bss_start *cmd;
387662306a36Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
387762306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
387862306a36Sopenharmony_ci	int rc;
387962306a36Sopenharmony_ci
388062306a36Sopenharmony_ci	if (enable && (priv->running_bsses & (1 << mwl8k_vif->macid)))
388162306a36Sopenharmony_ci		return 0;
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_ci	if (!enable && !(priv->running_bsses & (1 << mwl8k_vif->macid)))
388462306a36Sopenharmony_ci		return 0;
388562306a36Sopenharmony_ci
388662306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
388762306a36Sopenharmony_ci	if (cmd == NULL)
388862306a36Sopenharmony_ci		return -ENOMEM;
388962306a36Sopenharmony_ci
389062306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_BSS_START);
389162306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
389262306a36Sopenharmony_ci	cmd->enable = cpu_to_le32(enable);
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
389562306a36Sopenharmony_ci	kfree(cmd);
389662306a36Sopenharmony_ci
389762306a36Sopenharmony_ci	if (!rc) {
389862306a36Sopenharmony_ci		if (enable)
389962306a36Sopenharmony_ci			priv->running_bsses |= (1 << mwl8k_vif->macid);
390062306a36Sopenharmony_ci		else
390162306a36Sopenharmony_ci			priv->running_bsses &= ~(1 << mwl8k_vif->macid);
390262306a36Sopenharmony_ci	}
390362306a36Sopenharmony_ci	return rc;
390462306a36Sopenharmony_ci}
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_cistatic void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable, u32 bitmap)
390762306a36Sopenharmony_ci{
390862306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
390962306a36Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif, *tmp_vif;
391062306a36Sopenharmony_ci	struct ieee80211_vif *vif;
391162306a36Sopenharmony_ci
391262306a36Sopenharmony_ci	list_for_each_entry_safe(mwl8k_vif, tmp_vif, &priv->vif_list, list) {
391362306a36Sopenharmony_ci		vif = mwl8k_vif->vif;
391462306a36Sopenharmony_ci
391562306a36Sopenharmony_ci		if (!(bitmap & (1 << mwl8k_vif->macid)))
391662306a36Sopenharmony_ci			continue;
391762306a36Sopenharmony_ci
391862306a36Sopenharmony_ci		if (vif->type == NL80211_IFTYPE_AP)
391962306a36Sopenharmony_ci			mwl8k_cmd_bss_start(hw, vif, enable);
392062306a36Sopenharmony_ci	}
392162306a36Sopenharmony_ci}
392262306a36Sopenharmony_ci/*
392362306a36Sopenharmony_ci * CMD_BASTREAM.
392462306a36Sopenharmony_ci */
392562306a36Sopenharmony_ci
392662306a36Sopenharmony_ci/*
392762306a36Sopenharmony_ci * UPSTREAM is tx direction
392862306a36Sopenharmony_ci */
392962306a36Sopenharmony_ci#define BASTREAM_FLAG_DIRECTION_UPSTREAM	0x00
393062306a36Sopenharmony_ci#define BASTREAM_FLAG_IMMEDIATE_TYPE		0x01
393162306a36Sopenharmony_ci
393262306a36Sopenharmony_cienum ba_stream_action_type {
393362306a36Sopenharmony_ci	MWL8K_BA_CREATE,
393462306a36Sopenharmony_ci	MWL8K_BA_UPDATE,
393562306a36Sopenharmony_ci	MWL8K_BA_DESTROY,
393662306a36Sopenharmony_ci	MWL8K_BA_FLUSH,
393762306a36Sopenharmony_ci	MWL8K_BA_CHECK,
393862306a36Sopenharmony_ci};
393962306a36Sopenharmony_ci
394062306a36Sopenharmony_ci
394162306a36Sopenharmony_cistruct mwl8k_create_ba_stream {
394262306a36Sopenharmony_ci	__le32	flags;
394362306a36Sopenharmony_ci	__le32	idle_thrs;
394462306a36Sopenharmony_ci	__le32	bar_thrs;
394562306a36Sopenharmony_ci	__le32	window_size;
394662306a36Sopenharmony_ci	u8	peer_mac_addr[6];
394762306a36Sopenharmony_ci	u8	dialog_token;
394862306a36Sopenharmony_ci	u8	tid;
394962306a36Sopenharmony_ci	u8	queue_id;
395062306a36Sopenharmony_ci	u8	param_info;
395162306a36Sopenharmony_ci	__le32	ba_context;
395262306a36Sopenharmony_ci	u8	reset_seq_no_flag;
395362306a36Sopenharmony_ci	__le16	curr_seq_no;
395462306a36Sopenharmony_ci	u8	sta_src_mac_addr[6];
395562306a36Sopenharmony_ci} __packed;
395662306a36Sopenharmony_ci
395762306a36Sopenharmony_cistruct mwl8k_destroy_ba_stream {
395862306a36Sopenharmony_ci	__le32	flags;
395962306a36Sopenharmony_ci	__le32	ba_context;
396062306a36Sopenharmony_ci} __packed;
396162306a36Sopenharmony_ci
396262306a36Sopenharmony_cistruct mwl8k_cmd_bastream {
396362306a36Sopenharmony_ci	struct mwl8k_cmd_pkt	header;
396462306a36Sopenharmony_ci	__le32	action;
396562306a36Sopenharmony_ci	union {
396662306a36Sopenharmony_ci		struct mwl8k_create_ba_stream	create_params;
396762306a36Sopenharmony_ci		struct mwl8k_destroy_ba_stream	destroy_params;
396862306a36Sopenharmony_ci	};
396962306a36Sopenharmony_ci} __packed;
397062306a36Sopenharmony_ci
397162306a36Sopenharmony_cistatic int
397262306a36Sopenharmony_cimwl8k_check_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream,
397362306a36Sopenharmony_ci	       struct ieee80211_vif *vif)
397462306a36Sopenharmony_ci{
397562306a36Sopenharmony_ci	struct mwl8k_cmd_bastream *cmd;
397662306a36Sopenharmony_ci	int rc;
397762306a36Sopenharmony_ci
397862306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
397962306a36Sopenharmony_ci	if (cmd == NULL)
398062306a36Sopenharmony_ci		return -ENOMEM;
398162306a36Sopenharmony_ci
398262306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM);
398362306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
398462306a36Sopenharmony_ci
398562306a36Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_BA_CHECK);
398662306a36Sopenharmony_ci
398762306a36Sopenharmony_ci	cmd->create_params.queue_id = stream->idx;
398862306a36Sopenharmony_ci	memcpy(&cmd->create_params.peer_mac_addr[0], stream->sta->addr,
398962306a36Sopenharmony_ci	       ETH_ALEN);
399062306a36Sopenharmony_ci	cmd->create_params.tid = stream->tid;
399162306a36Sopenharmony_ci
399262306a36Sopenharmony_ci	cmd->create_params.flags =
399362306a36Sopenharmony_ci		cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE) |
399462306a36Sopenharmony_ci		cpu_to_le32(BASTREAM_FLAG_DIRECTION_UPSTREAM);
399562306a36Sopenharmony_ci
399662306a36Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
399762306a36Sopenharmony_ci
399862306a36Sopenharmony_ci	kfree(cmd);
399962306a36Sopenharmony_ci
400062306a36Sopenharmony_ci	return rc;
400162306a36Sopenharmony_ci}
400262306a36Sopenharmony_ci
400362306a36Sopenharmony_cistatic int
400462306a36Sopenharmony_cimwl8k_create_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream,
400562306a36Sopenharmony_ci		u8 buf_size, struct ieee80211_vif *vif)
400662306a36Sopenharmony_ci{
400762306a36Sopenharmony_ci	struct mwl8k_cmd_bastream *cmd;
400862306a36Sopenharmony_ci	int rc;
400962306a36Sopenharmony_ci
401062306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
401162306a36Sopenharmony_ci	if (cmd == NULL)
401262306a36Sopenharmony_ci		return -ENOMEM;
401362306a36Sopenharmony_ci
401462306a36Sopenharmony_ci
401562306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM);
401662306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
401762306a36Sopenharmony_ci
401862306a36Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_BA_CREATE);
401962306a36Sopenharmony_ci
402062306a36Sopenharmony_ci	cmd->create_params.bar_thrs = cpu_to_le32((u32)buf_size);
402162306a36Sopenharmony_ci	cmd->create_params.window_size = cpu_to_le32((u32)buf_size);
402262306a36Sopenharmony_ci	cmd->create_params.queue_id = stream->idx;
402362306a36Sopenharmony_ci
402462306a36Sopenharmony_ci	memcpy(cmd->create_params.peer_mac_addr, stream->sta->addr, ETH_ALEN);
402562306a36Sopenharmony_ci	cmd->create_params.tid = stream->tid;
402662306a36Sopenharmony_ci	cmd->create_params.curr_seq_no = cpu_to_le16(0);
402762306a36Sopenharmony_ci	cmd->create_params.reset_seq_no_flag = 1;
402862306a36Sopenharmony_ci
402962306a36Sopenharmony_ci	cmd->create_params.param_info =
403062306a36Sopenharmony_ci		(stream->sta->deflink.ht_cap.ampdu_factor &
403162306a36Sopenharmony_ci		 IEEE80211_HT_AMPDU_PARM_FACTOR) |
403262306a36Sopenharmony_ci		((stream->sta->deflink.ht_cap.ampdu_density << 2) &
403362306a36Sopenharmony_ci		 IEEE80211_HT_AMPDU_PARM_DENSITY);
403462306a36Sopenharmony_ci
403562306a36Sopenharmony_ci	cmd->create_params.flags =
403662306a36Sopenharmony_ci		cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE |
403762306a36Sopenharmony_ci					BASTREAM_FLAG_DIRECTION_UPSTREAM);
403862306a36Sopenharmony_ci
403962306a36Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
404062306a36Sopenharmony_ci
404162306a36Sopenharmony_ci	wiphy_debug(hw->wiphy, "Created a BA stream for %pM : tid %d\n",
404262306a36Sopenharmony_ci		stream->sta->addr, stream->tid);
404362306a36Sopenharmony_ci	kfree(cmd);
404462306a36Sopenharmony_ci
404562306a36Sopenharmony_ci	return rc;
404662306a36Sopenharmony_ci}
404762306a36Sopenharmony_ci
404862306a36Sopenharmony_cistatic void mwl8k_destroy_ba(struct ieee80211_hw *hw,
404962306a36Sopenharmony_ci			     u8 idx)
405062306a36Sopenharmony_ci{
405162306a36Sopenharmony_ci	struct mwl8k_cmd_bastream *cmd;
405262306a36Sopenharmony_ci
405362306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
405462306a36Sopenharmony_ci	if (cmd == NULL)
405562306a36Sopenharmony_ci		return;
405662306a36Sopenharmony_ci
405762306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM);
405862306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
405962306a36Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_BA_DESTROY);
406062306a36Sopenharmony_ci
406162306a36Sopenharmony_ci	cmd->destroy_params.ba_context = cpu_to_le32(idx);
406262306a36Sopenharmony_ci	mwl8k_post_cmd(hw, &cmd->header);
406362306a36Sopenharmony_ci
406462306a36Sopenharmony_ci	wiphy_debug(hw->wiphy, "Deleted BA stream index %d\n", idx);
406562306a36Sopenharmony_ci
406662306a36Sopenharmony_ci	kfree(cmd);
406762306a36Sopenharmony_ci}
406862306a36Sopenharmony_ci
406962306a36Sopenharmony_ci/*
407062306a36Sopenharmony_ci * CMD_SET_NEW_STN.
407162306a36Sopenharmony_ci */
407262306a36Sopenharmony_cistruct mwl8k_cmd_set_new_stn {
407362306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
407462306a36Sopenharmony_ci	__le16 aid;
407562306a36Sopenharmony_ci	__u8 mac_addr[6];
407662306a36Sopenharmony_ci	__le16 stn_id;
407762306a36Sopenharmony_ci	__le16 action;
407862306a36Sopenharmony_ci	__le16 rsvd;
407962306a36Sopenharmony_ci	__le32 legacy_rates;
408062306a36Sopenharmony_ci	__u8 ht_rates[4];
408162306a36Sopenharmony_ci	__le16 cap_info;
408262306a36Sopenharmony_ci	__le16 ht_capabilities_info;
408362306a36Sopenharmony_ci	__u8 mac_ht_param_info;
408462306a36Sopenharmony_ci	__u8 rev;
408562306a36Sopenharmony_ci	__u8 control_channel;
408662306a36Sopenharmony_ci	__u8 add_channel;
408762306a36Sopenharmony_ci	__le16 op_mode;
408862306a36Sopenharmony_ci	__le16 stbc;
408962306a36Sopenharmony_ci	__u8 add_qos_info;
409062306a36Sopenharmony_ci	__u8 is_qos_sta;
409162306a36Sopenharmony_ci	__le32 fw_sta_ptr;
409262306a36Sopenharmony_ci} __packed;
409362306a36Sopenharmony_ci
409462306a36Sopenharmony_ci#define MWL8K_STA_ACTION_ADD		0
409562306a36Sopenharmony_ci#define MWL8K_STA_ACTION_REMOVE		2
409662306a36Sopenharmony_ci
409762306a36Sopenharmony_cistatic int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw,
409862306a36Sopenharmony_ci				     struct ieee80211_vif *vif,
409962306a36Sopenharmony_ci				     struct ieee80211_sta *sta)
410062306a36Sopenharmony_ci{
410162306a36Sopenharmony_ci	struct mwl8k_cmd_set_new_stn *cmd;
410262306a36Sopenharmony_ci	u32 rates;
410362306a36Sopenharmony_ci	int rc;
410462306a36Sopenharmony_ci
410562306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
410662306a36Sopenharmony_ci	if (cmd == NULL)
410762306a36Sopenharmony_ci		return -ENOMEM;
410862306a36Sopenharmony_ci
410962306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
411062306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
411162306a36Sopenharmony_ci	cmd->aid = cpu_to_le16(sta->aid);
411262306a36Sopenharmony_ci	memcpy(cmd->mac_addr, sta->addr, ETH_ALEN);
411362306a36Sopenharmony_ci	cmd->stn_id = cpu_to_le16(sta->aid);
411462306a36Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_STA_ACTION_ADD);
411562306a36Sopenharmony_ci	if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ)
411662306a36Sopenharmony_ci		rates = sta->deflink.supp_rates[NL80211_BAND_2GHZ];
411762306a36Sopenharmony_ci	else
411862306a36Sopenharmony_ci		rates = sta->deflink.supp_rates[NL80211_BAND_5GHZ] << 5;
411962306a36Sopenharmony_ci	cmd->legacy_rates = cpu_to_le32(rates);
412062306a36Sopenharmony_ci	if (sta->deflink.ht_cap.ht_supported) {
412162306a36Sopenharmony_ci		cmd->ht_rates[0] = sta->deflink.ht_cap.mcs.rx_mask[0];
412262306a36Sopenharmony_ci		cmd->ht_rates[1] = sta->deflink.ht_cap.mcs.rx_mask[1];
412362306a36Sopenharmony_ci		cmd->ht_rates[2] = sta->deflink.ht_cap.mcs.rx_mask[2];
412462306a36Sopenharmony_ci		cmd->ht_rates[3] = sta->deflink.ht_cap.mcs.rx_mask[3];
412562306a36Sopenharmony_ci		cmd->ht_capabilities_info = cpu_to_le16(sta->deflink.ht_cap.cap);
412662306a36Sopenharmony_ci		cmd->mac_ht_param_info = (sta->deflink.ht_cap.ampdu_factor & 3) |
412762306a36Sopenharmony_ci			((sta->deflink.ht_cap.ampdu_density & 7) << 2);
412862306a36Sopenharmony_ci		cmd->is_qos_sta = 1;
412962306a36Sopenharmony_ci	}
413062306a36Sopenharmony_ci
413162306a36Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
413262306a36Sopenharmony_ci	kfree(cmd);
413362306a36Sopenharmony_ci
413462306a36Sopenharmony_ci	return rc;
413562306a36Sopenharmony_ci}
413662306a36Sopenharmony_ci
413762306a36Sopenharmony_cistatic int mwl8k_cmd_set_new_stn_add_self(struct ieee80211_hw *hw,
413862306a36Sopenharmony_ci					  struct ieee80211_vif *vif)
413962306a36Sopenharmony_ci{
414062306a36Sopenharmony_ci	struct mwl8k_cmd_set_new_stn *cmd;
414162306a36Sopenharmony_ci	int rc;
414262306a36Sopenharmony_ci
414362306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
414462306a36Sopenharmony_ci	if (cmd == NULL)
414562306a36Sopenharmony_ci		return -ENOMEM;
414662306a36Sopenharmony_ci
414762306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
414862306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
414962306a36Sopenharmony_ci	memcpy(cmd->mac_addr, vif->addr, ETH_ALEN);
415062306a36Sopenharmony_ci
415162306a36Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
415262306a36Sopenharmony_ci	kfree(cmd);
415362306a36Sopenharmony_ci
415462306a36Sopenharmony_ci	return rc;
415562306a36Sopenharmony_ci}
415662306a36Sopenharmony_ci
415762306a36Sopenharmony_cistatic int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw,
415862306a36Sopenharmony_ci				     struct ieee80211_vif *vif, u8 *addr)
415962306a36Sopenharmony_ci{
416062306a36Sopenharmony_ci	struct mwl8k_cmd_set_new_stn *cmd;
416162306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
416262306a36Sopenharmony_ci	int rc, i;
416362306a36Sopenharmony_ci	u8 idx;
416462306a36Sopenharmony_ci
416562306a36Sopenharmony_ci	spin_lock(&priv->stream_lock);
416662306a36Sopenharmony_ci	/* Destroy any active ampdu streams for this sta */
416762306a36Sopenharmony_ci	for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) {
416862306a36Sopenharmony_ci		struct mwl8k_ampdu_stream *s;
416962306a36Sopenharmony_ci		s = &priv->ampdu[i];
417062306a36Sopenharmony_ci		if (s->state != AMPDU_NO_STREAM) {
417162306a36Sopenharmony_ci			if (memcmp(s->sta->addr, addr, ETH_ALEN) == 0) {
417262306a36Sopenharmony_ci				if (s->state == AMPDU_STREAM_ACTIVE) {
417362306a36Sopenharmony_ci					idx = s->idx;
417462306a36Sopenharmony_ci					spin_unlock(&priv->stream_lock);
417562306a36Sopenharmony_ci					mwl8k_destroy_ba(hw, idx);
417662306a36Sopenharmony_ci					spin_lock(&priv->stream_lock);
417762306a36Sopenharmony_ci				} else if (s->state == AMPDU_STREAM_NEW) {
417862306a36Sopenharmony_ci					mwl8k_remove_stream(hw, s);
417962306a36Sopenharmony_ci				}
418062306a36Sopenharmony_ci			}
418162306a36Sopenharmony_ci		}
418262306a36Sopenharmony_ci	}
418362306a36Sopenharmony_ci
418462306a36Sopenharmony_ci	spin_unlock(&priv->stream_lock);
418562306a36Sopenharmony_ci
418662306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
418762306a36Sopenharmony_ci	if (cmd == NULL)
418862306a36Sopenharmony_ci		return -ENOMEM;
418962306a36Sopenharmony_ci
419062306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
419162306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
419262306a36Sopenharmony_ci	memcpy(cmd->mac_addr, addr, ETH_ALEN);
419362306a36Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_STA_ACTION_REMOVE);
419462306a36Sopenharmony_ci
419562306a36Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
419662306a36Sopenharmony_ci	kfree(cmd);
419762306a36Sopenharmony_ci
419862306a36Sopenharmony_ci	return rc;
419962306a36Sopenharmony_ci}
420062306a36Sopenharmony_ci
420162306a36Sopenharmony_ci/*
420262306a36Sopenharmony_ci * CMD_UPDATE_ENCRYPTION.
420362306a36Sopenharmony_ci */
420462306a36Sopenharmony_ci
420562306a36Sopenharmony_ci#define MAX_ENCR_KEY_LENGTH	16
420662306a36Sopenharmony_ci#define MIC_KEY_LENGTH		8
420762306a36Sopenharmony_ci
420862306a36Sopenharmony_cistruct mwl8k_cmd_update_encryption {
420962306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
421062306a36Sopenharmony_ci
421162306a36Sopenharmony_ci	__le32 action;
421262306a36Sopenharmony_ci	__le32 reserved;
421362306a36Sopenharmony_ci	__u8 mac_addr[6];
421462306a36Sopenharmony_ci	__u8 encr_type;
421562306a36Sopenharmony_ci
421662306a36Sopenharmony_ci} __packed;
421762306a36Sopenharmony_ci
421862306a36Sopenharmony_cistruct mwl8k_cmd_set_key {
421962306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
422062306a36Sopenharmony_ci
422162306a36Sopenharmony_ci	__le32 action;
422262306a36Sopenharmony_ci	__le32 reserved;
422362306a36Sopenharmony_ci	__le16 length;
422462306a36Sopenharmony_ci	__le16 key_type_id;
422562306a36Sopenharmony_ci	__le32 key_info;
422662306a36Sopenharmony_ci	__le32 key_id;
422762306a36Sopenharmony_ci	__le16 key_len;
422862306a36Sopenharmony_ci	struct {
422962306a36Sopenharmony_ci		__u8 key_material[MAX_ENCR_KEY_LENGTH];
423062306a36Sopenharmony_ci		__u8 tkip_tx_mic_key[MIC_KEY_LENGTH];
423162306a36Sopenharmony_ci		__u8 tkip_rx_mic_key[MIC_KEY_LENGTH];
423262306a36Sopenharmony_ci	} tkip;
423362306a36Sopenharmony_ci	__le16 tkip_rsc_low;
423462306a36Sopenharmony_ci	__le32 tkip_rsc_high;
423562306a36Sopenharmony_ci	__le16 tkip_tsc_low;
423662306a36Sopenharmony_ci	__le32 tkip_tsc_high;
423762306a36Sopenharmony_ci	__u8 mac_addr[6];
423862306a36Sopenharmony_ci} __packed;
423962306a36Sopenharmony_ci
424062306a36Sopenharmony_cienum {
424162306a36Sopenharmony_ci	MWL8K_ENCR_ENABLE,
424262306a36Sopenharmony_ci	MWL8K_ENCR_SET_KEY,
424362306a36Sopenharmony_ci	MWL8K_ENCR_REMOVE_KEY,
424462306a36Sopenharmony_ci	MWL8K_ENCR_SET_GROUP_KEY,
424562306a36Sopenharmony_ci};
424662306a36Sopenharmony_ci
424762306a36Sopenharmony_ci#define MWL8K_UPDATE_ENCRYPTION_TYPE_WEP	0
424862306a36Sopenharmony_ci#define MWL8K_UPDATE_ENCRYPTION_TYPE_DISABLE	1
424962306a36Sopenharmony_ci#define MWL8K_UPDATE_ENCRYPTION_TYPE_TKIP	4
425062306a36Sopenharmony_ci#define MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED	7
425162306a36Sopenharmony_ci#define MWL8K_UPDATE_ENCRYPTION_TYPE_AES	8
425262306a36Sopenharmony_ci
425362306a36Sopenharmony_cienum {
425462306a36Sopenharmony_ci	MWL8K_ALG_WEP,
425562306a36Sopenharmony_ci	MWL8K_ALG_TKIP,
425662306a36Sopenharmony_ci	MWL8K_ALG_CCMP,
425762306a36Sopenharmony_ci};
425862306a36Sopenharmony_ci
425962306a36Sopenharmony_ci#define MWL8K_KEY_FLAG_TXGROUPKEY	0x00000004
426062306a36Sopenharmony_ci#define MWL8K_KEY_FLAG_PAIRWISE		0x00000008
426162306a36Sopenharmony_ci#define MWL8K_KEY_FLAG_TSC_VALID	0x00000040
426262306a36Sopenharmony_ci#define MWL8K_KEY_FLAG_WEP_TXKEY	0x01000000
426362306a36Sopenharmony_ci#define MWL8K_KEY_FLAG_MICKEY_VALID	0x02000000
426462306a36Sopenharmony_ci
426562306a36Sopenharmony_cistatic int mwl8k_cmd_update_encryption_enable(struct ieee80211_hw *hw,
426662306a36Sopenharmony_ci					      struct ieee80211_vif *vif,
426762306a36Sopenharmony_ci					      u8 *addr,
426862306a36Sopenharmony_ci					      u8 encr_type)
426962306a36Sopenharmony_ci{
427062306a36Sopenharmony_ci	struct mwl8k_cmd_update_encryption *cmd;
427162306a36Sopenharmony_ci	int rc;
427262306a36Sopenharmony_ci
427362306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
427462306a36Sopenharmony_ci	if (cmd == NULL)
427562306a36Sopenharmony_ci		return -ENOMEM;
427662306a36Sopenharmony_ci
427762306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION);
427862306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
427962306a36Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_ENCR_ENABLE);
428062306a36Sopenharmony_ci	memcpy(cmd->mac_addr, addr, ETH_ALEN);
428162306a36Sopenharmony_ci	cmd->encr_type = encr_type;
428262306a36Sopenharmony_ci
428362306a36Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
428462306a36Sopenharmony_ci	kfree(cmd);
428562306a36Sopenharmony_ci
428662306a36Sopenharmony_ci	return rc;
428762306a36Sopenharmony_ci}
428862306a36Sopenharmony_ci
428962306a36Sopenharmony_cistatic int mwl8k_encryption_set_cmd_info(struct mwl8k_cmd_set_key *cmd,
429062306a36Sopenharmony_ci						u8 *addr,
429162306a36Sopenharmony_ci						struct ieee80211_key_conf *key)
429262306a36Sopenharmony_ci{
429362306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION);
429462306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
429562306a36Sopenharmony_ci	cmd->length = cpu_to_le16(sizeof(*cmd) -
429662306a36Sopenharmony_ci				offsetof(struct mwl8k_cmd_set_key, length));
429762306a36Sopenharmony_ci	cmd->key_id = cpu_to_le32(key->keyidx);
429862306a36Sopenharmony_ci	cmd->key_len = cpu_to_le16(key->keylen);
429962306a36Sopenharmony_ci	memcpy(cmd->mac_addr, addr, ETH_ALEN);
430062306a36Sopenharmony_ci
430162306a36Sopenharmony_ci	switch (key->cipher) {
430262306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP40:
430362306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP104:
430462306a36Sopenharmony_ci		cmd->key_type_id = cpu_to_le16(MWL8K_ALG_WEP);
430562306a36Sopenharmony_ci		if (key->keyidx == 0)
430662306a36Sopenharmony_ci			cmd->key_info =	cpu_to_le32(MWL8K_KEY_FLAG_WEP_TXKEY);
430762306a36Sopenharmony_ci
430862306a36Sopenharmony_ci		break;
430962306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
431062306a36Sopenharmony_ci		cmd->key_type_id = cpu_to_le16(MWL8K_ALG_TKIP);
431162306a36Sopenharmony_ci		cmd->key_info =	(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
431262306a36Sopenharmony_ci			? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE)
431362306a36Sopenharmony_ci			: cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY);
431462306a36Sopenharmony_ci		cmd->key_info |= cpu_to_le32(MWL8K_KEY_FLAG_MICKEY_VALID
431562306a36Sopenharmony_ci						| MWL8K_KEY_FLAG_TSC_VALID);
431662306a36Sopenharmony_ci		break;
431762306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
431862306a36Sopenharmony_ci		cmd->key_type_id = cpu_to_le16(MWL8K_ALG_CCMP);
431962306a36Sopenharmony_ci		cmd->key_info =	(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
432062306a36Sopenharmony_ci			? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE)
432162306a36Sopenharmony_ci			: cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY);
432262306a36Sopenharmony_ci		break;
432362306a36Sopenharmony_ci	default:
432462306a36Sopenharmony_ci		return -ENOTSUPP;
432562306a36Sopenharmony_ci	}
432662306a36Sopenharmony_ci
432762306a36Sopenharmony_ci	return 0;
432862306a36Sopenharmony_ci}
432962306a36Sopenharmony_ci
433062306a36Sopenharmony_cistatic int mwl8k_cmd_encryption_set_key(struct ieee80211_hw *hw,
433162306a36Sopenharmony_ci						struct ieee80211_vif *vif,
433262306a36Sopenharmony_ci						u8 *addr,
433362306a36Sopenharmony_ci						struct ieee80211_key_conf *key)
433462306a36Sopenharmony_ci{
433562306a36Sopenharmony_ci	struct mwl8k_cmd_set_key *cmd;
433662306a36Sopenharmony_ci	int rc;
433762306a36Sopenharmony_ci	int keymlen;
433862306a36Sopenharmony_ci	u32 action;
433962306a36Sopenharmony_ci	u8 idx;
434062306a36Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
434162306a36Sopenharmony_ci
434262306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
434362306a36Sopenharmony_ci	if (cmd == NULL)
434462306a36Sopenharmony_ci		return -ENOMEM;
434562306a36Sopenharmony_ci
434662306a36Sopenharmony_ci	rc = mwl8k_encryption_set_cmd_info(cmd, addr, key);
434762306a36Sopenharmony_ci	if (rc < 0)
434862306a36Sopenharmony_ci		goto done;
434962306a36Sopenharmony_ci
435062306a36Sopenharmony_ci	idx = key->keyidx;
435162306a36Sopenharmony_ci
435262306a36Sopenharmony_ci	if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
435362306a36Sopenharmony_ci		action = MWL8K_ENCR_SET_KEY;
435462306a36Sopenharmony_ci	else
435562306a36Sopenharmony_ci		action = MWL8K_ENCR_SET_GROUP_KEY;
435662306a36Sopenharmony_ci
435762306a36Sopenharmony_ci	switch (key->cipher) {
435862306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP40:
435962306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP104:
436062306a36Sopenharmony_ci		if (!mwl8k_vif->wep_key_conf[idx].enabled) {
436162306a36Sopenharmony_ci			memcpy(mwl8k_vif->wep_key_conf[idx].key, key,
436262306a36Sopenharmony_ci						sizeof(*key) + key->keylen);
436362306a36Sopenharmony_ci			mwl8k_vif->wep_key_conf[idx].enabled = 1;
436462306a36Sopenharmony_ci		}
436562306a36Sopenharmony_ci
436662306a36Sopenharmony_ci		keymlen = key->keylen;
436762306a36Sopenharmony_ci		action = MWL8K_ENCR_SET_KEY;
436862306a36Sopenharmony_ci		break;
436962306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
437062306a36Sopenharmony_ci		keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH;
437162306a36Sopenharmony_ci		break;
437262306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
437362306a36Sopenharmony_ci		keymlen = key->keylen;
437462306a36Sopenharmony_ci		break;
437562306a36Sopenharmony_ci	default:
437662306a36Sopenharmony_ci		rc = -ENOTSUPP;
437762306a36Sopenharmony_ci		goto done;
437862306a36Sopenharmony_ci	}
437962306a36Sopenharmony_ci
438062306a36Sopenharmony_ci	memcpy(&cmd->tkip, key->key, keymlen);
438162306a36Sopenharmony_ci	cmd->action = cpu_to_le32(action);
438262306a36Sopenharmony_ci
438362306a36Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
438462306a36Sopenharmony_cidone:
438562306a36Sopenharmony_ci	kfree(cmd);
438662306a36Sopenharmony_ci
438762306a36Sopenharmony_ci	return rc;
438862306a36Sopenharmony_ci}
438962306a36Sopenharmony_ci
439062306a36Sopenharmony_cistatic int mwl8k_cmd_encryption_remove_key(struct ieee80211_hw *hw,
439162306a36Sopenharmony_ci						struct ieee80211_vif *vif,
439262306a36Sopenharmony_ci						u8 *addr,
439362306a36Sopenharmony_ci						struct ieee80211_key_conf *key)
439462306a36Sopenharmony_ci{
439562306a36Sopenharmony_ci	struct mwl8k_cmd_set_key *cmd;
439662306a36Sopenharmony_ci	int rc;
439762306a36Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
439862306a36Sopenharmony_ci
439962306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
440062306a36Sopenharmony_ci	if (cmd == NULL)
440162306a36Sopenharmony_ci		return -ENOMEM;
440262306a36Sopenharmony_ci
440362306a36Sopenharmony_ci	rc = mwl8k_encryption_set_cmd_info(cmd, addr, key);
440462306a36Sopenharmony_ci	if (rc < 0)
440562306a36Sopenharmony_ci		goto done;
440662306a36Sopenharmony_ci
440762306a36Sopenharmony_ci	if (key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
440862306a36Sopenharmony_ci			key->cipher == WLAN_CIPHER_SUITE_WEP104)
440962306a36Sopenharmony_ci		mwl8k_vif->wep_key_conf[key->keyidx].enabled = 0;
441062306a36Sopenharmony_ci
441162306a36Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_ENCR_REMOVE_KEY);
441262306a36Sopenharmony_ci
441362306a36Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
441462306a36Sopenharmony_cidone:
441562306a36Sopenharmony_ci	kfree(cmd);
441662306a36Sopenharmony_ci
441762306a36Sopenharmony_ci	return rc;
441862306a36Sopenharmony_ci}
441962306a36Sopenharmony_ci
442062306a36Sopenharmony_cistatic int mwl8k_set_key(struct ieee80211_hw *hw,
442162306a36Sopenharmony_ci			 enum set_key_cmd cmd_param,
442262306a36Sopenharmony_ci			 struct ieee80211_vif *vif,
442362306a36Sopenharmony_ci			 struct ieee80211_sta *sta,
442462306a36Sopenharmony_ci			 struct ieee80211_key_conf *key)
442562306a36Sopenharmony_ci{
442662306a36Sopenharmony_ci	int rc = 0;
442762306a36Sopenharmony_ci	u8 encr_type;
442862306a36Sopenharmony_ci	u8 *addr;
442962306a36Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
443062306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
443162306a36Sopenharmony_ci
443262306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_STATION && !priv->ap_fw)
443362306a36Sopenharmony_ci		return -EOPNOTSUPP;
443462306a36Sopenharmony_ci
443562306a36Sopenharmony_ci	if (sta == NULL)
443662306a36Sopenharmony_ci		addr = vif->addr;
443762306a36Sopenharmony_ci	else
443862306a36Sopenharmony_ci		addr = sta->addr;
443962306a36Sopenharmony_ci
444062306a36Sopenharmony_ci	if (cmd_param == SET_KEY) {
444162306a36Sopenharmony_ci		rc = mwl8k_cmd_encryption_set_key(hw, vif, addr, key);
444262306a36Sopenharmony_ci		if (rc)
444362306a36Sopenharmony_ci			goto out;
444462306a36Sopenharmony_ci
444562306a36Sopenharmony_ci		if ((key->cipher == WLAN_CIPHER_SUITE_WEP40)
444662306a36Sopenharmony_ci				|| (key->cipher == WLAN_CIPHER_SUITE_WEP104))
444762306a36Sopenharmony_ci			encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_WEP;
444862306a36Sopenharmony_ci		else
444962306a36Sopenharmony_ci			encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED;
445062306a36Sopenharmony_ci
445162306a36Sopenharmony_ci		rc = mwl8k_cmd_update_encryption_enable(hw, vif, addr,
445262306a36Sopenharmony_ci								encr_type);
445362306a36Sopenharmony_ci		if (rc)
445462306a36Sopenharmony_ci			goto out;
445562306a36Sopenharmony_ci
445662306a36Sopenharmony_ci		mwl8k_vif->is_hw_crypto_enabled = true;
445762306a36Sopenharmony_ci
445862306a36Sopenharmony_ci	} else {
445962306a36Sopenharmony_ci		rc = mwl8k_cmd_encryption_remove_key(hw, vif, addr, key);
446062306a36Sopenharmony_ci
446162306a36Sopenharmony_ci		if (rc)
446262306a36Sopenharmony_ci			goto out;
446362306a36Sopenharmony_ci	}
446462306a36Sopenharmony_ciout:
446562306a36Sopenharmony_ci	return rc;
446662306a36Sopenharmony_ci}
446762306a36Sopenharmony_ci
446862306a36Sopenharmony_ci/*
446962306a36Sopenharmony_ci * CMD_UPDATE_STADB.
447062306a36Sopenharmony_ci */
447162306a36Sopenharmony_cistruct ewc_ht_info {
447262306a36Sopenharmony_ci	__le16	control1;
447362306a36Sopenharmony_ci	__le16	control2;
447462306a36Sopenharmony_ci	__le16	control3;
447562306a36Sopenharmony_ci} __packed;
447662306a36Sopenharmony_ci
447762306a36Sopenharmony_cistruct peer_capability_info {
447862306a36Sopenharmony_ci	/* Peer type - AP vs. STA.  */
447962306a36Sopenharmony_ci	__u8	peer_type;
448062306a36Sopenharmony_ci
448162306a36Sopenharmony_ci	/* Basic 802.11 capabilities from assoc resp.  */
448262306a36Sopenharmony_ci	__le16	basic_caps;
448362306a36Sopenharmony_ci
448462306a36Sopenharmony_ci	/* Set if peer supports 802.11n high throughput (HT).  */
448562306a36Sopenharmony_ci	__u8	ht_support;
448662306a36Sopenharmony_ci
448762306a36Sopenharmony_ci	/* Valid if HT is supported.  */
448862306a36Sopenharmony_ci	__le16	ht_caps;
448962306a36Sopenharmony_ci	__u8	extended_ht_caps;
449062306a36Sopenharmony_ci	struct ewc_ht_info	ewc_info;
449162306a36Sopenharmony_ci
449262306a36Sopenharmony_ci	/* Legacy rate table. Intersection of our rates and peer rates.  */
449362306a36Sopenharmony_ci	__u8	legacy_rates[12];
449462306a36Sopenharmony_ci
449562306a36Sopenharmony_ci	/* HT rate table. Intersection of our rates and peer rates.  */
449662306a36Sopenharmony_ci	__u8	ht_rates[16];
449762306a36Sopenharmony_ci	__u8	pad[16];
449862306a36Sopenharmony_ci
449962306a36Sopenharmony_ci	/* If set, interoperability mode, no proprietary extensions.  */
450062306a36Sopenharmony_ci	__u8	interop;
450162306a36Sopenharmony_ci	__u8	pad2;
450262306a36Sopenharmony_ci	__u8	station_id;
450362306a36Sopenharmony_ci	__le16	amsdu_enabled;
450462306a36Sopenharmony_ci} __packed;
450562306a36Sopenharmony_ci
450662306a36Sopenharmony_cistruct mwl8k_cmd_update_stadb {
450762306a36Sopenharmony_ci	struct mwl8k_cmd_pkt header;
450862306a36Sopenharmony_ci
450962306a36Sopenharmony_ci	/* See STADB_ACTION_TYPE */
451062306a36Sopenharmony_ci	__le32	action;
451162306a36Sopenharmony_ci
451262306a36Sopenharmony_ci	/* Peer MAC address */
451362306a36Sopenharmony_ci	__u8	peer_addr[ETH_ALEN];
451462306a36Sopenharmony_ci
451562306a36Sopenharmony_ci	__le32	reserved;
451662306a36Sopenharmony_ci
451762306a36Sopenharmony_ci	/* Peer info - valid during add/update.  */
451862306a36Sopenharmony_ci	struct peer_capability_info	peer_info;
451962306a36Sopenharmony_ci} __packed;
452062306a36Sopenharmony_ci
452162306a36Sopenharmony_ci#define MWL8K_STA_DB_MODIFY_ENTRY	1
452262306a36Sopenharmony_ci#define MWL8K_STA_DB_DEL_ENTRY		2
452362306a36Sopenharmony_ci
452462306a36Sopenharmony_ci/* Peer Entry flags - used to define the type of the peer node */
452562306a36Sopenharmony_ci#define MWL8K_PEER_TYPE_ACCESSPOINT	2
452662306a36Sopenharmony_ci
452762306a36Sopenharmony_cistatic int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw,
452862306a36Sopenharmony_ci				      struct ieee80211_vif *vif,
452962306a36Sopenharmony_ci				      struct ieee80211_sta *sta)
453062306a36Sopenharmony_ci{
453162306a36Sopenharmony_ci	struct mwl8k_cmd_update_stadb *cmd;
453262306a36Sopenharmony_ci	struct peer_capability_info *p;
453362306a36Sopenharmony_ci	u32 rates;
453462306a36Sopenharmony_ci	int rc;
453562306a36Sopenharmony_ci
453662306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
453762306a36Sopenharmony_ci	if (cmd == NULL)
453862306a36Sopenharmony_ci		return -ENOMEM;
453962306a36Sopenharmony_ci
454062306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB);
454162306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
454262306a36Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_STA_DB_MODIFY_ENTRY);
454362306a36Sopenharmony_ci	memcpy(cmd->peer_addr, sta->addr, ETH_ALEN);
454462306a36Sopenharmony_ci
454562306a36Sopenharmony_ci	p = &cmd->peer_info;
454662306a36Sopenharmony_ci	p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT;
454762306a36Sopenharmony_ci	p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability);
454862306a36Sopenharmony_ci	p->ht_support = sta->deflink.ht_cap.ht_supported;
454962306a36Sopenharmony_ci	p->ht_caps = cpu_to_le16(sta->deflink.ht_cap.cap);
455062306a36Sopenharmony_ci	p->extended_ht_caps = (sta->deflink.ht_cap.ampdu_factor & 3) |
455162306a36Sopenharmony_ci		((sta->deflink.ht_cap.ampdu_density & 7) << 2);
455262306a36Sopenharmony_ci	if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ)
455362306a36Sopenharmony_ci		rates = sta->deflink.supp_rates[NL80211_BAND_2GHZ];
455462306a36Sopenharmony_ci	else
455562306a36Sopenharmony_ci		rates = sta->deflink.supp_rates[NL80211_BAND_5GHZ] << 5;
455662306a36Sopenharmony_ci	legacy_rate_mask_to_array(p->legacy_rates, rates);
455762306a36Sopenharmony_ci	memcpy(p->ht_rates, &sta->deflink.ht_cap.mcs, 16);
455862306a36Sopenharmony_ci	p->interop = 1;
455962306a36Sopenharmony_ci	p->amsdu_enabled = 0;
456062306a36Sopenharmony_ci
456162306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
456262306a36Sopenharmony_ci	if (!rc)
456362306a36Sopenharmony_ci		rc = p->station_id;
456462306a36Sopenharmony_ci	kfree(cmd);
456562306a36Sopenharmony_ci
456662306a36Sopenharmony_ci	return rc;
456762306a36Sopenharmony_ci}
456862306a36Sopenharmony_ci
456962306a36Sopenharmony_cistatic int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw,
457062306a36Sopenharmony_ci				      struct ieee80211_vif *vif, u8 *addr)
457162306a36Sopenharmony_ci{
457262306a36Sopenharmony_ci	struct mwl8k_cmd_update_stadb *cmd;
457362306a36Sopenharmony_ci	int rc;
457462306a36Sopenharmony_ci
457562306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
457662306a36Sopenharmony_ci	if (cmd == NULL)
457762306a36Sopenharmony_ci		return -ENOMEM;
457862306a36Sopenharmony_ci
457962306a36Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB);
458062306a36Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
458162306a36Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_STA_DB_DEL_ENTRY);
458262306a36Sopenharmony_ci	memcpy(cmd->peer_addr, addr, ETH_ALEN);
458362306a36Sopenharmony_ci
458462306a36Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
458562306a36Sopenharmony_ci	kfree(cmd);
458662306a36Sopenharmony_ci
458762306a36Sopenharmony_ci	return rc;
458862306a36Sopenharmony_ci}
458962306a36Sopenharmony_ci
459062306a36Sopenharmony_ci
459162306a36Sopenharmony_ci/*
459262306a36Sopenharmony_ci * Interrupt handling.
459362306a36Sopenharmony_ci */
459462306a36Sopenharmony_cistatic irqreturn_t mwl8k_interrupt(int irq, void *dev_id)
459562306a36Sopenharmony_ci{
459662306a36Sopenharmony_ci	struct ieee80211_hw *hw = dev_id;
459762306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
459862306a36Sopenharmony_ci	u32 status;
459962306a36Sopenharmony_ci
460062306a36Sopenharmony_ci	status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
460162306a36Sopenharmony_ci	if (!status)
460262306a36Sopenharmony_ci		return IRQ_NONE;
460362306a36Sopenharmony_ci
460462306a36Sopenharmony_ci	if (status & MWL8K_A2H_INT_TX_DONE) {
460562306a36Sopenharmony_ci		status &= ~MWL8K_A2H_INT_TX_DONE;
460662306a36Sopenharmony_ci		tasklet_schedule(&priv->poll_tx_task);
460762306a36Sopenharmony_ci	}
460862306a36Sopenharmony_ci
460962306a36Sopenharmony_ci	if (status & MWL8K_A2H_INT_RX_READY) {
461062306a36Sopenharmony_ci		status &= ~MWL8K_A2H_INT_RX_READY;
461162306a36Sopenharmony_ci		tasklet_schedule(&priv->poll_rx_task);
461262306a36Sopenharmony_ci	}
461362306a36Sopenharmony_ci
461462306a36Sopenharmony_ci	if (status & MWL8K_A2H_INT_BA_WATCHDOG) {
461562306a36Sopenharmony_ci		iowrite32(~MWL8K_A2H_INT_BA_WATCHDOG,
461662306a36Sopenharmony_ci			  priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
461762306a36Sopenharmony_ci
461862306a36Sopenharmony_ci		atomic_inc(&priv->watchdog_event_pending);
461962306a36Sopenharmony_ci		status &= ~MWL8K_A2H_INT_BA_WATCHDOG;
462062306a36Sopenharmony_ci		ieee80211_queue_work(hw, &priv->watchdog_ba_handle);
462162306a36Sopenharmony_ci	}
462262306a36Sopenharmony_ci
462362306a36Sopenharmony_ci	if (status)
462462306a36Sopenharmony_ci		iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
462562306a36Sopenharmony_ci
462662306a36Sopenharmony_ci	if (status & MWL8K_A2H_INT_OPC_DONE) {
462762306a36Sopenharmony_ci		if (priv->hostcmd_wait != NULL)
462862306a36Sopenharmony_ci			complete(priv->hostcmd_wait);
462962306a36Sopenharmony_ci	}
463062306a36Sopenharmony_ci
463162306a36Sopenharmony_ci	if (status & MWL8K_A2H_INT_QUEUE_EMPTY) {
463262306a36Sopenharmony_ci		if (!mutex_is_locked(&priv->fw_mutex) &&
463362306a36Sopenharmony_ci		    priv->radio_on && priv->pending_tx_pkts)
463462306a36Sopenharmony_ci			mwl8k_tx_start(priv);
463562306a36Sopenharmony_ci	}
463662306a36Sopenharmony_ci
463762306a36Sopenharmony_ci	return IRQ_HANDLED;
463862306a36Sopenharmony_ci}
463962306a36Sopenharmony_ci
464062306a36Sopenharmony_cistatic void mwl8k_tx_poll(struct tasklet_struct *t)
464162306a36Sopenharmony_ci{
464262306a36Sopenharmony_ci	struct mwl8k_priv *priv = from_tasklet(priv, t, poll_tx_task);
464362306a36Sopenharmony_ci	struct ieee80211_hw *hw = pci_get_drvdata(priv->pdev);
464462306a36Sopenharmony_ci	int limit;
464562306a36Sopenharmony_ci	int i;
464662306a36Sopenharmony_ci
464762306a36Sopenharmony_ci	limit = 32;
464862306a36Sopenharmony_ci
464962306a36Sopenharmony_ci	spin_lock(&priv->tx_lock);
465062306a36Sopenharmony_ci
465162306a36Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
465262306a36Sopenharmony_ci		limit -= mwl8k_txq_reclaim(hw, i, limit, 0);
465362306a36Sopenharmony_ci
465462306a36Sopenharmony_ci	if (!priv->pending_tx_pkts && priv->tx_wait != NULL) {
465562306a36Sopenharmony_ci		complete(priv->tx_wait);
465662306a36Sopenharmony_ci		priv->tx_wait = NULL;
465762306a36Sopenharmony_ci	}
465862306a36Sopenharmony_ci
465962306a36Sopenharmony_ci	spin_unlock(&priv->tx_lock);
466062306a36Sopenharmony_ci
466162306a36Sopenharmony_ci	if (limit) {
466262306a36Sopenharmony_ci		writel(~MWL8K_A2H_INT_TX_DONE,
466362306a36Sopenharmony_ci		       priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
466462306a36Sopenharmony_ci	} else {
466562306a36Sopenharmony_ci		tasklet_schedule(&priv->poll_tx_task);
466662306a36Sopenharmony_ci	}
466762306a36Sopenharmony_ci}
466862306a36Sopenharmony_ci
466962306a36Sopenharmony_cistatic void mwl8k_rx_poll(struct tasklet_struct *t)
467062306a36Sopenharmony_ci{
467162306a36Sopenharmony_ci	struct mwl8k_priv *priv = from_tasklet(priv, t, poll_rx_task);
467262306a36Sopenharmony_ci	struct ieee80211_hw *hw = pci_get_drvdata(priv->pdev);
467362306a36Sopenharmony_ci	int limit;
467462306a36Sopenharmony_ci
467562306a36Sopenharmony_ci	limit = 32;
467662306a36Sopenharmony_ci	limit -= rxq_process(hw, 0, limit);
467762306a36Sopenharmony_ci	limit -= rxq_refill(hw, 0, limit);
467862306a36Sopenharmony_ci
467962306a36Sopenharmony_ci	if (limit) {
468062306a36Sopenharmony_ci		writel(~MWL8K_A2H_INT_RX_READY,
468162306a36Sopenharmony_ci		       priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
468262306a36Sopenharmony_ci	} else {
468362306a36Sopenharmony_ci		tasklet_schedule(&priv->poll_rx_task);
468462306a36Sopenharmony_ci	}
468562306a36Sopenharmony_ci}
468662306a36Sopenharmony_ci
468762306a36Sopenharmony_ci
468862306a36Sopenharmony_ci/*
468962306a36Sopenharmony_ci * Core driver operations.
469062306a36Sopenharmony_ci */
469162306a36Sopenharmony_cistatic void mwl8k_tx(struct ieee80211_hw *hw,
469262306a36Sopenharmony_ci		     struct ieee80211_tx_control *control,
469362306a36Sopenharmony_ci		     struct sk_buff *skb)
469462306a36Sopenharmony_ci{
469562306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
469662306a36Sopenharmony_ci	int index = skb_get_queue_mapping(skb);
469762306a36Sopenharmony_ci
469862306a36Sopenharmony_ci	if (!priv->radio_on) {
469962306a36Sopenharmony_ci		wiphy_debug(hw->wiphy,
470062306a36Sopenharmony_ci			    "dropped TX frame since radio disabled\n");
470162306a36Sopenharmony_ci		dev_kfree_skb(skb);
470262306a36Sopenharmony_ci		return;
470362306a36Sopenharmony_ci	}
470462306a36Sopenharmony_ci
470562306a36Sopenharmony_ci	mwl8k_txq_xmit(hw, index, control->sta, skb);
470662306a36Sopenharmony_ci}
470762306a36Sopenharmony_ci
470862306a36Sopenharmony_cistatic int mwl8k_start(struct ieee80211_hw *hw)
470962306a36Sopenharmony_ci{
471062306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
471162306a36Sopenharmony_ci	int rc;
471262306a36Sopenharmony_ci
471362306a36Sopenharmony_ci	rc = request_irq(priv->pdev->irq, mwl8k_interrupt,
471462306a36Sopenharmony_ci			 IRQF_SHARED, MWL8K_NAME, hw);
471562306a36Sopenharmony_ci	if (rc) {
471662306a36Sopenharmony_ci		priv->irq = -1;
471762306a36Sopenharmony_ci		wiphy_err(hw->wiphy, "failed to register IRQ handler\n");
471862306a36Sopenharmony_ci		return -EIO;
471962306a36Sopenharmony_ci	}
472062306a36Sopenharmony_ci	priv->irq = priv->pdev->irq;
472162306a36Sopenharmony_ci
472262306a36Sopenharmony_ci	/* Enable TX reclaim and RX tasklets.  */
472362306a36Sopenharmony_ci	tasklet_enable(&priv->poll_tx_task);
472462306a36Sopenharmony_ci	tasklet_enable(&priv->poll_rx_task);
472562306a36Sopenharmony_ci
472662306a36Sopenharmony_ci	/* Enable interrupts */
472762306a36Sopenharmony_ci	iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
472862306a36Sopenharmony_ci	iowrite32(MWL8K_A2H_EVENTS,
472962306a36Sopenharmony_ci		  priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
473062306a36Sopenharmony_ci
473162306a36Sopenharmony_ci	rc = mwl8k_fw_lock(hw);
473262306a36Sopenharmony_ci	if (!rc) {
473362306a36Sopenharmony_ci		rc = mwl8k_cmd_radio_enable(hw);
473462306a36Sopenharmony_ci
473562306a36Sopenharmony_ci		if (!priv->ap_fw) {
473662306a36Sopenharmony_ci			if (!rc)
473762306a36Sopenharmony_ci				rc = mwl8k_cmd_enable_sniffer(hw, 0);
473862306a36Sopenharmony_ci
473962306a36Sopenharmony_ci			if (!rc)
474062306a36Sopenharmony_ci				rc = mwl8k_cmd_set_pre_scan(hw);
474162306a36Sopenharmony_ci
474262306a36Sopenharmony_ci			if (!rc)
474362306a36Sopenharmony_ci				rc = mwl8k_cmd_set_post_scan(hw,
474462306a36Sopenharmony_ci						"\x00\x00\x00\x00\x00\x00");
474562306a36Sopenharmony_ci		}
474662306a36Sopenharmony_ci
474762306a36Sopenharmony_ci		if (!rc)
474862306a36Sopenharmony_ci			rc = mwl8k_cmd_set_rateadapt_mode(hw, 0);
474962306a36Sopenharmony_ci
475062306a36Sopenharmony_ci		if (!rc)
475162306a36Sopenharmony_ci			rc = mwl8k_cmd_set_wmm_mode(hw, 0);
475262306a36Sopenharmony_ci
475362306a36Sopenharmony_ci		mwl8k_fw_unlock(hw);
475462306a36Sopenharmony_ci	}
475562306a36Sopenharmony_ci
475662306a36Sopenharmony_ci	if (rc) {
475762306a36Sopenharmony_ci		iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
475862306a36Sopenharmony_ci		free_irq(priv->pdev->irq, hw);
475962306a36Sopenharmony_ci		priv->irq = -1;
476062306a36Sopenharmony_ci		tasklet_disable(&priv->poll_tx_task);
476162306a36Sopenharmony_ci		tasklet_disable(&priv->poll_rx_task);
476262306a36Sopenharmony_ci	} else {
476362306a36Sopenharmony_ci		ieee80211_wake_queues(hw);
476462306a36Sopenharmony_ci	}
476562306a36Sopenharmony_ci
476662306a36Sopenharmony_ci	return rc;
476762306a36Sopenharmony_ci}
476862306a36Sopenharmony_ci
476962306a36Sopenharmony_cistatic void mwl8k_stop(struct ieee80211_hw *hw)
477062306a36Sopenharmony_ci{
477162306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
477262306a36Sopenharmony_ci	int i;
477362306a36Sopenharmony_ci
477462306a36Sopenharmony_ci	if (!priv->hw_restart_in_progress)
477562306a36Sopenharmony_ci		mwl8k_cmd_radio_disable(hw);
477662306a36Sopenharmony_ci
477762306a36Sopenharmony_ci	ieee80211_stop_queues(hw);
477862306a36Sopenharmony_ci
477962306a36Sopenharmony_ci	/* Disable interrupts */
478062306a36Sopenharmony_ci	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
478162306a36Sopenharmony_ci	if (priv->irq != -1) {
478262306a36Sopenharmony_ci		free_irq(priv->pdev->irq, hw);
478362306a36Sopenharmony_ci		priv->irq = -1;
478462306a36Sopenharmony_ci	}
478562306a36Sopenharmony_ci
478662306a36Sopenharmony_ci	/* Stop finalize join worker */
478762306a36Sopenharmony_ci	cancel_work_sync(&priv->finalize_join_worker);
478862306a36Sopenharmony_ci	cancel_work_sync(&priv->watchdog_ba_handle);
478962306a36Sopenharmony_ci	if (priv->beacon_skb != NULL)
479062306a36Sopenharmony_ci		dev_kfree_skb(priv->beacon_skb);
479162306a36Sopenharmony_ci
479262306a36Sopenharmony_ci	/* Stop TX reclaim and RX tasklets.  */
479362306a36Sopenharmony_ci	tasklet_disable(&priv->poll_tx_task);
479462306a36Sopenharmony_ci	tasklet_disable(&priv->poll_rx_task);
479562306a36Sopenharmony_ci
479662306a36Sopenharmony_ci	/* Return all skbs to mac80211 */
479762306a36Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
479862306a36Sopenharmony_ci		mwl8k_txq_reclaim(hw, i, INT_MAX, 1);
479962306a36Sopenharmony_ci}
480062306a36Sopenharmony_ci
480162306a36Sopenharmony_cistatic int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image);
480262306a36Sopenharmony_ci
480362306a36Sopenharmony_cistatic int mwl8k_add_interface(struct ieee80211_hw *hw,
480462306a36Sopenharmony_ci			       struct ieee80211_vif *vif)
480562306a36Sopenharmony_ci{
480662306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
480762306a36Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif;
480862306a36Sopenharmony_ci	u32 macids_supported;
480962306a36Sopenharmony_ci	int macid, rc;
481062306a36Sopenharmony_ci	struct mwl8k_device_info *di;
481162306a36Sopenharmony_ci
481262306a36Sopenharmony_ci	/*
481362306a36Sopenharmony_ci	 * Reject interface creation if sniffer mode is active, as
481462306a36Sopenharmony_ci	 * STA operation is mutually exclusive with hardware sniffer
481562306a36Sopenharmony_ci	 * mode.  (Sniffer mode is only used on STA firmware.)
481662306a36Sopenharmony_ci	 */
481762306a36Sopenharmony_ci	if (priv->sniffer_enabled) {
481862306a36Sopenharmony_ci		wiphy_info(hw->wiphy,
481962306a36Sopenharmony_ci			   "unable to create STA interface because sniffer mode is enabled\n");
482062306a36Sopenharmony_ci		return -EINVAL;
482162306a36Sopenharmony_ci	}
482262306a36Sopenharmony_ci
482362306a36Sopenharmony_ci	di = priv->device_info;
482462306a36Sopenharmony_ci	switch (vif->type) {
482562306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
482662306a36Sopenharmony_ci		if (!priv->ap_fw && di->fw_image_ap) {
482762306a36Sopenharmony_ci			/* we must load the ap fw to meet this request */
482862306a36Sopenharmony_ci			if (!list_empty(&priv->vif_list))
482962306a36Sopenharmony_ci				return -EBUSY;
483062306a36Sopenharmony_ci			rc = mwl8k_reload_firmware(hw, di->fw_image_ap);
483162306a36Sopenharmony_ci			if (rc)
483262306a36Sopenharmony_ci				return rc;
483362306a36Sopenharmony_ci		}
483462306a36Sopenharmony_ci		macids_supported = priv->ap_macids_supported;
483562306a36Sopenharmony_ci		break;
483662306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
483762306a36Sopenharmony_ci		if (priv->ap_fw && di->fw_image_sta) {
483862306a36Sopenharmony_ci			if (!list_empty(&priv->vif_list)) {
483962306a36Sopenharmony_ci				wiphy_warn(hw->wiphy, "AP interface is running.\n"
484062306a36Sopenharmony_ci					   "Adding STA interface for WDS");
484162306a36Sopenharmony_ci			} else {
484262306a36Sopenharmony_ci				/* we must load the sta fw to
484362306a36Sopenharmony_ci				 * meet this request.
484462306a36Sopenharmony_ci				 */
484562306a36Sopenharmony_ci				rc = mwl8k_reload_firmware(hw,
484662306a36Sopenharmony_ci							   di->fw_image_sta);
484762306a36Sopenharmony_ci				if (rc)
484862306a36Sopenharmony_ci					return rc;
484962306a36Sopenharmony_ci			}
485062306a36Sopenharmony_ci		}
485162306a36Sopenharmony_ci		macids_supported = priv->sta_macids_supported;
485262306a36Sopenharmony_ci		break;
485362306a36Sopenharmony_ci	default:
485462306a36Sopenharmony_ci		return -EINVAL;
485562306a36Sopenharmony_ci	}
485662306a36Sopenharmony_ci
485762306a36Sopenharmony_ci	macid = ffs(macids_supported & ~priv->macids_used);
485862306a36Sopenharmony_ci	if (!macid--)
485962306a36Sopenharmony_ci		return -EBUSY;
486062306a36Sopenharmony_ci
486162306a36Sopenharmony_ci	/* Setup driver private area. */
486262306a36Sopenharmony_ci	mwl8k_vif = MWL8K_VIF(vif);
486362306a36Sopenharmony_ci	memset(mwl8k_vif, 0, sizeof(*mwl8k_vif));
486462306a36Sopenharmony_ci	mwl8k_vif->vif = vif;
486562306a36Sopenharmony_ci	mwl8k_vif->macid = macid;
486662306a36Sopenharmony_ci	mwl8k_vif->seqno = 0;
486762306a36Sopenharmony_ci	memcpy(mwl8k_vif->bssid, vif->addr, ETH_ALEN);
486862306a36Sopenharmony_ci	mwl8k_vif->is_hw_crypto_enabled = false;
486962306a36Sopenharmony_ci
487062306a36Sopenharmony_ci	/* Set the mac address.  */
487162306a36Sopenharmony_ci	mwl8k_cmd_set_mac_addr(hw, vif, vif->addr);
487262306a36Sopenharmony_ci
487362306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_AP)
487462306a36Sopenharmony_ci		mwl8k_cmd_set_new_stn_add_self(hw, vif);
487562306a36Sopenharmony_ci
487662306a36Sopenharmony_ci	priv->macids_used |= 1 << mwl8k_vif->macid;
487762306a36Sopenharmony_ci	list_add_tail(&mwl8k_vif->list, &priv->vif_list);
487862306a36Sopenharmony_ci
487962306a36Sopenharmony_ci	return 0;
488062306a36Sopenharmony_ci}
488162306a36Sopenharmony_ci
488262306a36Sopenharmony_cistatic void mwl8k_remove_vif(struct mwl8k_priv *priv, struct mwl8k_vif *vif)
488362306a36Sopenharmony_ci{
488462306a36Sopenharmony_ci	/* Has ieee80211_restart_hw re-added the removed interfaces? */
488562306a36Sopenharmony_ci	if (!priv->macids_used)
488662306a36Sopenharmony_ci		return;
488762306a36Sopenharmony_ci
488862306a36Sopenharmony_ci	priv->macids_used &= ~(1 << vif->macid);
488962306a36Sopenharmony_ci	list_del(&vif->list);
489062306a36Sopenharmony_ci}
489162306a36Sopenharmony_ci
489262306a36Sopenharmony_cistatic void mwl8k_remove_interface(struct ieee80211_hw *hw,
489362306a36Sopenharmony_ci				   struct ieee80211_vif *vif)
489462306a36Sopenharmony_ci{
489562306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
489662306a36Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
489762306a36Sopenharmony_ci
489862306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_AP)
489962306a36Sopenharmony_ci		mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr);
490062306a36Sopenharmony_ci
490162306a36Sopenharmony_ci	mwl8k_cmd_del_mac_addr(hw, vif, vif->addr);
490262306a36Sopenharmony_ci
490362306a36Sopenharmony_ci	mwl8k_remove_vif(priv, mwl8k_vif);
490462306a36Sopenharmony_ci}
490562306a36Sopenharmony_ci
490662306a36Sopenharmony_cistatic void mwl8k_hw_restart_work(struct work_struct *work)
490762306a36Sopenharmony_ci{
490862306a36Sopenharmony_ci	struct mwl8k_priv *priv =
490962306a36Sopenharmony_ci		container_of(work, struct mwl8k_priv, fw_reload);
491062306a36Sopenharmony_ci	struct ieee80211_hw *hw = priv->hw;
491162306a36Sopenharmony_ci	struct mwl8k_device_info *di;
491262306a36Sopenharmony_ci	int rc;
491362306a36Sopenharmony_ci
491462306a36Sopenharmony_ci	/* If some command is waiting for a response, clear it */
491562306a36Sopenharmony_ci	if (priv->hostcmd_wait != NULL) {
491662306a36Sopenharmony_ci		complete(priv->hostcmd_wait);
491762306a36Sopenharmony_ci		priv->hostcmd_wait = NULL;
491862306a36Sopenharmony_ci	}
491962306a36Sopenharmony_ci
492062306a36Sopenharmony_ci	priv->hw_restart_owner = current;
492162306a36Sopenharmony_ci	di = priv->device_info;
492262306a36Sopenharmony_ci	mwl8k_fw_lock(hw);
492362306a36Sopenharmony_ci
492462306a36Sopenharmony_ci	if (priv->ap_fw)
492562306a36Sopenharmony_ci		rc = mwl8k_reload_firmware(hw, di->fw_image_ap);
492662306a36Sopenharmony_ci	else
492762306a36Sopenharmony_ci		rc = mwl8k_reload_firmware(hw, di->fw_image_sta);
492862306a36Sopenharmony_ci
492962306a36Sopenharmony_ci	if (rc)
493062306a36Sopenharmony_ci		goto fail;
493162306a36Sopenharmony_ci
493262306a36Sopenharmony_ci	priv->hw_restart_owner = NULL;
493362306a36Sopenharmony_ci	priv->hw_restart_in_progress = false;
493462306a36Sopenharmony_ci
493562306a36Sopenharmony_ci	/*
493662306a36Sopenharmony_ci	 * This unlock will wake up the queues and
493762306a36Sopenharmony_ci	 * also opens the command path for other
493862306a36Sopenharmony_ci	 * commands
493962306a36Sopenharmony_ci	 */
494062306a36Sopenharmony_ci	mwl8k_fw_unlock(hw);
494162306a36Sopenharmony_ci
494262306a36Sopenharmony_ci	ieee80211_restart_hw(hw);
494362306a36Sopenharmony_ci
494462306a36Sopenharmony_ci	wiphy_err(hw->wiphy, "Firmware restarted successfully\n");
494562306a36Sopenharmony_ci
494662306a36Sopenharmony_ci	return;
494762306a36Sopenharmony_cifail:
494862306a36Sopenharmony_ci	mwl8k_fw_unlock(hw);
494962306a36Sopenharmony_ci
495062306a36Sopenharmony_ci	wiphy_err(hw->wiphy, "Firmware restart failed\n");
495162306a36Sopenharmony_ci}
495262306a36Sopenharmony_ci
495362306a36Sopenharmony_cistatic int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
495462306a36Sopenharmony_ci{
495562306a36Sopenharmony_ci	struct ieee80211_conf *conf = &hw->conf;
495662306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
495762306a36Sopenharmony_ci	int rc;
495862306a36Sopenharmony_ci
495962306a36Sopenharmony_ci	rc = mwl8k_fw_lock(hw);
496062306a36Sopenharmony_ci	if (rc)
496162306a36Sopenharmony_ci		return rc;
496262306a36Sopenharmony_ci
496362306a36Sopenharmony_ci	if (conf->flags & IEEE80211_CONF_IDLE)
496462306a36Sopenharmony_ci		rc = mwl8k_cmd_radio_disable(hw);
496562306a36Sopenharmony_ci	else
496662306a36Sopenharmony_ci		rc = mwl8k_cmd_radio_enable(hw);
496762306a36Sopenharmony_ci	if (rc)
496862306a36Sopenharmony_ci		goto out;
496962306a36Sopenharmony_ci
497062306a36Sopenharmony_ci	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
497162306a36Sopenharmony_ci		rc = mwl8k_cmd_set_rf_channel(hw, conf);
497262306a36Sopenharmony_ci		if (rc)
497362306a36Sopenharmony_ci			goto out;
497462306a36Sopenharmony_ci	}
497562306a36Sopenharmony_ci
497662306a36Sopenharmony_ci	if (conf->power_level > 18)
497762306a36Sopenharmony_ci		conf->power_level = 18;
497862306a36Sopenharmony_ci
497962306a36Sopenharmony_ci	if (priv->ap_fw) {
498062306a36Sopenharmony_ci
498162306a36Sopenharmony_ci		if (conf->flags & IEEE80211_CONF_CHANGE_POWER) {
498262306a36Sopenharmony_ci			rc = mwl8k_cmd_tx_power(hw, conf, conf->power_level);
498362306a36Sopenharmony_ci			if (rc)
498462306a36Sopenharmony_ci				goto out;
498562306a36Sopenharmony_ci		}
498662306a36Sopenharmony_ci
498762306a36Sopenharmony_ci
498862306a36Sopenharmony_ci	} else {
498962306a36Sopenharmony_ci		rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level);
499062306a36Sopenharmony_ci		if (rc)
499162306a36Sopenharmony_ci			goto out;
499262306a36Sopenharmony_ci		rc = mwl8k_cmd_mimo_config(hw, 0x7, 0x7);
499362306a36Sopenharmony_ci	}
499462306a36Sopenharmony_ci
499562306a36Sopenharmony_ciout:
499662306a36Sopenharmony_ci	mwl8k_fw_unlock(hw);
499762306a36Sopenharmony_ci
499862306a36Sopenharmony_ci	return rc;
499962306a36Sopenharmony_ci}
500062306a36Sopenharmony_ci
500162306a36Sopenharmony_cistatic void
500262306a36Sopenharmony_cimwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
500362306a36Sopenharmony_ci			   struct ieee80211_bss_conf *info, u32 changed)
500462306a36Sopenharmony_ci{
500562306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
500662306a36Sopenharmony_ci	u32 ap_legacy_rates = 0;
500762306a36Sopenharmony_ci	u8 ap_mcs_rates[16];
500862306a36Sopenharmony_ci	int rc;
500962306a36Sopenharmony_ci
501062306a36Sopenharmony_ci	if (mwl8k_fw_lock(hw))
501162306a36Sopenharmony_ci		return;
501262306a36Sopenharmony_ci
501362306a36Sopenharmony_ci	/*
501462306a36Sopenharmony_ci	 * No need to capture a beacon if we're no longer associated.
501562306a36Sopenharmony_ci	 */
501662306a36Sopenharmony_ci	if ((changed & BSS_CHANGED_ASSOC) && !vif->cfg.assoc)
501762306a36Sopenharmony_ci		priv->capture_beacon = false;
501862306a36Sopenharmony_ci
501962306a36Sopenharmony_ci	/*
502062306a36Sopenharmony_ci	 * Get the AP's legacy and MCS rates.
502162306a36Sopenharmony_ci	 */
502262306a36Sopenharmony_ci	if (vif->cfg.assoc) {
502362306a36Sopenharmony_ci		struct ieee80211_sta *ap;
502462306a36Sopenharmony_ci
502562306a36Sopenharmony_ci		rcu_read_lock();
502662306a36Sopenharmony_ci
502762306a36Sopenharmony_ci		ap = ieee80211_find_sta(vif, vif->bss_conf.bssid);
502862306a36Sopenharmony_ci		if (ap == NULL) {
502962306a36Sopenharmony_ci			rcu_read_unlock();
503062306a36Sopenharmony_ci			goto out;
503162306a36Sopenharmony_ci		}
503262306a36Sopenharmony_ci
503362306a36Sopenharmony_ci		if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ) {
503462306a36Sopenharmony_ci			ap_legacy_rates = ap->deflink.supp_rates[NL80211_BAND_2GHZ];
503562306a36Sopenharmony_ci		} else {
503662306a36Sopenharmony_ci			ap_legacy_rates =
503762306a36Sopenharmony_ci				ap->deflink.supp_rates[NL80211_BAND_5GHZ] << 5;
503862306a36Sopenharmony_ci		}
503962306a36Sopenharmony_ci		memcpy(ap_mcs_rates, &ap->deflink.ht_cap.mcs, 16);
504062306a36Sopenharmony_ci
504162306a36Sopenharmony_ci		rcu_read_unlock();
504262306a36Sopenharmony_ci
504362306a36Sopenharmony_ci		if (changed & BSS_CHANGED_ASSOC) {
504462306a36Sopenharmony_ci			if (!priv->ap_fw) {
504562306a36Sopenharmony_ci				rc = mwl8k_cmd_set_rate(hw, vif,
504662306a36Sopenharmony_ci							ap_legacy_rates,
504762306a36Sopenharmony_ci							ap_mcs_rates);
504862306a36Sopenharmony_ci				if (rc)
504962306a36Sopenharmony_ci					goto out;
505062306a36Sopenharmony_ci
505162306a36Sopenharmony_ci				rc = mwl8k_cmd_use_fixed_rate_sta(hw);
505262306a36Sopenharmony_ci				if (rc)
505362306a36Sopenharmony_ci					goto out;
505462306a36Sopenharmony_ci			} else {
505562306a36Sopenharmony_ci				int idx;
505662306a36Sopenharmony_ci				int rate;
505762306a36Sopenharmony_ci
505862306a36Sopenharmony_ci				/* Use AP firmware specific rate command.
505962306a36Sopenharmony_ci				 */
506062306a36Sopenharmony_ci				idx = ffs(vif->bss_conf.basic_rates);
506162306a36Sopenharmony_ci				if (idx)
506262306a36Sopenharmony_ci					idx--;
506362306a36Sopenharmony_ci
506462306a36Sopenharmony_ci				if (hw->conf.chandef.chan->band ==
506562306a36Sopenharmony_ci				    NL80211_BAND_2GHZ)
506662306a36Sopenharmony_ci					rate = mwl8k_rates_24[idx].hw_value;
506762306a36Sopenharmony_ci				else
506862306a36Sopenharmony_ci					rate = mwl8k_rates_50[idx].hw_value;
506962306a36Sopenharmony_ci
507062306a36Sopenharmony_ci				mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate);
507162306a36Sopenharmony_ci			}
507262306a36Sopenharmony_ci		}
507362306a36Sopenharmony_ci	}
507462306a36Sopenharmony_ci
507562306a36Sopenharmony_ci	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
507662306a36Sopenharmony_ci		rc = mwl8k_set_radio_preamble(hw,
507762306a36Sopenharmony_ci				vif->bss_conf.use_short_preamble);
507862306a36Sopenharmony_ci		if (rc)
507962306a36Sopenharmony_ci			goto out;
508062306a36Sopenharmony_ci	}
508162306a36Sopenharmony_ci
508262306a36Sopenharmony_ci	if ((changed & BSS_CHANGED_ERP_SLOT) && !priv->ap_fw)  {
508362306a36Sopenharmony_ci		rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot);
508462306a36Sopenharmony_ci		if (rc)
508562306a36Sopenharmony_ci			goto out;
508662306a36Sopenharmony_ci	}
508762306a36Sopenharmony_ci
508862306a36Sopenharmony_ci	if (vif->cfg.assoc && !priv->ap_fw &&
508962306a36Sopenharmony_ci	    (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT |
509062306a36Sopenharmony_ci			BSS_CHANGED_HT))) {
509162306a36Sopenharmony_ci		rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates);
509262306a36Sopenharmony_ci		if (rc)
509362306a36Sopenharmony_ci			goto out;
509462306a36Sopenharmony_ci	}
509562306a36Sopenharmony_ci
509662306a36Sopenharmony_ci	if (vif->cfg.assoc &&
509762306a36Sopenharmony_ci	    (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INT))) {
509862306a36Sopenharmony_ci		/*
509962306a36Sopenharmony_ci		 * Finalize the join.  Tell rx handler to process
510062306a36Sopenharmony_ci		 * next beacon from our BSSID.
510162306a36Sopenharmony_ci		 */
510262306a36Sopenharmony_ci		memcpy(priv->capture_bssid, vif->bss_conf.bssid, ETH_ALEN);
510362306a36Sopenharmony_ci		priv->capture_beacon = true;
510462306a36Sopenharmony_ci	}
510562306a36Sopenharmony_ci
510662306a36Sopenharmony_ciout:
510762306a36Sopenharmony_ci	mwl8k_fw_unlock(hw);
510862306a36Sopenharmony_ci}
510962306a36Sopenharmony_ci
511062306a36Sopenharmony_cistatic void
511162306a36Sopenharmony_cimwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
511262306a36Sopenharmony_ci			  struct ieee80211_bss_conf *info, u32 changed)
511362306a36Sopenharmony_ci{
511462306a36Sopenharmony_ci	int rc;
511562306a36Sopenharmony_ci
511662306a36Sopenharmony_ci	if (mwl8k_fw_lock(hw))
511762306a36Sopenharmony_ci		return;
511862306a36Sopenharmony_ci
511962306a36Sopenharmony_ci	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
512062306a36Sopenharmony_ci		rc = mwl8k_set_radio_preamble(hw,
512162306a36Sopenharmony_ci				vif->bss_conf.use_short_preamble);
512262306a36Sopenharmony_ci		if (rc)
512362306a36Sopenharmony_ci			goto out;
512462306a36Sopenharmony_ci	}
512562306a36Sopenharmony_ci
512662306a36Sopenharmony_ci	if (changed & BSS_CHANGED_BASIC_RATES) {
512762306a36Sopenharmony_ci		int idx;
512862306a36Sopenharmony_ci		int rate;
512962306a36Sopenharmony_ci
513062306a36Sopenharmony_ci		/*
513162306a36Sopenharmony_ci		 * Use lowest supported basic rate for multicasts
513262306a36Sopenharmony_ci		 * and management frames (such as probe responses --
513362306a36Sopenharmony_ci		 * beacons will always go out at 1 Mb/s).
513462306a36Sopenharmony_ci		 */
513562306a36Sopenharmony_ci		idx = ffs(vif->bss_conf.basic_rates);
513662306a36Sopenharmony_ci		if (idx)
513762306a36Sopenharmony_ci			idx--;
513862306a36Sopenharmony_ci
513962306a36Sopenharmony_ci		if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ)
514062306a36Sopenharmony_ci			rate = mwl8k_rates_24[idx].hw_value;
514162306a36Sopenharmony_ci		else
514262306a36Sopenharmony_ci			rate = mwl8k_rates_50[idx].hw_value;
514362306a36Sopenharmony_ci
514462306a36Sopenharmony_ci		mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate);
514562306a36Sopenharmony_ci	}
514662306a36Sopenharmony_ci
514762306a36Sopenharmony_ci	if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) {
514862306a36Sopenharmony_ci		struct sk_buff *skb;
514962306a36Sopenharmony_ci
515062306a36Sopenharmony_ci		skb = ieee80211_beacon_get(hw, vif, 0);
515162306a36Sopenharmony_ci		if (skb != NULL) {
515262306a36Sopenharmony_ci			mwl8k_cmd_set_beacon(hw, vif, skb->data, skb->len);
515362306a36Sopenharmony_ci			kfree_skb(skb);
515462306a36Sopenharmony_ci		}
515562306a36Sopenharmony_ci	}
515662306a36Sopenharmony_ci
515762306a36Sopenharmony_ci	if (changed & BSS_CHANGED_BEACON_ENABLED)
515862306a36Sopenharmony_ci		mwl8k_cmd_bss_start(hw, vif, info->enable_beacon);
515962306a36Sopenharmony_ci
516062306a36Sopenharmony_ciout:
516162306a36Sopenharmony_ci	mwl8k_fw_unlock(hw);
516262306a36Sopenharmony_ci}
516362306a36Sopenharmony_ci
516462306a36Sopenharmony_cistatic void
516562306a36Sopenharmony_cimwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
516662306a36Sopenharmony_ci		       struct ieee80211_bss_conf *info, u64 changed)
516762306a36Sopenharmony_ci{
516862306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_STATION)
516962306a36Sopenharmony_ci		mwl8k_bss_info_changed_sta(hw, vif, info, changed);
517062306a36Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_AP)
517162306a36Sopenharmony_ci		mwl8k_bss_info_changed_ap(hw, vif, info, changed);
517262306a36Sopenharmony_ci}
517362306a36Sopenharmony_ci
517462306a36Sopenharmony_cistatic u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw,
517562306a36Sopenharmony_ci				   struct netdev_hw_addr_list *mc_list)
517662306a36Sopenharmony_ci{
517762306a36Sopenharmony_ci	struct mwl8k_cmd_pkt *cmd;
517862306a36Sopenharmony_ci
517962306a36Sopenharmony_ci	/*
518062306a36Sopenharmony_ci	 * Synthesize and return a command packet that programs the
518162306a36Sopenharmony_ci	 * hardware multicast address filter.  At this point we don't
518262306a36Sopenharmony_ci	 * know whether FIF_ALLMULTI is being requested, but if it is,
518362306a36Sopenharmony_ci	 * we'll end up throwing this packet away and creating a new
518462306a36Sopenharmony_ci	 * one in mwl8k_configure_filter().
518562306a36Sopenharmony_ci	 */
518662306a36Sopenharmony_ci	cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_list);
518762306a36Sopenharmony_ci
518862306a36Sopenharmony_ci	return (unsigned long)cmd;
518962306a36Sopenharmony_ci}
519062306a36Sopenharmony_ci
519162306a36Sopenharmony_cistatic int
519262306a36Sopenharmony_cimwl8k_configure_filter_sniffer(struct ieee80211_hw *hw,
519362306a36Sopenharmony_ci			       unsigned int changed_flags,
519462306a36Sopenharmony_ci			       unsigned int *total_flags)
519562306a36Sopenharmony_ci{
519662306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
519762306a36Sopenharmony_ci
519862306a36Sopenharmony_ci	/*
519962306a36Sopenharmony_ci	 * Hardware sniffer mode is mutually exclusive with STA
520062306a36Sopenharmony_ci	 * operation, so refuse to enable sniffer mode if a STA
520162306a36Sopenharmony_ci	 * interface is active.
520262306a36Sopenharmony_ci	 */
520362306a36Sopenharmony_ci	if (!list_empty(&priv->vif_list)) {
520462306a36Sopenharmony_ci		if (net_ratelimit())
520562306a36Sopenharmony_ci			wiphy_info(hw->wiphy,
520662306a36Sopenharmony_ci				   "not enabling sniffer mode because STA interface is active\n");
520762306a36Sopenharmony_ci		return 0;
520862306a36Sopenharmony_ci	}
520962306a36Sopenharmony_ci
521062306a36Sopenharmony_ci	if (!priv->sniffer_enabled) {
521162306a36Sopenharmony_ci		if (mwl8k_cmd_enable_sniffer(hw, 1))
521262306a36Sopenharmony_ci			return 0;
521362306a36Sopenharmony_ci		priv->sniffer_enabled = true;
521462306a36Sopenharmony_ci	}
521562306a36Sopenharmony_ci
521662306a36Sopenharmony_ci	*total_flags &=	FIF_ALLMULTI |
521762306a36Sopenharmony_ci			FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL |
521862306a36Sopenharmony_ci			FIF_OTHER_BSS;
521962306a36Sopenharmony_ci
522062306a36Sopenharmony_ci	return 1;
522162306a36Sopenharmony_ci}
522262306a36Sopenharmony_ci
522362306a36Sopenharmony_cistatic struct mwl8k_vif *mwl8k_first_vif(struct mwl8k_priv *priv)
522462306a36Sopenharmony_ci{
522562306a36Sopenharmony_ci	if (!list_empty(&priv->vif_list))
522662306a36Sopenharmony_ci		return list_entry(priv->vif_list.next, struct mwl8k_vif, list);
522762306a36Sopenharmony_ci
522862306a36Sopenharmony_ci	return NULL;
522962306a36Sopenharmony_ci}
523062306a36Sopenharmony_ci
523162306a36Sopenharmony_cistatic void mwl8k_configure_filter(struct ieee80211_hw *hw,
523262306a36Sopenharmony_ci				   unsigned int changed_flags,
523362306a36Sopenharmony_ci				   unsigned int *total_flags,
523462306a36Sopenharmony_ci				   u64 multicast)
523562306a36Sopenharmony_ci{
523662306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
523762306a36Sopenharmony_ci	struct mwl8k_cmd_pkt *cmd = (void *)(unsigned long)multicast;
523862306a36Sopenharmony_ci
523962306a36Sopenharmony_ci	/*
524062306a36Sopenharmony_ci	 * AP firmware doesn't allow fine-grained control over
524162306a36Sopenharmony_ci	 * the receive filter.
524262306a36Sopenharmony_ci	 */
524362306a36Sopenharmony_ci	if (priv->ap_fw) {
524462306a36Sopenharmony_ci		*total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC;
524562306a36Sopenharmony_ci		kfree(cmd);
524662306a36Sopenharmony_ci		return;
524762306a36Sopenharmony_ci	}
524862306a36Sopenharmony_ci
524962306a36Sopenharmony_ci	/*
525062306a36Sopenharmony_ci	 * Enable hardware sniffer mode if FIF_CONTROL or
525162306a36Sopenharmony_ci	 * FIF_OTHER_BSS is requested.
525262306a36Sopenharmony_ci	 */
525362306a36Sopenharmony_ci	if (*total_flags & (FIF_CONTROL | FIF_OTHER_BSS) &&
525462306a36Sopenharmony_ci	    mwl8k_configure_filter_sniffer(hw, changed_flags, total_flags)) {
525562306a36Sopenharmony_ci		kfree(cmd);
525662306a36Sopenharmony_ci		return;
525762306a36Sopenharmony_ci	}
525862306a36Sopenharmony_ci
525962306a36Sopenharmony_ci	/* Clear unsupported feature flags */
526062306a36Sopenharmony_ci	*total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC;
526162306a36Sopenharmony_ci
526262306a36Sopenharmony_ci	if (mwl8k_fw_lock(hw)) {
526362306a36Sopenharmony_ci		kfree(cmd);
526462306a36Sopenharmony_ci		return;
526562306a36Sopenharmony_ci	}
526662306a36Sopenharmony_ci
526762306a36Sopenharmony_ci	if (priv->sniffer_enabled) {
526862306a36Sopenharmony_ci		mwl8k_cmd_enable_sniffer(hw, 0);
526962306a36Sopenharmony_ci		priv->sniffer_enabled = false;
527062306a36Sopenharmony_ci	}
527162306a36Sopenharmony_ci
527262306a36Sopenharmony_ci	if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
527362306a36Sopenharmony_ci		if (*total_flags & FIF_BCN_PRBRESP_PROMISC) {
527462306a36Sopenharmony_ci			/*
527562306a36Sopenharmony_ci			 * Disable the BSS filter.
527662306a36Sopenharmony_ci			 */
527762306a36Sopenharmony_ci			mwl8k_cmd_set_pre_scan(hw);
527862306a36Sopenharmony_ci		} else {
527962306a36Sopenharmony_ci			struct mwl8k_vif *mwl8k_vif;
528062306a36Sopenharmony_ci			const u8 *bssid;
528162306a36Sopenharmony_ci
528262306a36Sopenharmony_ci			/*
528362306a36Sopenharmony_ci			 * Enable the BSS filter.
528462306a36Sopenharmony_ci			 *
528562306a36Sopenharmony_ci			 * If there is an active STA interface, use that
528662306a36Sopenharmony_ci			 * interface's BSSID, otherwise use a dummy one
528762306a36Sopenharmony_ci			 * (where the OUI part needs to be nonzero for
528862306a36Sopenharmony_ci			 * the BSSID to be accepted by POST_SCAN).
528962306a36Sopenharmony_ci			 */
529062306a36Sopenharmony_ci			mwl8k_vif = mwl8k_first_vif(priv);
529162306a36Sopenharmony_ci			if (mwl8k_vif != NULL)
529262306a36Sopenharmony_ci				bssid = mwl8k_vif->vif->bss_conf.bssid;
529362306a36Sopenharmony_ci			else
529462306a36Sopenharmony_ci				bssid = "\x01\x00\x00\x00\x00\x00";
529562306a36Sopenharmony_ci
529662306a36Sopenharmony_ci			mwl8k_cmd_set_post_scan(hw, bssid);
529762306a36Sopenharmony_ci		}
529862306a36Sopenharmony_ci	}
529962306a36Sopenharmony_ci
530062306a36Sopenharmony_ci	/*
530162306a36Sopenharmony_ci	 * If FIF_ALLMULTI is being requested, throw away the command
530262306a36Sopenharmony_ci	 * packet that ->prepare_multicast() built and replace it with
530362306a36Sopenharmony_ci	 * a command packet that enables reception of all multicast
530462306a36Sopenharmony_ci	 * packets.
530562306a36Sopenharmony_ci	 */
530662306a36Sopenharmony_ci	if (*total_flags & FIF_ALLMULTI) {
530762306a36Sopenharmony_ci		kfree(cmd);
530862306a36Sopenharmony_ci		cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, NULL);
530962306a36Sopenharmony_ci	}
531062306a36Sopenharmony_ci
531162306a36Sopenharmony_ci	if (cmd != NULL) {
531262306a36Sopenharmony_ci		mwl8k_post_cmd(hw, cmd);
531362306a36Sopenharmony_ci		kfree(cmd);
531462306a36Sopenharmony_ci	}
531562306a36Sopenharmony_ci
531662306a36Sopenharmony_ci	mwl8k_fw_unlock(hw);
531762306a36Sopenharmony_ci}
531862306a36Sopenharmony_ci
531962306a36Sopenharmony_cistatic int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
532062306a36Sopenharmony_ci{
532162306a36Sopenharmony_ci	return mwl8k_cmd_set_rts_threshold(hw, value);
532262306a36Sopenharmony_ci}
532362306a36Sopenharmony_ci
532462306a36Sopenharmony_cistatic int mwl8k_sta_remove(struct ieee80211_hw *hw,
532562306a36Sopenharmony_ci			    struct ieee80211_vif *vif,
532662306a36Sopenharmony_ci			    struct ieee80211_sta *sta)
532762306a36Sopenharmony_ci{
532862306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
532962306a36Sopenharmony_ci
533062306a36Sopenharmony_ci	if (priv->ap_fw)
533162306a36Sopenharmony_ci		return mwl8k_cmd_set_new_stn_del(hw, vif, sta->addr);
533262306a36Sopenharmony_ci	else
533362306a36Sopenharmony_ci		return mwl8k_cmd_update_stadb_del(hw, vif, sta->addr);
533462306a36Sopenharmony_ci}
533562306a36Sopenharmony_ci
533662306a36Sopenharmony_cistatic int mwl8k_sta_add(struct ieee80211_hw *hw,
533762306a36Sopenharmony_ci			 struct ieee80211_vif *vif,
533862306a36Sopenharmony_ci			 struct ieee80211_sta *sta)
533962306a36Sopenharmony_ci{
534062306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
534162306a36Sopenharmony_ci	int ret;
534262306a36Sopenharmony_ci	int i;
534362306a36Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
534462306a36Sopenharmony_ci	struct ieee80211_key_conf *key;
534562306a36Sopenharmony_ci
534662306a36Sopenharmony_ci	if (!priv->ap_fw) {
534762306a36Sopenharmony_ci		ret = mwl8k_cmd_update_stadb_add(hw, vif, sta);
534862306a36Sopenharmony_ci		if (ret >= 0) {
534962306a36Sopenharmony_ci			MWL8K_STA(sta)->peer_id = ret;
535062306a36Sopenharmony_ci			if (sta->deflink.ht_cap.ht_supported)
535162306a36Sopenharmony_ci				MWL8K_STA(sta)->is_ampdu_allowed = true;
535262306a36Sopenharmony_ci			ret = 0;
535362306a36Sopenharmony_ci		}
535462306a36Sopenharmony_ci
535562306a36Sopenharmony_ci	} else {
535662306a36Sopenharmony_ci		ret = mwl8k_cmd_set_new_stn_add(hw, vif, sta);
535762306a36Sopenharmony_ci	}
535862306a36Sopenharmony_ci
535962306a36Sopenharmony_ci	for (i = 0; i < NUM_WEP_KEYS; i++) {
536062306a36Sopenharmony_ci		key = IEEE80211_KEY_CONF(mwl8k_vif->wep_key_conf[i].key);
536162306a36Sopenharmony_ci		if (mwl8k_vif->wep_key_conf[i].enabled)
536262306a36Sopenharmony_ci			mwl8k_set_key(hw, SET_KEY, vif, sta, key);
536362306a36Sopenharmony_ci	}
536462306a36Sopenharmony_ci	return ret;
536562306a36Sopenharmony_ci}
536662306a36Sopenharmony_ci
536762306a36Sopenharmony_cistatic int mwl8k_conf_tx(struct ieee80211_hw *hw,
536862306a36Sopenharmony_ci			 struct ieee80211_vif *vif,
536962306a36Sopenharmony_ci			 unsigned int link_id, u16 queue,
537062306a36Sopenharmony_ci			 const struct ieee80211_tx_queue_params *params)
537162306a36Sopenharmony_ci{
537262306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
537362306a36Sopenharmony_ci	int rc;
537462306a36Sopenharmony_ci
537562306a36Sopenharmony_ci	rc = mwl8k_fw_lock(hw);
537662306a36Sopenharmony_ci	if (!rc) {
537762306a36Sopenharmony_ci		BUG_ON(queue > MWL8K_TX_WMM_QUEUES - 1);
537862306a36Sopenharmony_ci		memcpy(&priv->wmm_params[queue], params, sizeof(*params));
537962306a36Sopenharmony_ci
538062306a36Sopenharmony_ci		if (!priv->wmm_enabled)
538162306a36Sopenharmony_ci			rc = mwl8k_cmd_set_wmm_mode(hw, 1);
538262306a36Sopenharmony_ci
538362306a36Sopenharmony_ci		if (!rc) {
538462306a36Sopenharmony_ci			int q = MWL8K_TX_WMM_QUEUES - 1 - queue;
538562306a36Sopenharmony_ci			rc = mwl8k_cmd_set_edca_params(hw, q,
538662306a36Sopenharmony_ci						       params->cw_min,
538762306a36Sopenharmony_ci						       params->cw_max,
538862306a36Sopenharmony_ci						       params->aifs,
538962306a36Sopenharmony_ci						       params->txop);
539062306a36Sopenharmony_ci		}
539162306a36Sopenharmony_ci
539262306a36Sopenharmony_ci		mwl8k_fw_unlock(hw);
539362306a36Sopenharmony_ci	}
539462306a36Sopenharmony_ci
539562306a36Sopenharmony_ci	return rc;
539662306a36Sopenharmony_ci}
539762306a36Sopenharmony_ci
539862306a36Sopenharmony_cistatic int mwl8k_get_stats(struct ieee80211_hw *hw,
539962306a36Sopenharmony_ci			   struct ieee80211_low_level_stats *stats)
540062306a36Sopenharmony_ci{
540162306a36Sopenharmony_ci	return mwl8k_cmd_get_stat(hw, stats);
540262306a36Sopenharmony_ci}
540362306a36Sopenharmony_ci
540462306a36Sopenharmony_cistatic int mwl8k_get_survey(struct ieee80211_hw *hw, int idx,
540562306a36Sopenharmony_ci				struct survey_info *survey)
540662306a36Sopenharmony_ci{
540762306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
540862306a36Sopenharmony_ci	struct ieee80211_conf *conf = &hw->conf;
540962306a36Sopenharmony_ci	struct ieee80211_supported_band *sband;
541062306a36Sopenharmony_ci
541162306a36Sopenharmony_ci	if (priv->ap_fw) {
541262306a36Sopenharmony_ci		sband = hw->wiphy->bands[NL80211_BAND_2GHZ];
541362306a36Sopenharmony_ci
541462306a36Sopenharmony_ci		if (sband && idx >= sband->n_channels) {
541562306a36Sopenharmony_ci			idx -= sband->n_channels;
541662306a36Sopenharmony_ci			sband = NULL;
541762306a36Sopenharmony_ci		}
541862306a36Sopenharmony_ci
541962306a36Sopenharmony_ci		if (!sband)
542062306a36Sopenharmony_ci			sband = hw->wiphy->bands[NL80211_BAND_5GHZ];
542162306a36Sopenharmony_ci
542262306a36Sopenharmony_ci		if (!sband || idx >= sband->n_channels)
542362306a36Sopenharmony_ci			return -ENOENT;
542462306a36Sopenharmony_ci
542562306a36Sopenharmony_ci		memcpy(survey, &priv->survey[idx], sizeof(*survey));
542662306a36Sopenharmony_ci		survey->channel = &sband->channels[idx];
542762306a36Sopenharmony_ci
542862306a36Sopenharmony_ci		return 0;
542962306a36Sopenharmony_ci	}
543062306a36Sopenharmony_ci
543162306a36Sopenharmony_ci	if (idx != 0)
543262306a36Sopenharmony_ci		return -ENOENT;
543362306a36Sopenharmony_ci
543462306a36Sopenharmony_ci	survey->channel = conf->chandef.chan;
543562306a36Sopenharmony_ci	survey->filled = SURVEY_INFO_NOISE_DBM;
543662306a36Sopenharmony_ci	survey->noise = priv->noise;
543762306a36Sopenharmony_ci
543862306a36Sopenharmony_ci	return 0;
543962306a36Sopenharmony_ci}
544062306a36Sopenharmony_ci
544162306a36Sopenharmony_ci#define MAX_AMPDU_ATTEMPTS 5
544262306a36Sopenharmony_ci
544362306a36Sopenharmony_cistatic int
544462306a36Sopenharmony_cimwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
544562306a36Sopenharmony_ci		   struct ieee80211_ampdu_params *params)
544662306a36Sopenharmony_ci{
544762306a36Sopenharmony_ci	struct ieee80211_sta *sta = params->sta;
544862306a36Sopenharmony_ci	enum ieee80211_ampdu_mlme_action action = params->action;
544962306a36Sopenharmony_ci	u16 tid = params->tid;
545062306a36Sopenharmony_ci	u16 *ssn = &params->ssn;
545162306a36Sopenharmony_ci	u8 buf_size = params->buf_size;
545262306a36Sopenharmony_ci	int i, rc = 0;
545362306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
545462306a36Sopenharmony_ci	struct mwl8k_ampdu_stream *stream;
545562306a36Sopenharmony_ci	u8 *addr = sta->addr, idx;
545662306a36Sopenharmony_ci	struct mwl8k_sta *sta_info = MWL8K_STA(sta);
545762306a36Sopenharmony_ci
545862306a36Sopenharmony_ci	if (!ieee80211_hw_check(hw, AMPDU_AGGREGATION))
545962306a36Sopenharmony_ci		return -ENOTSUPP;
546062306a36Sopenharmony_ci
546162306a36Sopenharmony_ci	spin_lock(&priv->stream_lock);
546262306a36Sopenharmony_ci	stream = mwl8k_lookup_stream(hw, addr, tid);
546362306a36Sopenharmony_ci
546462306a36Sopenharmony_ci	switch (action) {
546562306a36Sopenharmony_ci	case IEEE80211_AMPDU_RX_START:
546662306a36Sopenharmony_ci	case IEEE80211_AMPDU_RX_STOP:
546762306a36Sopenharmony_ci		break;
546862306a36Sopenharmony_ci	case IEEE80211_AMPDU_TX_START:
546962306a36Sopenharmony_ci		/* By the time we get here the hw queues may contain outgoing
547062306a36Sopenharmony_ci		 * packets for this RA/TID that are not part of this BA
547162306a36Sopenharmony_ci		 * session.  The hw will assign sequence numbers to these
547262306a36Sopenharmony_ci		 * packets as they go out.  So if we query the hw for its next
547362306a36Sopenharmony_ci		 * sequence number and use that for the SSN here, it may end up
547462306a36Sopenharmony_ci		 * being wrong, which will lead to sequence number mismatch at
547562306a36Sopenharmony_ci		 * the recipient.  To avoid this, we reset the sequence number
547662306a36Sopenharmony_ci		 * to O for the first MPDU in this BA stream.
547762306a36Sopenharmony_ci		 */
547862306a36Sopenharmony_ci		*ssn = 0;
547962306a36Sopenharmony_ci		if (stream == NULL) {
548062306a36Sopenharmony_ci			/* This means that somebody outside this driver called
548162306a36Sopenharmony_ci			 * ieee80211_start_tx_ba_session.  This is unexpected
548262306a36Sopenharmony_ci			 * because we do our own rate control.  Just warn and
548362306a36Sopenharmony_ci			 * move on.
548462306a36Sopenharmony_ci			 */
548562306a36Sopenharmony_ci			wiphy_warn(hw->wiphy, "Unexpected call to %s.  "
548662306a36Sopenharmony_ci				   "Proceeding anyway.\n", __func__);
548762306a36Sopenharmony_ci			stream = mwl8k_add_stream(hw, sta, tid);
548862306a36Sopenharmony_ci		}
548962306a36Sopenharmony_ci		if (stream == NULL) {
549062306a36Sopenharmony_ci			wiphy_debug(hw->wiphy, "no free AMPDU streams\n");
549162306a36Sopenharmony_ci			rc = -EBUSY;
549262306a36Sopenharmony_ci			break;
549362306a36Sopenharmony_ci		}
549462306a36Sopenharmony_ci		stream->state = AMPDU_STREAM_IN_PROGRESS;
549562306a36Sopenharmony_ci
549662306a36Sopenharmony_ci		/* Release the lock before we do the time consuming stuff */
549762306a36Sopenharmony_ci		spin_unlock(&priv->stream_lock);
549862306a36Sopenharmony_ci		for (i = 0; i < MAX_AMPDU_ATTEMPTS; i++) {
549962306a36Sopenharmony_ci
550062306a36Sopenharmony_ci			/* Check if link is still valid */
550162306a36Sopenharmony_ci			if (!sta_info->is_ampdu_allowed) {
550262306a36Sopenharmony_ci				spin_lock(&priv->stream_lock);
550362306a36Sopenharmony_ci				mwl8k_remove_stream(hw, stream);
550462306a36Sopenharmony_ci				spin_unlock(&priv->stream_lock);
550562306a36Sopenharmony_ci				return -EBUSY;
550662306a36Sopenharmony_ci			}
550762306a36Sopenharmony_ci
550862306a36Sopenharmony_ci			rc = mwl8k_check_ba(hw, stream, vif);
550962306a36Sopenharmony_ci
551062306a36Sopenharmony_ci			/* If HW restart is in progress mwl8k_post_cmd will
551162306a36Sopenharmony_ci			 * return -EBUSY. Avoid retrying mwl8k_check_ba in
551262306a36Sopenharmony_ci			 * such cases
551362306a36Sopenharmony_ci			 */
551462306a36Sopenharmony_ci			if (!rc || rc == -EBUSY)
551562306a36Sopenharmony_ci				break;
551662306a36Sopenharmony_ci			/*
551762306a36Sopenharmony_ci			 * HW queues take time to be flushed, give them
551862306a36Sopenharmony_ci			 * sufficient time
551962306a36Sopenharmony_ci			 */
552062306a36Sopenharmony_ci
552162306a36Sopenharmony_ci			msleep(1000);
552262306a36Sopenharmony_ci		}
552362306a36Sopenharmony_ci		spin_lock(&priv->stream_lock);
552462306a36Sopenharmony_ci		if (rc) {
552562306a36Sopenharmony_ci			wiphy_err(hw->wiphy, "Stream for tid %d busy after %d"
552662306a36Sopenharmony_ci				" attempts\n", tid, MAX_AMPDU_ATTEMPTS);
552762306a36Sopenharmony_ci			mwl8k_remove_stream(hw, stream);
552862306a36Sopenharmony_ci			rc = -EBUSY;
552962306a36Sopenharmony_ci			break;
553062306a36Sopenharmony_ci		}
553162306a36Sopenharmony_ci		rc = IEEE80211_AMPDU_TX_START_IMMEDIATE;
553262306a36Sopenharmony_ci		break;
553362306a36Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_CONT:
553462306a36Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_FLUSH:
553562306a36Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
553662306a36Sopenharmony_ci		if (stream) {
553762306a36Sopenharmony_ci			if (stream->state == AMPDU_STREAM_ACTIVE) {
553862306a36Sopenharmony_ci				idx = stream->idx;
553962306a36Sopenharmony_ci				spin_unlock(&priv->stream_lock);
554062306a36Sopenharmony_ci				mwl8k_destroy_ba(hw, idx);
554162306a36Sopenharmony_ci				spin_lock(&priv->stream_lock);
554262306a36Sopenharmony_ci			}
554362306a36Sopenharmony_ci			mwl8k_remove_stream(hw, stream);
554462306a36Sopenharmony_ci		}
554562306a36Sopenharmony_ci		ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid);
554662306a36Sopenharmony_ci		break;
554762306a36Sopenharmony_ci	case IEEE80211_AMPDU_TX_OPERATIONAL:
554862306a36Sopenharmony_ci		BUG_ON(stream == NULL);
554962306a36Sopenharmony_ci		BUG_ON(stream->state != AMPDU_STREAM_IN_PROGRESS);
555062306a36Sopenharmony_ci		spin_unlock(&priv->stream_lock);
555162306a36Sopenharmony_ci		rc = mwl8k_create_ba(hw, stream, buf_size, vif);
555262306a36Sopenharmony_ci		spin_lock(&priv->stream_lock);
555362306a36Sopenharmony_ci		if (!rc)
555462306a36Sopenharmony_ci			stream->state = AMPDU_STREAM_ACTIVE;
555562306a36Sopenharmony_ci		else {
555662306a36Sopenharmony_ci			idx = stream->idx;
555762306a36Sopenharmony_ci			spin_unlock(&priv->stream_lock);
555862306a36Sopenharmony_ci			mwl8k_destroy_ba(hw, idx);
555962306a36Sopenharmony_ci			spin_lock(&priv->stream_lock);
556062306a36Sopenharmony_ci			wiphy_debug(hw->wiphy,
556162306a36Sopenharmony_ci				"Failed adding stream for sta %pM tid %d\n",
556262306a36Sopenharmony_ci				addr, tid);
556362306a36Sopenharmony_ci			mwl8k_remove_stream(hw, stream);
556462306a36Sopenharmony_ci		}
556562306a36Sopenharmony_ci		break;
556662306a36Sopenharmony_ci
556762306a36Sopenharmony_ci	default:
556862306a36Sopenharmony_ci		rc = -ENOTSUPP;
556962306a36Sopenharmony_ci	}
557062306a36Sopenharmony_ci
557162306a36Sopenharmony_ci	spin_unlock(&priv->stream_lock);
557262306a36Sopenharmony_ci	return rc;
557362306a36Sopenharmony_ci}
557462306a36Sopenharmony_ci
557562306a36Sopenharmony_cistatic void mwl8k_sw_scan_start(struct ieee80211_hw *hw,
557662306a36Sopenharmony_ci				struct ieee80211_vif *vif,
557762306a36Sopenharmony_ci				const u8 *mac_addr)
557862306a36Sopenharmony_ci{
557962306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
558062306a36Sopenharmony_ci	u8 tmp;
558162306a36Sopenharmony_ci
558262306a36Sopenharmony_ci	if (!priv->ap_fw)
558362306a36Sopenharmony_ci		return;
558462306a36Sopenharmony_ci
558562306a36Sopenharmony_ci	/* clear all stats */
558662306a36Sopenharmony_ci	priv->channel_time = 0;
558762306a36Sopenharmony_ci	ioread32(priv->regs + BBU_RXRDY_CNT_REG);
558862306a36Sopenharmony_ci	ioread32(priv->regs + NOK_CCA_CNT_REG);
558962306a36Sopenharmony_ci	mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp);
559062306a36Sopenharmony_ci
559162306a36Sopenharmony_ci	priv->sw_scan_start = true;
559262306a36Sopenharmony_ci}
559362306a36Sopenharmony_ci
559462306a36Sopenharmony_cistatic void mwl8k_sw_scan_complete(struct ieee80211_hw *hw,
559562306a36Sopenharmony_ci				   struct ieee80211_vif *vif)
559662306a36Sopenharmony_ci{
559762306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
559862306a36Sopenharmony_ci	u8 tmp;
559962306a36Sopenharmony_ci
560062306a36Sopenharmony_ci	if (!priv->ap_fw)
560162306a36Sopenharmony_ci		return;
560262306a36Sopenharmony_ci
560362306a36Sopenharmony_ci	priv->sw_scan_start = false;
560462306a36Sopenharmony_ci
560562306a36Sopenharmony_ci	/* clear all stats */
560662306a36Sopenharmony_ci	priv->channel_time = 0;
560762306a36Sopenharmony_ci	ioread32(priv->regs + BBU_RXRDY_CNT_REG);
560862306a36Sopenharmony_ci	ioread32(priv->regs + NOK_CCA_CNT_REG);
560962306a36Sopenharmony_ci	mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp);
561062306a36Sopenharmony_ci}
561162306a36Sopenharmony_ci
561262306a36Sopenharmony_cistatic const struct ieee80211_ops mwl8k_ops = {
561362306a36Sopenharmony_ci	.tx			= mwl8k_tx,
561462306a36Sopenharmony_ci	.wake_tx_queue		= ieee80211_handle_wake_tx_queue,
561562306a36Sopenharmony_ci	.start			= mwl8k_start,
561662306a36Sopenharmony_ci	.stop			= mwl8k_stop,
561762306a36Sopenharmony_ci	.add_interface		= mwl8k_add_interface,
561862306a36Sopenharmony_ci	.remove_interface	= mwl8k_remove_interface,
561962306a36Sopenharmony_ci	.config			= mwl8k_config,
562062306a36Sopenharmony_ci	.bss_info_changed	= mwl8k_bss_info_changed,
562162306a36Sopenharmony_ci	.prepare_multicast	= mwl8k_prepare_multicast,
562262306a36Sopenharmony_ci	.configure_filter	= mwl8k_configure_filter,
562362306a36Sopenharmony_ci	.set_key                = mwl8k_set_key,
562462306a36Sopenharmony_ci	.set_rts_threshold	= mwl8k_set_rts_threshold,
562562306a36Sopenharmony_ci	.sta_add		= mwl8k_sta_add,
562662306a36Sopenharmony_ci	.sta_remove		= mwl8k_sta_remove,
562762306a36Sopenharmony_ci	.conf_tx		= mwl8k_conf_tx,
562862306a36Sopenharmony_ci	.get_stats		= mwl8k_get_stats,
562962306a36Sopenharmony_ci	.get_survey		= mwl8k_get_survey,
563062306a36Sopenharmony_ci	.ampdu_action		= mwl8k_ampdu_action,
563162306a36Sopenharmony_ci	.sw_scan_start		= mwl8k_sw_scan_start,
563262306a36Sopenharmony_ci	.sw_scan_complete	= mwl8k_sw_scan_complete,
563362306a36Sopenharmony_ci};
563462306a36Sopenharmony_ci
563562306a36Sopenharmony_cistatic void mwl8k_finalize_join_worker(struct work_struct *work)
563662306a36Sopenharmony_ci{
563762306a36Sopenharmony_ci	struct mwl8k_priv *priv =
563862306a36Sopenharmony_ci		container_of(work, struct mwl8k_priv, finalize_join_worker);
563962306a36Sopenharmony_ci	struct sk_buff *skb = priv->beacon_skb;
564062306a36Sopenharmony_ci	struct ieee80211_mgmt *mgmt = (void *)skb->data;
564162306a36Sopenharmony_ci	int len = skb->len - offsetof(struct ieee80211_mgmt, u.beacon.variable);
564262306a36Sopenharmony_ci	const u8 *tim = cfg80211_find_ie(WLAN_EID_TIM,
564362306a36Sopenharmony_ci					 mgmt->u.beacon.variable, len);
564462306a36Sopenharmony_ci	int dtim_period = 1;
564562306a36Sopenharmony_ci
564662306a36Sopenharmony_ci	if (tim && tim[1] >= 2)
564762306a36Sopenharmony_ci		dtim_period = tim[3];
564862306a36Sopenharmony_ci
564962306a36Sopenharmony_ci	mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len, dtim_period);
565062306a36Sopenharmony_ci
565162306a36Sopenharmony_ci	dev_kfree_skb(skb);
565262306a36Sopenharmony_ci	priv->beacon_skb = NULL;
565362306a36Sopenharmony_ci}
565462306a36Sopenharmony_ci
565562306a36Sopenharmony_cienum {
565662306a36Sopenharmony_ci	MWL8363 = 0,
565762306a36Sopenharmony_ci	MWL8687,
565862306a36Sopenharmony_ci	MWL8366,
565962306a36Sopenharmony_ci	MWL8764,
566062306a36Sopenharmony_ci};
566162306a36Sopenharmony_ci
566262306a36Sopenharmony_ci#define MWL8K_8366_AP_FW_API 3
566362306a36Sopenharmony_ci#define _MWL8K_8366_AP_FW(api) "mwl8k/fmimage_8366_ap-" #api ".fw"
566462306a36Sopenharmony_ci#define MWL8K_8366_AP_FW(api) _MWL8K_8366_AP_FW(api)
566562306a36Sopenharmony_ci
566662306a36Sopenharmony_ci#define MWL8K_8764_AP_FW_API 1
566762306a36Sopenharmony_ci#define _MWL8K_8764_AP_FW(api) "mwl8k/fmimage_8764_ap-" #api ".fw"
566862306a36Sopenharmony_ci#define MWL8K_8764_AP_FW(api) _MWL8K_8764_AP_FW(api)
566962306a36Sopenharmony_ci
567062306a36Sopenharmony_cistatic struct mwl8k_device_info mwl8k_info_tbl[] = {
567162306a36Sopenharmony_ci	[MWL8363] = {
567262306a36Sopenharmony_ci		.part_name	= "88w8363",
567362306a36Sopenharmony_ci		.helper_image	= "mwl8k/helper_8363.fw",
567462306a36Sopenharmony_ci		.fw_image_sta	= "mwl8k/fmimage_8363.fw",
567562306a36Sopenharmony_ci	},
567662306a36Sopenharmony_ci	[MWL8687] = {
567762306a36Sopenharmony_ci		.part_name	= "88w8687",
567862306a36Sopenharmony_ci		.helper_image	= "mwl8k/helper_8687.fw",
567962306a36Sopenharmony_ci		.fw_image_sta	= "mwl8k/fmimage_8687.fw",
568062306a36Sopenharmony_ci	},
568162306a36Sopenharmony_ci	[MWL8366] = {
568262306a36Sopenharmony_ci		.part_name	= "88w8366",
568362306a36Sopenharmony_ci		.helper_image	= "mwl8k/helper_8366.fw",
568462306a36Sopenharmony_ci		.fw_image_sta	= "mwl8k/fmimage_8366.fw",
568562306a36Sopenharmony_ci		.fw_image_ap	= MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API),
568662306a36Sopenharmony_ci		.fw_api_ap	= MWL8K_8366_AP_FW_API,
568762306a36Sopenharmony_ci		.ap_rxd_ops	= &rxd_ap_ops,
568862306a36Sopenharmony_ci	},
568962306a36Sopenharmony_ci	[MWL8764] = {
569062306a36Sopenharmony_ci		.part_name	= "88w8764",
569162306a36Sopenharmony_ci		.fw_image_ap	= MWL8K_8764_AP_FW(MWL8K_8764_AP_FW_API),
569262306a36Sopenharmony_ci		.fw_api_ap	= MWL8K_8764_AP_FW_API,
569362306a36Sopenharmony_ci		.ap_rxd_ops	= &rxd_ap_ops,
569462306a36Sopenharmony_ci	},
569562306a36Sopenharmony_ci};
569662306a36Sopenharmony_ci
569762306a36Sopenharmony_ciMODULE_FIRMWARE("mwl8k/helper_8363.fw");
569862306a36Sopenharmony_ciMODULE_FIRMWARE("mwl8k/fmimage_8363.fw");
569962306a36Sopenharmony_ciMODULE_FIRMWARE("mwl8k/helper_8687.fw");
570062306a36Sopenharmony_ciMODULE_FIRMWARE("mwl8k/fmimage_8687.fw");
570162306a36Sopenharmony_ciMODULE_FIRMWARE("mwl8k/helper_8366.fw");
570262306a36Sopenharmony_ciMODULE_FIRMWARE("mwl8k/fmimage_8366.fw");
570362306a36Sopenharmony_ciMODULE_FIRMWARE(MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API));
570462306a36Sopenharmony_ci
570562306a36Sopenharmony_cistatic const struct pci_device_id mwl8k_pci_id_table[] = {
570662306a36Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a0a), .driver_data = MWL8363, },
570762306a36Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, },
570862306a36Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, },
570962306a36Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, },
571062306a36Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, },
571162306a36Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, },
571262306a36Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a41), .driver_data = MWL8366, },
571362306a36Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a42), .driver_data = MWL8366, },
571462306a36Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a43), .driver_data = MWL8366, },
571562306a36Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2b36), .driver_data = MWL8764, },
571662306a36Sopenharmony_ci	{ },
571762306a36Sopenharmony_ci};
571862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table);
571962306a36Sopenharmony_ci
572062306a36Sopenharmony_cistatic int mwl8k_request_alt_fw(struct mwl8k_priv *priv)
572162306a36Sopenharmony_ci{
572262306a36Sopenharmony_ci	int rc;
572362306a36Sopenharmony_ci	printk(KERN_ERR "%s: Error requesting preferred fw %s.\n"
572462306a36Sopenharmony_ci	       "Trying alternative firmware %s\n", pci_name(priv->pdev),
572562306a36Sopenharmony_ci	       priv->fw_pref, priv->fw_alt);
572662306a36Sopenharmony_ci	rc = mwl8k_request_fw(priv, priv->fw_alt, &priv->fw_ucode, true);
572762306a36Sopenharmony_ci	if (rc) {
572862306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error requesting alt fw %s\n",
572962306a36Sopenharmony_ci		       pci_name(priv->pdev), priv->fw_alt);
573062306a36Sopenharmony_ci		return rc;
573162306a36Sopenharmony_ci	}
573262306a36Sopenharmony_ci	return 0;
573362306a36Sopenharmony_ci}
573462306a36Sopenharmony_ci
573562306a36Sopenharmony_cistatic int mwl8k_firmware_load_success(struct mwl8k_priv *priv);
573662306a36Sopenharmony_cistatic void mwl8k_fw_state_machine(const struct firmware *fw, void *context)
573762306a36Sopenharmony_ci{
573862306a36Sopenharmony_ci	struct mwl8k_priv *priv = context;
573962306a36Sopenharmony_ci	struct mwl8k_device_info *di = priv->device_info;
574062306a36Sopenharmony_ci	int rc;
574162306a36Sopenharmony_ci
574262306a36Sopenharmony_ci	switch (priv->fw_state) {
574362306a36Sopenharmony_ci	case FW_STATE_INIT:
574462306a36Sopenharmony_ci		if (!fw) {
574562306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error requesting helper fw %s\n",
574662306a36Sopenharmony_ci			       pci_name(priv->pdev), di->helper_image);
574762306a36Sopenharmony_ci			goto fail;
574862306a36Sopenharmony_ci		}
574962306a36Sopenharmony_ci		priv->fw_helper = fw;
575062306a36Sopenharmony_ci		rc = mwl8k_request_fw(priv, priv->fw_pref, &priv->fw_ucode,
575162306a36Sopenharmony_ci				      true);
575262306a36Sopenharmony_ci		if (rc && priv->fw_alt) {
575362306a36Sopenharmony_ci			rc = mwl8k_request_alt_fw(priv);
575462306a36Sopenharmony_ci			if (rc)
575562306a36Sopenharmony_ci				goto fail;
575662306a36Sopenharmony_ci			priv->fw_state = FW_STATE_LOADING_ALT;
575762306a36Sopenharmony_ci		} else if (rc)
575862306a36Sopenharmony_ci			goto fail;
575962306a36Sopenharmony_ci		else
576062306a36Sopenharmony_ci			priv->fw_state = FW_STATE_LOADING_PREF;
576162306a36Sopenharmony_ci		break;
576262306a36Sopenharmony_ci
576362306a36Sopenharmony_ci	case FW_STATE_LOADING_PREF:
576462306a36Sopenharmony_ci		if (!fw) {
576562306a36Sopenharmony_ci			if (priv->fw_alt) {
576662306a36Sopenharmony_ci				rc = mwl8k_request_alt_fw(priv);
576762306a36Sopenharmony_ci				if (rc)
576862306a36Sopenharmony_ci					goto fail;
576962306a36Sopenharmony_ci				priv->fw_state = FW_STATE_LOADING_ALT;
577062306a36Sopenharmony_ci			} else
577162306a36Sopenharmony_ci				goto fail;
577262306a36Sopenharmony_ci		} else {
577362306a36Sopenharmony_ci			priv->fw_ucode = fw;
577462306a36Sopenharmony_ci			rc = mwl8k_firmware_load_success(priv);
577562306a36Sopenharmony_ci			if (rc)
577662306a36Sopenharmony_ci				goto fail;
577762306a36Sopenharmony_ci			else
577862306a36Sopenharmony_ci				complete(&priv->firmware_loading_complete);
577962306a36Sopenharmony_ci		}
578062306a36Sopenharmony_ci		break;
578162306a36Sopenharmony_ci
578262306a36Sopenharmony_ci	case FW_STATE_LOADING_ALT:
578362306a36Sopenharmony_ci		if (!fw) {
578462306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error requesting alt fw %s\n",
578562306a36Sopenharmony_ci			       pci_name(priv->pdev), di->helper_image);
578662306a36Sopenharmony_ci			goto fail;
578762306a36Sopenharmony_ci		}
578862306a36Sopenharmony_ci		priv->fw_ucode = fw;
578962306a36Sopenharmony_ci		rc = mwl8k_firmware_load_success(priv);
579062306a36Sopenharmony_ci		if (rc)
579162306a36Sopenharmony_ci			goto fail;
579262306a36Sopenharmony_ci		else
579362306a36Sopenharmony_ci			complete(&priv->firmware_loading_complete);
579462306a36Sopenharmony_ci		break;
579562306a36Sopenharmony_ci
579662306a36Sopenharmony_ci	default:
579762306a36Sopenharmony_ci		printk(KERN_ERR "%s: Unexpected firmware loading state: %d\n",
579862306a36Sopenharmony_ci		       MWL8K_NAME, priv->fw_state);
579962306a36Sopenharmony_ci		BUG_ON(1);
580062306a36Sopenharmony_ci	}
580162306a36Sopenharmony_ci
580262306a36Sopenharmony_ci	return;
580362306a36Sopenharmony_ci
580462306a36Sopenharmony_cifail:
580562306a36Sopenharmony_ci	priv->fw_state = FW_STATE_ERROR;
580662306a36Sopenharmony_ci	complete(&priv->firmware_loading_complete);
580762306a36Sopenharmony_ci	mwl8k_release_firmware(priv);
580862306a36Sopenharmony_ci	device_release_driver(&priv->pdev->dev);
580962306a36Sopenharmony_ci}
581062306a36Sopenharmony_ci
581162306a36Sopenharmony_ci#define MAX_RESTART_ATTEMPTS 1
581262306a36Sopenharmony_cistatic int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image,
581362306a36Sopenharmony_ci			       bool nowait)
581462306a36Sopenharmony_ci{
581562306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
581662306a36Sopenharmony_ci	int rc;
581762306a36Sopenharmony_ci	int count = MAX_RESTART_ATTEMPTS;
581862306a36Sopenharmony_ci
581962306a36Sopenharmony_ciretry:
582062306a36Sopenharmony_ci	/* Reset firmware and hardware */
582162306a36Sopenharmony_ci	mwl8k_hw_reset(priv);
582262306a36Sopenharmony_ci
582362306a36Sopenharmony_ci	/* Ask userland hotplug daemon for the device firmware */
582462306a36Sopenharmony_ci	rc = mwl8k_request_firmware(priv, fw_image, nowait);
582562306a36Sopenharmony_ci	if (rc) {
582662306a36Sopenharmony_ci		wiphy_err(hw->wiphy, "Firmware files not found\n");
582762306a36Sopenharmony_ci		return rc;
582862306a36Sopenharmony_ci	}
582962306a36Sopenharmony_ci
583062306a36Sopenharmony_ci	if (nowait)
583162306a36Sopenharmony_ci		return rc;
583262306a36Sopenharmony_ci
583362306a36Sopenharmony_ci	/* Load firmware into hardware */
583462306a36Sopenharmony_ci	rc = mwl8k_load_firmware(hw);
583562306a36Sopenharmony_ci	if (rc)
583662306a36Sopenharmony_ci		wiphy_err(hw->wiphy, "Cannot start firmware\n");
583762306a36Sopenharmony_ci
583862306a36Sopenharmony_ci	/* Reclaim memory once firmware is successfully loaded */
583962306a36Sopenharmony_ci	mwl8k_release_firmware(priv);
584062306a36Sopenharmony_ci
584162306a36Sopenharmony_ci	if (rc && count) {
584262306a36Sopenharmony_ci		/* FW did not start successfully;
584362306a36Sopenharmony_ci		 * lets try one more time
584462306a36Sopenharmony_ci		 */
584562306a36Sopenharmony_ci		count--;
584662306a36Sopenharmony_ci		wiphy_err(hw->wiphy, "Trying to reload the firmware again\n");
584762306a36Sopenharmony_ci		msleep(20);
584862306a36Sopenharmony_ci		goto retry;
584962306a36Sopenharmony_ci	}
585062306a36Sopenharmony_ci
585162306a36Sopenharmony_ci	return rc;
585262306a36Sopenharmony_ci}
585362306a36Sopenharmony_ci
585462306a36Sopenharmony_cistatic int mwl8k_init_txqs(struct ieee80211_hw *hw)
585562306a36Sopenharmony_ci{
585662306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
585762306a36Sopenharmony_ci	int rc = 0;
585862306a36Sopenharmony_ci	int i;
585962306a36Sopenharmony_ci
586062306a36Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++) {
586162306a36Sopenharmony_ci		rc = mwl8k_txq_init(hw, i);
586262306a36Sopenharmony_ci		if (rc)
586362306a36Sopenharmony_ci			break;
586462306a36Sopenharmony_ci		if (priv->ap_fw)
586562306a36Sopenharmony_ci			iowrite32(priv->txq[i].txd_dma,
586662306a36Sopenharmony_ci				  priv->sram + priv->txq_offset[i]);
586762306a36Sopenharmony_ci	}
586862306a36Sopenharmony_ci	return rc;
586962306a36Sopenharmony_ci}
587062306a36Sopenharmony_ci
587162306a36Sopenharmony_ci/* initialize hw after successfully loading a firmware image */
587262306a36Sopenharmony_cistatic int mwl8k_probe_hw(struct ieee80211_hw *hw)
587362306a36Sopenharmony_ci{
587462306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
587562306a36Sopenharmony_ci	int rc = 0;
587662306a36Sopenharmony_ci	int i;
587762306a36Sopenharmony_ci
587862306a36Sopenharmony_ci	if (priv->ap_fw) {
587962306a36Sopenharmony_ci		priv->rxd_ops = priv->device_info->ap_rxd_ops;
588062306a36Sopenharmony_ci		if (priv->rxd_ops == NULL) {
588162306a36Sopenharmony_ci			wiphy_err(hw->wiphy,
588262306a36Sopenharmony_ci				  "Driver does not have AP firmware image support for this hardware\n");
588362306a36Sopenharmony_ci			rc = -ENOENT;
588462306a36Sopenharmony_ci			goto err_stop_firmware;
588562306a36Sopenharmony_ci		}
588662306a36Sopenharmony_ci	} else {
588762306a36Sopenharmony_ci		priv->rxd_ops = &rxd_sta_ops;
588862306a36Sopenharmony_ci	}
588962306a36Sopenharmony_ci
589062306a36Sopenharmony_ci	priv->sniffer_enabled = false;
589162306a36Sopenharmony_ci	priv->wmm_enabled = false;
589262306a36Sopenharmony_ci	priv->pending_tx_pkts = 0;
589362306a36Sopenharmony_ci	atomic_set(&priv->watchdog_event_pending, 0);
589462306a36Sopenharmony_ci
589562306a36Sopenharmony_ci	rc = mwl8k_rxq_init(hw, 0);
589662306a36Sopenharmony_ci	if (rc)
589762306a36Sopenharmony_ci		goto err_stop_firmware;
589862306a36Sopenharmony_ci	rxq_refill(hw, 0, INT_MAX);
589962306a36Sopenharmony_ci
590062306a36Sopenharmony_ci	/* For the sta firmware, we need to know the dma addresses of tx queues
590162306a36Sopenharmony_ci	 * before sending MWL8K_CMD_GET_HW_SPEC.  So we must initialize them
590262306a36Sopenharmony_ci	 * prior to issuing this command.  But for the AP case, we learn the
590362306a36Sopenharmony_ci	 * total number of queues from the result CMD_GET_HW_SPEC, so for this
590462306a36Sopenharmony_ci	 * case we must initialize the tx queues after.
590562306a36Sopenharmony_ci	 */
590662306a36Sopenharmony_ci	priv->num_ampdu_queues = 0;
590762306a36Sopenharmony_ci	if (!priv->ap_fw) {
590862306a36Sopenharmony_ci		rc = mwl8k_init_txqs(hw);
590962306a36Sopenharmony_ci		if (rc)
591062306a36Sopenharmony_ci			goto err_free_queues;
591162306a36Sopenharmony_ci	}
591262306a36Sopenharmony_ci
591362306a36Sopenharmony_ci	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
591462306a36Sopenharmony_ci	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
591562306a36Sopenharmony_ci	iowrite32(MWL8K_A2H_INT_TX_DONE|MWL8K_A2H_INT_RX_READY|
591662306a36Sopenharmony_ci		  MWL8K_A2H_INT_BA_WATCHDOG,
591762306a36Sopenharmony_ci		  priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL);
591862306a36Sopenharmony_ci	iowrite32(MWL8K_A2H_INT_OPC_DONE,
591962306a36Sopenharmony_ci		  priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
592062306a36Sopenharmony_ci
592162306a36Sopenharmony_ci	rc = request_irq(priv->pdev->irq, mwl8k_interrupt,
592262306a36Sopenharmony_ci			 IRQF_SHARED, MWL8K_NAME, hw);
592362306a36Sopenharmony_ci	if (rc) {
592462306a36Sopenharmony_ci		wiphy_err(hw->wiphy, "failed to register IRQ handler\n");
592562306a36Sopenharmony_ci		goto err_free_queues;
592662306a36Sopenharmony_ci	}
592762306a36Sopenharmony_ci
592862306a36Sopenharmony_ci	/*
592962306a36Sopenharmony_ci	 * When hw restart is requested,
593062306a36Sopenharmony_ci	 * mac80211 will take care of clearing
593162306a36Sopenharmony_ci	 * the ampdu streams, so do not clear
593262306a36Sopenharmony_ci	 * the ampdu state here
593362306a36Sopenharmony_ci	 */
593462306a36Sopenharmony_ci	if (!priv->hw_restart_in_progress)
593562306a36Sopenharmony_ci		memset(priv->ampdu, 0, sizeof(priv->ampdu));
593662306a36Sopenharmony_ci
593762306a36Sopenharmony_ci	/*
593862306a36Sopenharmony_ci	 * Temporarily enable interrupts.  Initial firmware host
593962306a36Sopenharmony_ci	 * commands use interrupts and avoid polling.  Disable
594062306a36Sopenharmony_ci	 * interrupts when done.
594162306a36Sopenharmony_ci	 */
594262306a36Sopenharmony_ci	iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
594362306a36Sopenharmony_ci
594462306a36Sopenharmony_ci	/* Get config data, mac addrs etc */
594562306a36Sopenharmony_ci	if (priv->ap_fw) {
594662306a36Sopenharmony_ci		rc = mwl8k_cmd_get_hw_spec_ap(hw);
594762306a36Sopenharmony_ci		if (!rc)
594862306a36Sopenharmony_ci			rc = mwl8k_init_txqs(hw);
594962306a36Sopenharmony_ci		if (!rc)
595062306a36Sopenharmony_ci			rc = mwl8k_cmd_set_hw_spec(hw);
595162306a36Sopenharmony_ci	} else {
595262306a36Sopenharmony_ci		rc = mwl8k_cmd_get_hw_spec_sta(hw);
595362306a36Sopenharmony_ci	}
595462306a36Sopenharmony_ci	if (rc) {
595562306a36Sopenharmony_ci		wiphy_err(hw->wiphy, "Cannot initialise firmware\n");
595662306a36Sopenharmony_ci		goto err_free_irq;
595762306a36Sopenharmony_ci	}
595862306a36Sopenharmony_ci
595962306a36Sopenharmony_ci	/* Turn radio off */
596062306a36Sopenharmony_ci	rc = mwl8k_cmd_radio_disable(hw);
596162306a36Sopenharmony_ci	if (rc) {
596262306a36Sopenharmony_ci		wiphy_err(hw->wiphy, "Cannot disable\n");
596362306a36Sopenharmony_ci		goto err_free_irq;
596462306a36Sopenharmony_ci	}
596562306a36Sopenharmony_ci
596662306a36Sopenharmony_ci	/* Clear MAC address */
596762306a36Sopenharmony_ci	rc = mwl8k_cmd_set_mac_addr(hw, NULL, "\x00\x00\x00\x00\x00\x00");
596862306a36Sopenharmony_ci	if (rc) {
596962306a36Sopenharmony_ci		wiphy_err(hw->wiphy, "Cannot clear MAC address\n");
597062306a36Sopenharmony_ci		goto err_free_irq;
597162306a36Sopenharmony_ci	}
597262306a36Sopenharmony_ci
597362306a36Sopenharmony_ci	/* Configure Antennas */
597462306a36Sopenharmony_ci	rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3);
597562306a36Sopenharmony_ci	if (rc)
597662306a36Sopenharmony_ci		wiphy_warn(hw->wiphy, "failed to set # of RX antennas");
597762306a36Sopenharmony_ci	rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7);
597862306a36Sopenharmony_ci	if (rc)
597962306a36Sopenharmony_ci		wiphy_warn(hw->wiphy, "failed to set # of TX antennas");
598062306a36Sopenharmony_ci
598162306a36Sopenharmony_ci
598262306a36Sopenharmony_ci	/* Disable interrupts */
598362306a36Sopenharmony_ci	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
598462306a36Sopenharmony_ci	free_irq(priv->pdev->irq, hw);
598562306a36Sopenharmony_ci
598662306a36Sopenharmony_ci	wiphy_info(hw->wiphy, "%s v%d, %pm, %s firmware %u.%u.%u.%u\n",
598762306a36Sopenharmony_ci		   priv->device_info->part_name,
598862306a36Sopenharmony_ci		   priv->hw_rev, hw->wiphy->perm_addr,
598962306a36Sopenharmony_ci		   priv->ap_fw ? "AP" : "STA",
599062306a36Sopenharmony_ci		   (priv->fw_rev >> 24) & 0xff, (priv->fw_rev >> 16) & 0xff,
599162306a36Sopenharmony_ci		   (priv->fw_rev >> 8) & 0xff, priv->fw_rev & 0xff);
599262306a36Sopenharmony_ci
599362306a36Sopenharmony_ci	return 0;
599462306a36Sopenharmony_ci
599562306a36Sopenharmony_cierr_free_irq:
599662306a36Sopenharmony_ci	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
599762306a36Sopenharmony_ci	free_irq(priv->pdev->irq, hw);
599862306a36Sopenharmony_ci
599962306a36Sopenharmony_cierr_free_queues:
600062306a36Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
600162306a36Sopenharmony_ci		mwl8k_txq_deinit(hw, i);
600262306a36Sopenharmony_ci	mwl8k_rxq_deinit(hw, 0);
600362306a36Sopenharmony_ci
600462306a36Sopenharmony_cierr_stop_firmware:
600562306a36Sopenharmony_ci	mwl8k_hw_reset(priv);
600662306a36Sopenharmony_ci
600762306a36Sopenharmony_ci	return rc;
600862306a36Sopenharmony_ci}
600962306a36Sopenharmony_ci
601062306a36Sopenharmony_ci/*
601162306a36Sopenharmony_ci * invoke mwl8k_reload_firmware to change the firmware image after the device
601262306a36Sopenharmony_ci * has already been registered
601362306a36Sopenharmony_ci */
601462306a36Sopenharmony_cistatic int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)
601562306a36Sopenharmony_ci{
601662306a36Sopenharmony_ci	int i, rc = 0;
601762306a36Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
601862306a36Sopenharmony_ci	struct mwl8k_vif *vif, *tmp_vif;
601962306a36Sopenharmony_ci
602062306a36Sopenharmony_ci	mwl8k_stop(hw);
602162306a36Sopenharmony_ci	mwl8k_rxq_deinit(hw, 0);
602262306a36Sopenharmony_ci
602362306a36Sopenharmony_ci	/*
602462306a36Sopenharmony_ci	 * All the existing interfaces are re-added by the ieee80211_reconfig;
602562306a36Sopenharmony_ci	 * which means driver should remove existing interfaces before calling
602662306a36Sopenharmony_ci	 * ieee80211_restart_hw
602762306a36Sopenharmony_ci	 */
602862306a36Sopenharmony_ci	if (priv->hw_restart_in_progress)
602962306a36Sopenharmony_ci		list_for_each_entry_safe(vif, tmp_vif, &priv->vif_list, list)
603062306a36Sopenharmony_ci			mwl8k_remove_vif(priv, vif);
603162306a36Sopenharmony_ci
603262306a36Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
603362306a36Sopenharmony_ci		mwl8k_txq_deinit(hw, i);
603462306a36Sopenharmony_ci
603562306a36Sopenharmony_ci	rc = mwl8k_init_firmware(hw, fw_image, false);
603662306a36Sopenharmony_ci	if (rc)
603762306a36Sopenharmony_ci		goto fail;
603862306a36Sopenharmony_ci
603962306a36Sopenharmony_ci	rc = mwl8k_probe_hw(hw);
604062306a36Sopenharmony_ci	if (rc)
604162306a36Sopenharmony_ci		goto fail;
604262306a36Sopenharmony_ci
604362306a36Sopenharmony_ci	if (priv->hw_restart_in_progress)
604462306a36Sopenharmony_ci		return rc;
604562306a36Sopenharmony_ci
604662306a36Sopenharmony_ci	rc = mwl8k_start(hw);
604762306a36Sopenharmony_ci	if (rc)
604862306a36Sopenharmony_ci		goto fail;
604962306a36Sopenharmony_ci
605062306a36Sopenharmony_ci	rc = mwl8k_config(hw, ~0);
605162306a36Sopenharmony_ci	if (rc)
605262306a36Sopenharmony_ci		goto fail;
605362306a36Sopenharmony_ci
605462306a36Sopenharmony_ci	for (i = 0; i < MWL8K_TX_WMM_QUEUES; i++) {
605562306a36Sopenharmony_ci		rc = mwl8k_conf_tx(hw, NULL, 0, i, &priv->wmm_params[i]);
605662306a36Sopenharmony_ci		if (rc)
605762306a36Sopenharmony_ci			goto fail;
605862306a36Sopenharmony_ci	}
605962306a36Sopenharmony_ci
606062306a36Sopenharmony_ci	return rc;
606162306a36Sopenharmony_ci
606262306a36Sopenharmony_cifail:
606362306a36Sopenharmony_ci	printk(KERN_WARNING "mwl8k: Failed to reload firmware image.\n");
606462306a36Sopenharmony_ci	return rc;
606562306a36Sopenharmony_ci}
606662306a36Sopenharmony_ci
606762306a36Sopenharmony_cistatic const struct ieee80211_iface_limit ap_if_limits[] = {
606862306a36Sopenharmony_ci	{ .max = 8,	.types = BIT(NL80211_IFTYPE_AP) },
606962306a36Sopenharmony_ci	{ .max = 1,	.types = BIT(NL80211_IFTYPE_STATION) },
607062306a36Sopenharmony_ci};
607162306a36Sopenharmony_ci
607262306a36Sopenharmony_cistatic const struct ieee80211_iface_combination ap_if_comb = {
607362306a36Sopenharmony_ci	.limits = ap_if_limits,
607462306a36Sopenharmony_ci	.n_limits = ARRAY_SIZE(ap_if_limits),
607562306a36Sopenharmony_ci	.max_interfaces = 8,
607662306a36Sopenharmony_ci	.num_different_channels = 1,
607762306a36Sopenharmony_ci};
607862306a36Sopenharmony_ci
607962306a36Sopenharmony_ci
608062306a36Sopenharmony_cistatic int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
608162306a36Sopenharmony_ci{
608262306a36Sopenharmony_ci	struct ieee80211_hw *hw = priv->hw;
608362306a36Sopenharmony_ci	int i, rc;
608462306a36Sopenharmony_ci
608562306a36Sopenharmony_ci	rc = mwl8k_load_firmware(hw);
608662306a36Sopenharmony_ci	mwl8k_release_firmware(priv);
608762306a36Sopenharmony_ci	if (rc) {
608862306a36Sopenharmony_ci		wiphy_err(hw->wiphy, "Cannot start firmware\n");
608962306a36Sopenharmony_ci		return rc;
609062306a36Sopenharmony_ci	}
609162306a36Sopenharmony_ci
609262306a36Sopenharmony_ci	/*
609362306a36Sopenharmony_ci	 * Extra headroom is the size of the required DMA header
609462306a36Sopenharmony_ci	 * minus the size of the smallest 802.11 frame (CTS frame).
609562306a36Sopenharmony_ci	 */
609662306a36Sopenharmony_ci	hw->extra_tx_headroom =
609762306a36Sopenharmony_ci		sizeof(struct mwl8k_dma_data) - sizeof(struct ieee80211_cts);
609862306a36Sopenharmony_ci
609962306a36Sopenharmony_ci	hw->extra_tx_headroom -= priv->ap_fw ? REDUCED_TX_HEADROOM : 0;
610062306a36Sopenharmony_ci
610162306a36Sopenharmony_ci	hw->queues = MWL8K_TX_WMM_QUEUES;
610262306a36Sopenharmony_ci
610362306a36Sopenharmony_ci	/* Set rssi values to dBm */
610462306a36Sopenharmony_ci	ieee80211_hw_set(hw, SIGNAL_DBM);
610562306a36Sopenharmony_ci	ieee80211_hw_set(hw, HAS_RATE_CONTROL);
610662306a36Sopenharmony_ci
610762306a36Sopenharmony_ci	/*
610862306a36Sopenharmony_ci	 * Ask mac80211 to not to trigger PS mode
610962306a36Sopenharmony_ci	 * based on PM bit of incoming frames.
611062306a36Sopenharmony_ci	 */
611162306a36Sopenharmony_ci	if (priv->ap_fw)
611262306a36Sopenharmony_ci		ieee80211_hw_set(hw, AP_LINK_PS);
611362306a36Sopenharmony_ci
611462306a36Sopenharmony_ci	hw->vif_data_size = sizeof(struct mwl8k_vif);
611562306a36Sopenharmony_ci	hw->sta_data_size = sizeof(struct mwl8k_sta);
611662306a36Sopenharmony_ci
611762306a36Sopenharmony_ci	priv->macids_used = 0;
611862306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->vif_list);
611962306a36Sopenharmony_ci
612062306a36Sopenharmony_ci	/* Set default radio state and preamble */
612162306a36Sopenharmony_ci	priv->radio_on = false;
612262306a36Sopenharmony_ci	priv->radio_short_preamble = false;
612362306a36Sopenharmony_ci
612462306a36Sopenharmony_ci	/* Finalize join worker */
612562306a36Sopenharmony_ci	INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker);
612662306a36Sopenharmony_ci	/* Handle watchdog ba events */
612762306a36Sopenharmony_ci	INIT_WORK(&priv->watchdog_ba_handle, mwl8k_watchdog_ba_events);
612862306a36Sopenharmony_ci	/* To reload the firmware if it crashes */
612962306a36Sopenharmony_ci	INIT_WORK(&priv->fw_reload, mwl8k_hw_restart_work);
613062306a36Sopenharmony_ci
613162306a36Sopenharmony_ci	/* TX reclaim and RX tasklets.  */
613262306a36Sopenharmony_ci	tasklet_setup(&priv->poll_tx_task, mwl8k_tx_poll);
613362306a36Sopenharmony_ci	tasklet_disable(&priv->poll_tx_task);
613462306a36Sopenharmony_ci	tasklet_setup(&priv->poll_rx_task, mwl8k_rx_poll);
613562306a36Sopenharmony_ci	tasklet_disable(&priv->poll_rx_task);
613662306a36Sopenharmony_ci
613762306a36Sopenharmony_ci	/* Power management cookie */
613862306a36Sopenharmony_ci	priv->cookie = dma_alloc_coherent(&priv->pdev->dev, 4,
613962306a36Sopenharmony_ci					  &priv->cookie_dma, GFP_KERNEL);
614062306a36Sopenharmony_ci	if (priv->cookie == NULL)
614162306a36Sopenharmony_ci		return -ENOMEM;
614262306a36Sopenharmony_ci
614362306a36Sopenharmony_ci	mutex_init(&priv->fw_mutex);
614462306a36Sopenharmony_ci	priv->fw_mutex_owner = NULL;
614562306a36Sopenharmony_ci	priv->fw_mutex_depth = 0;
614662306a36Sopenharmony_ci	priv->hostcmd_wait = NULL;
614762306a36Sopenharmony_ci
614862306a36Sopenharmony_ci	spin_lock_init(&priv->tx_lock);
614962306a36Sopenharmony_ci
615062306a36Sopenharmony_ci	spin_lock_init(&priv->stream_lock);
615162306a36Sopenharmony_ci
615262306a36Sopenharmony_ci	priv->tx_wait = NULL;
615362306a36Sopenharmony_ci
615462306a36Sopenharmony_ci	rc = mwl8k_probe_hw(hw);
615562306a36Sopenharmony_ci	if (rc)
615662306a36Sopenharmony_ci		goto err_free_cookie;
615762306a36Sopenharmony_ci
615862306a36Sopenharmony_ci	hw->wiphy->interface_modes = 0;
615962306a36Sopenharmony_ci
616062306a36Sopenharmony_ci	if (priv->ap_macids_supported || priv->device_info->fw_image_ap) {
616162306a36Sopenharmony_ci		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP);
616262306a36Sopenharmony_ci		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
616362306a36Sopenharmony_ci		hw->wiphy->iface_combinations = &ap_if_comb;
616462306a36Sopenharmony_ci		hw->wiphy->n_iface_combinations = 1;
616562306a36Sopenharmony_ci	}
616662306a36Sopenharmony_ci
616762306a36Sopenharmony_ci	if (priv->sta_macids_supported || priv->device_info->fw_image_sta)
616862306a36Sopenharmony_ci		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
616962306a36Sopenharmony_ci
617062306a36Sopenharmony_ci	wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
617162306a36Sopenharmony_ci
617262306a36Sopenharmony_ci	rc = ieee80211_register_hw(hw);
617362306a36Sopenharmony_ci	if (rc) {
617462306a36Sopenharmony_ci		wiphy_err(hw->wiphy, "Cannot register device\n");
617562306a36Sopenharmony_ci		goto err_unprobe_hw;
617662306a36Sopenharmony_ci	}
617762306a36Sopenharmony_ci
617862306a36Sopenharmony_ci	return 0;
617962306a36Sopenharmony_ci
618062306a36Sopenharmony_cierr_unprobe_hw:
618162306a36Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
618262306a36Sopenharmony_ci		mwl8k_txq_deinit(hw, i);
618362306a36Sopenharmony_ci	mwl8k_rxq_deinit(hw, 0);
618462306a36Sopenharmony_ci
618562306a36Sopenharmony_cierr_free_cookie:
618662306a36Sopenharmony_ci	if (priv->cookie != NULL)
618762306a36Sopenharmony_ci		dma_free_coherent(&priv->pdev->dev, 4, priv->cookie,
618862306a36Sopenharmony_ci				  priv->cookie_dma);
618962306a36Sopenharmony_ci
619062306a36Sopenharmony_ci	return rc;
619162306a36Sopenharmony_ci}
619262306a36Sopenharmony_cistatic int mwl8k_probe(struct pci_dev *pdev,
619362306a36Sopenharmony_ci				 const struct pci_device_id *id)
619462306a36Sopenharmony_ci{
619562306a36Sopenharmony_ci	static int printed_version;
619662306a36Sopenharmony_ci	struct ieee80211_hw *hw;
619762306a36Sopenharmony_ci	struct mwl8k_priv *priv;
619862306a36Sopenharmony_ci	struct mwl8k_device_info *di;
619962306a36Sopenharmony_ci	int rc;
620062306a36Sopenharmony_ci
620162306a36Sopenharmony_ci	if (!printed_version) {
620262306a36Sopenharmony_ci		printk(KERN_INFO "%s version %s\n", MWL8K_DESC, MWL8K_VERSION);
620362306a36Sopenharmony_ci		printed_version = 1;
620462306a36Sopenharmony_ci	}
620562306a36Sopenharmony_ci
620662306a36Sopenharmony_ci
620762306a36Sopenharmony_ci	rc = pci_enable_device(pdev);
620862306a36Sopenharmony_ci	if (rc) {
620962306a36Sopenharmony_ci		printk(KERN_ERR "%s: Cannot enable new PCI device\n",
621062306a36Sopenharmony_ci		       MWL8K_NAME);
621162306a36Sopenharmony_ci		return rc;
621262306a36Sopenharmony_ci	}
621362306a36Sopenharmony_ci
621462306a36Sopenharmony_ci	rc = pci_request_regions(pdev, MWL8K_NAME);
621562306a36Sopenharmony_ci	if (rc) {
621662306a36Sopenharmony_ci		printk(KERN_ERR "%s: Cannot obtain PCI resources\n",
621762306a36Sopenharmony_ci		       MWL8K_NAME);
621862306a36Sopenharmony_ci		goto err_disable_device;
621962306a36Sopenharmony_ci	}
622062306a36Sopenharmony_ci
622162306a36Sopenharmony_ci	pci_set_master(pdev);
622262306a36Sopenharmony_ci
622362306a36Sopenharmony_ci
622462306a36Sopenharmony_ci	hw = ieee80211_alloc_hw(sizeof(*priv), &mwl8k_ops);
622562306a36Sopenharmony_ci	if (hw == NULL) {
622662306a36Sopenharmony_ci		printk(KERN_ERR "%s: ieee80211 alloc failed\n", MWL8K_NAME);
622762306a36Sopenharmony_ci		rc = -ENOMEM;
622862306a36Sopenharmony_ci		goto err_free_reg;
622962306a36Sopenharmony_ci	}
623062306a36Sopenharmony_ci
623162306a36Sopenharmony_ci	SET_IEEE80211_DEV(hw, &pdev->dev);
623262306a36Sopenharmony_ci	pci_set_drvdata(pdev, hw);
623362306a36Sopenharmony_ci
623462306a36Sopenharmony_ci	priv = hw->priv;
623562306a36Sopenharmony_ci	priv->hw = hw;
623662306a36Sopenharmony_ci	priv->pdev = pdev;
623762306a36Sopenharmony_ci	priv->device_info = &mwl8k_info_tbl[id->driver_data];
623862306a36Sopenharmony_ci
623962306a36Sopenharmony_ci	if (id->driver_data == MWL8764)
624062306a36Sopenharmony_ci		priv->is_8764 = true;
624162306a36Sopenharmony_ci
624262306a36Sopenharmony_ci	priv->sram = pci_iomap(pdev, 0, 0x10000);
624362306a36Sopenharmony_ci	if (priv->sram == NULL) {
624462306a36Sopenharmony_ci		wiphy_err(hw->wiphy, "Cannot map device SRAM\n");
624562306a36Sopenharmony_ci		rc = -EIO;
624662306a36Sopenharmony_ci		goto err_iounmap;
624762306a36Sopenharmony_ci	}
624862306a36Sopenharmony_ci
624962306a36Sopenharmony_ci	/*
625062306a36Sopenharmony_ci	 * If BAR0 is a 32 bit BAR, the register BAR will be BAR1.
625162306a36Sopenharmony_ci	 * If BAR0 is a 64 bit BAR, the register BAR will be BAR2.
625262306a36Sopenharmony_ci	 */
625362306a36Sopenharmony_ci	priv->regs = pci_iomap(pdev, 1, 0x10000);
625462306a36Sopenharmony_ci	if (priv->regs == NULL) {
625562306a36Sopenharmony_ci		priv->regs = pci_iomap(pdev, 2, 0x10000);
625662306a36Sopenharmony_ci		if (priv->regs == NULL) {
625762306a36Sopenharmony_ci			wiphy_err(hw->wiphy, "Cannot map device registers\n");
625862306a36Sopenharmony_ci			rc = -EIO;
625962306a36Sopenharmony_ci			goto err_iounmap;
626062306a36Sopenharmony_ci		}
626162306a36Sopenharmony_ci	}
626262306a36Sopenharmony_ci
626362306a36Sopenharmony_ci	/*
626462306a36Sopenharmony_ci	 * Choose the initial fw image depending on user input.  If a second
626562306a36Sopenharmony_ci	 * image is available, make it the alternative image that will be
626662306a36Sopenharmony_ci	 * loaded if the first one fails.
626762306a36Sopenharmony_ci	 */
626862306a36Sopenharmony_ci	init_completion(&priv->firmware_loading_complete);
626962306a36Sopenharmony_ci	di = priv->device_info;
627062306a36Sopenharmony_ci	if (ap_mode_default && di->fw_image_ap) {
627162306a36Sopenharmony_ci		priv->fw_pref = di->fw_image_ap;
627262306a36Sopenharmony_ci		priv->fw_alt = di->fw_image_sta;
627362306a36Sopenharmony_ci	} else if (!ap_mode_default && di->fw_image_sta) {
627462306a36Sopenharmony_ci		priv->fw_pref = di->fw_image_sta;
627562306a36Sopenharmony_ci		priv->fw_alt = di->fw_image_ap;
627662306a36Sopenharmony_ci	} else if (ap_mode_default && !di->fw_image_ap && di->fw_image_sta) {
627762306a36Sopenharmony_ci		printk(KERN_WARNING "AP fw is unavailable.  Using STA fw.");
627862306a36Sopenharmony_ci		priv->fw_pref = di->fw_image_sta;
627962306a36Sopenharmony_ci	} else if (!ap_mode_default && !di->fw_image_sta && di->fw_image_ap) {
628062306a36Sopenharmony_ci		printk(KERN_WARNING "STA fw is unavailable.  Using AP fw.");
628162306a36Sopenharmony_ci		priv->fw_pref = di->fw_image_ap;
628262306a36Sopenharmony_ci	}
628362306a36Sopenharmony_ci	rc = mwl8k_init_firmware(hw, priv->fw_pref, true);
628462306a36Sopenharmony_ci	if (rc)
628562306a36Sopenharmony_ci		goto err_stop_firmware;
628662306a36Sopenharmony_ci
628762306a36Sopenharmony_ci	priv->hw_restart_in_progress = false;
628862306a36Sopenharmony_ci
628962306a36Sopenharmony_ci	priv->running_bsses = 0;
629062306a36Sopenharmony_ci
629162306a36Sopenharmony_ci	return rc;
629262306a36Sopenharmony_ci
629362306a36Sopenharmony_cierr_stop_firmware:
629462306a36Sopenharmony_ci	mwl8k_hw_reset(priv);
629562306a36Sopenharmony_ci
629662306a36Sopenharmony_cierr_iounmap:
629762306a36Sopenharmony_ci	if (priv->regs != NULL)
629862306a36Sopenharmony_ci		pci_iounmap(pdev, priv->regs);
629962306a36Sopenharmony_ci
630062306a36Sopenharmony_ci	if (priv->sram != NULL)
630162306a36Sopenharmony_ci		pci_iounmap(pdev, priv->sram);
630262306a36Sopenharmony_ci
630362306a36Sopenharmony_ci	ieee80211_free_hw(hw);
630462306a36Sopenharmony_ci
630562306a36Sopenharmony_cierr_free_reg:
630662306a36Sopenharmony_ci	pci_release_regions(pdev);
630762306a36Sopenharmony_ci
630862306a36Sopenharmony_cierr_disable_device:
630962306a36Sopenharmony_ci	pci_disable_device(pdev);
631062306a36Sopenharmony_ci
631162306a36Sopenharmony_ci	return rc;
631262306a36Sopenharmony_ci}
631362306a36Sopenharmony_ci
631462306a36Sopenharmony_cistatic void mwl8k_remove(struct pci_dev *pdev)
631562306a36Sopenharmony_ci{
631662306a36Sopenharmony_ci	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
631762306a36Sopenharmony_ci	struct mwl8k_priv *priv;
631862306a36Sopenharmony_ci	int i;
631962306a36Sopenharmony_ci
632062306a36Sopenharmony_ci	if (hw == NULL)
632162306a36Sopenharmony_ci		return;
632262306a36Sopenharmony_ci	priv = hw->priv;
632362306a36Sopenharmony_ci
632462306a36Sopenharmony_ci	wait_for_completion(&priv->firmware_loading_complete);
632562306a36Sopenharmony_ci
632662306a36Sopenharmony_ci	if (priv->fw_state == FW_STATE_ERROR) {
632762306a36Sopenharmony_ci		mwl8k_hw_reset(priv);
632862306a36Sopenharmony_ci		goto unmap;
632962306a36Sopenharmony_ci	}
633062306a36Sopenharmony_ci
633162306a36Sopenharmony_ci	ieee80211_stop_queues(hw);
633262306a36Sopenharmony_ci
633362306a36Sopenharmony_ci	ieee80211_unregister_hw(hw);
633462306a36Sopenharmony_ci
633562306a36Sopenharmony_ci	/* Remove TX reclaim and RX tasklets.  */
633662306a36Sopenharmony_ci	tasklet_kill(&priv->poll_tx_task);
633762306a36Sopenharmony_ci	tasklet_kill(&priv->poll_rx_task);
633862306a36Sopenharmony_ci
633962306a36Sopenharmony_ci	/* Stop hardware */
634062306a36Sopenharmony_ci	mwl8k_hw_reset(priv);
634162306a36Sopenharmony_ci
634262306a36Sopenharmony_ci	/* Return all skbs to mac80211 */
634362306a36Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
634462306a36Sopenharmony_ci		mwl8k_txq_reclaim(hw, i, INT_MAX, 1);
634562306a36Sopenharmony_ci
634662306a36Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
634762306a36Sopenharmony_ci		mwl8k_txq_deinit(hw, i);
634862306a36Sopenharmony_ci
634962306a36Sopenharmony_ci	mwl8k_rxq_deinit(hw, 0);
635062306a36Sopenharmony_ci
635162306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev, 4, priv->cookie, priv->cookie_dma);
635262306a36Sopenharmony_ci
635362306a36Sopenharmony_ciunmap:
635462306a36Sopenharmony_ci	pci_iounmap(pdev, priv->regs);
635562306a36Sopenharmony_ci	pci_iounmap(pdev, priv->sram);
635662306a36Sopenharmony_ci	ieee80211_free_hw(hw);
635762306a36Sopenharmony_ci	pci_release_regions(pdev);
635862306a36Sopenharmony_ci	pci_disable_device(pdev);
635962306a36Sopenharmony_ci}
636062306a36Sopenharmony_ci
636162306a36Sopenharmony_cistatic struct pci_driver mwl8k_driver = {
636262306a36Sopenharmony_ci	.name		= MWL8K_NAME,
636362306a36Sopenharmony_ci	.id_table	= mwl8k_pci_id_table,
636462306a36Sopenharmony_ci	.probe		= mwl8k_probe,
636562306a36Sopenharmony_ci	.remove		= mwl8k_remove,
636662306a36Sopenharmony_ci};
636762306a36Sopenharmony_ci
636862306a36Sopenharmony_cimodule_pci_driver(mwl8k_driver);
636962306a36Sopenharmony_ci
637062306a36Sopenharmony_ciMODULE_DESCRIPTION(MWL8K_DESC);
637162306a36Sopenharmony_ciMODULE_VERSION(MWL8K_VERSION);
637262306a36Sopenharmony_ciMODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com>");
637362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
6374