18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * drivers/net/wireless/mwl8k.c
38c2ecf20Sopenharmony_ci * Driver for Marvell TOPDOG 802.11 Wireless cards
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2008, 2009, 2010 Marvell Semiconductor Inc.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
88c2ecf20Sopenharmony_ci * License version 2.  This program is licensed "as is" without any
98c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/sched.h>
168c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
178c2ecf20Sopenharmony_ci#include <linux/list.h>
188c2ecf20Sopenharmony_ci#include <linux/pci.h>
198c2ecf20Sopenharmony_ci#include <linux/delay.h>
208c2ecf20Sopenharmony_ci#include <linux/completion.h>
218c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci#include <net/mac80211.h>
248c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
258c2ecf20Sopenharmony_ci#include <linux/firmware.h>
268c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define MWL8K_DESC	"Marvell TOPDOG(R) 802.11 Wireless Network Driver"
298c2ecf20Sopenharmony_ci#define MWL8K_NAME	KBUILD_MODNAME
308c2ecf20Sopenharmony_ci#define MWL8K_VERSION	"0.13"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* Module parameters */
338c2ecf20Sopenharmony_cistatic bool ap_mode_default;
348c2ecf20Sopenharmony_cimodule_param(ap_mode_default, bool, 0);
358c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ap_mode_default,
368c2ecf20Sopenharmony_ci		 "Set to 1 to make ap mode the default instead of sta mode");
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* Register definitions */
398c2ecf20Sopenharmony_ci#define MWL8K_HIU_GEN_PTR			0x00000c10
408c2ecf20Sopenharmony_ci#define  MWL8K_MODE_STA				 0x0000005a
418c2ecf20Sopenharmony_ci#define  MWL8K_MODE_AP				 0x000000a5
428c2ecf20Sopenharmony_ci#define MWL8K_HIU_INT_CODE			0x00000c14
438c2ecf20Sopenharmony_ci#define  MWL8K_FWSTA_READY			 0xf0f1f2f4
448c2ecf20Sopenharmony_ci#define  MWL8K_FWAP_READY			 0xf1f2f4a5
458c2ecf20Sopenharmony_ci#define  MWL8K_INT_CODE_CMD_FINISHED		 0x00000005
468c2ecf20Sopenharmony_ci#define MWL8K_HIU_SCRATCH			0x00000c40
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* Host->device communications */
498c2ecf20Sopenharmony_ci#define MWL8K_HIU_H2A_INTERRUPT_EVENTS		0x00000c18
508c2ecf20Sopenharmony_ci#define MWL8K_HIU_H2A_INTERRUPT_STATUS		0x00000c1c
518c2ecf20Sopenharmony_ci#define MWL8K_HIU_H2A_INTERRUPT_MASK		0x00000c20
528c2ecf20Sopenharmony_ci#define MWL8K_HIU_H2A_INTERRUPT_CLEAR_SEL	0x00000c24
538c2ecf20Sopenharmony_ci#define MWL8K_HIU_H2A_INTERRUPT_STATUS_MASK	0x00000c28
548c2ecf20Sopenharmony_ci#define  MWL8K_H2A_INT_DUMMY			 (1 << 20)
558c2ecf20Sopenharmony_ci#define  MWL8K_H2A_INT_RESET			 (1 << 15)
568c2ecf20Sopenharmony_ci#define  MWL8K_H2A_INT_DOORBELL			 (1 << 1)
578c2ecf20Sopenharmony_ci#define  MWL8K_H2A_INT_PPA_READY		 (1 << 0)
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/* Device->host communications */
608c2ecf20Sopenharmony_ci#define MWL8K_HIU_A2H_INTERRUPT_EVENTS		0x00000c2c
618c2ecf20Sopenharmony_ci#define MWL8K_HIU_A2H_INTERRUPT_STATUS		0x00000c30
628c2ecf20Sopenharmony_ci#define MWL8K_HIU_A2H_INTERRUPT_MASK		0x00000c34
638c2ecf20Sopenharmony_ci#define MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL	0x00000c38
648c2ecf20Sopenharmony_ci#define MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK	0x00000c3c
658c2ecf20Sopenharmony_ci#define  MWL8K_A2H_INT_DUMMY			 (1 << 20)
668c2ecf20Sopenharmony_ci#define  MWL8K_A2H_INT_BA_WATCHDOG		 (1 << 14)
678c2ecf20Sopenharmony_ci#define  MWL8K_A2H_INT_CHNL_SWITCHED		 (1 << 11)
688c2ecf20Sopenharmony_ci#define  MWL8K_A2H_INT_QUEUE_EMPTY		 (1 << 10)
698c2ecf20Sopenharmony_ci#define  MWL8K_A2H_INT_RADAR_DETECT		 (1 << 7)
708c2ecf20Sopenharmony_ci#define  MWL8K_A2H_INT_RADIO_ON			 (1 << 6)
718c2ecf20Sopenharmony_ci#define  MWL8K_A2H_INT_RADIO_OFF		 (1 << 5)
728c2ecf20Sopenharmony_ci#define  MWL8K_A2H_INT_MAC_EVENT		 (1 << 3)
738c2ecf20Sopenharmony_ci#define  MWL8K_A2H_INT_OPC_DONE			 (1 << 2)
748c2ecf20Sopenharmony_ci#define  MWL8K_A2H_INT_RX_READY			 (1 << 1)
758c2ecf20Sopenharmony_ci#define  MWL8K_A2H_INT_TX_DONE			 (1 << 0)
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/* HW micro second timer register
788c2ecf20Sopenharmony_ci * located at offset 0xA600. This
798c2ecf20Sopenharmony_ci * will be used to timestamp tx
808c2ecf20Sopenharmony_ci * packets.
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#define	MWL8K_HW_TIMER_REGISTER			0x0000a600
848c2ecf20Sopenharmony_ci#define BBU_RXRDY_CNT_REG			0x0000a860
858c2ecf20Sopenharmony_ci#define NOK_CCA_CNT_REG				0x0000a6a0
868c2ecf20Sopenharmony_ci#define BBU_AVG_NOISE_VAL			0x67
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci#define MWL8K_A2H_EVENTS	(MWL8K_A2H_INT_DUMMY | \
898c2ecf20Sopenharmony_ci				 MWL8K_A2H_INT_CHNL_SWITCHED | \
908c2ecf20Sopenharmony_ci				 MWL8K_A2H_INT_QUEUE_EMPTY | \
918c2ecf20Sopenharmony_ci				 MWL8K_A2H_INT_RADAR_DETECT | \
928c2ecf20Sopenharmony_ci				 MWL8K_A2H_INT_RADIO_ON | \
938c2ecf20Sopenharmony_ci				 MWL8K_A2H_INT_RADIO_OFF | \
948c2ecf20Sopenharmony_ci				 MWL8K_A2H_INT_MAC_EVENT | \
958c2ecf20Sopenharmony_ci				 MWL8K_A2H_INT_OPC_DONE | \
968c2ecf20Sopenharmony_ci				 MWL8K_A2H_INT_RX_READY | \
978c2ecf20Sopenharmony_ci				 MWL8K_A2H_INT_TX_DONE | \
988c2ecf20Sopenharmony_ci				 MWL8K_A2H_INT_BA_WATCHDOG)
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci#define MWL8K_RX_QUEUES		1
1018c2ecf20Sopenharmony_ci#define MWL8K_TX_WMM_QUEUES	4
1028c2ecf20Sopenharmony_ci#define MWL8K_MAX_AMPDU_QUEUES	8
1038c2ecf20Sopenharmony_ci#define MWL8K_MAX_TX_QUEUES	(MWL8K_TX_WMM_QUEUES + MWL8K_MAX_AMPDU_QUEUES)
1048c2ecf20Sopenharmony_ci#define mwl8k_tx_queues(priv)	(MWL8K_TX_WMM_QUEUES + (priv)->num_ampdu_queues)
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/* txpriorities are mapped with hw queues.
1078c2ecf20Sopenharmony_ci * Each hw queue has a txpriority.
1088c2ecf20Sopenharmony_ci */
1098c2ecf20Sopenharmony_ci#define TOTAL_HW_TX_QUEUES	8
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/* Each HW queue can have one AMPDU stream.
1128c2ecf20Sopenharmony_ci * But, because one of the hw queue is reserved,
1138c2ecf20Sopenharmony_ci * maximum AMPDU queues that can be created are
1148c2ecf20Sopenharmony_ci * one short of total tx queues.
1158c2ecf20Sopenharmony_ci */
1168c2ecf20Sopenharmony_ci#define MWL8K_NUM_AMPDU_STREAMS	(TOTAL_HW_TX_QUEUES - 1)
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci#define MWL8K_NUM_CHANS 18
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistruct rxd_ops {
1218c2ecf20Sopenharmony_ci	int rxd_size;
1228c2ecf20Sopenharmony_ci	void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr);
1238c2ecf20Sopenharmony_ci	void (*rxd_refill)(void *rxd, dma_addr_t addr, int len);
1248c2ecf20Sopenharmony_ci	int (*rxd_process)(void *rxd, struct ieee80211_rx_status *status,
1258c2ecf20Sopenharmony_ci			   __le16 *qos, s8 *noise);
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistruct mwl8k_device_info {
1298c2ecf20Sopenharmony_ci	char *part_name;
1308c2ecf20Sopenharmony_ci	char *helper_image;
1318c2ecf20Sopenharmony_ci	char *fw_image_sta;
1328c2ecf20Sopenharmony_ci	char *fw_image_ap;
1338c2ecf20Sopenharmony_ci	struct rxd_ops *ap_rxd_ops;
1348c2ecf20Sopenharmony_ci	u32 fw_api_ap;
1358c2ecf20Sopenharmony_ci};
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistruct mwl8k_rx_queue {
1388c2ecf20Sopenharmony_ci	int rxd_count;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* hw receives here */
1418c2ecf20Sopenharmony_ci	int head;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* refill descs here */
1448c2ecf20Sopenharmony_ci	int tail;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	void *rxd;
1478c2ecf20Sopenharmony_ci	dma_addr_t rxd_dma;
1488c2ecf20Sopenharmony_ci	struct {
1498c2ecf20Sopenharmony_ci		struct sk_buff *skb;
1508c2ecf20Sopenharmony_ci		DEFINE_DMA_UNMAP_ADDR(dma);
1518c2ecf20Sopenharmony_ci	} *buf;
1528c2ecf20Sopenharmony_ci};
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistruct mwl8k_tx_queue {
1558c2ecf20Sopenharmony_ci	/* hw transmits here */
1568c2ecf20Sopenharmony_ci	int head;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* sw appends here */
1598c2ecf20Sopenharmony_ci	int tail;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	unsigned int len;
1628c2ecf20Sopenharmony_ci	struct mwl8k_tx_desc *txd;
1638c2ecf20Sopenharmony_ci	dma_addr_t txd_dma;
1648c2ecf20Sopenharmony_ci	struct sk_buff **skb;
1658c2ecf20Sopenharmony_ci};
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cienum {
1688c2ecf20Sopenharmony_ci	AMPDU_NO_STREAM,
1698c2ecf20Sopenharmony_ci	AMPDU_STREAM_NEW,
1708c2ecf20Sopenharmony_ci	AMPDU_STREAM_IN_PROGRESS,
1718c2ecf20Sopenharmony_ci	AMPDU_STREAM_ACTIVE,
1728c2ecf20Sopenharmony_ci};
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistruct mwl8k_ampdu_stream {
1758c2ecf20Sopenharmony_ci	struct ieee80211_sta *sta;
1768c2ecf20Sopenharmony_ci	u8 tid;
1778c2ecf20Sopenharmony_ci	u8 state;
1788c2ecf20Sopenharmony_ci	u8 idx;
1798c2ecf20Sopenharmony_ci};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistruct mwl8k_priv {
1828c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw;
1838c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
1848c2ecf20Sopenharmony_ci	int irq;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	struct mwl8k_device_info *device_info;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	void __iomem *sram;
1898c2ecf20Sopenharmony_ci	void __iomem *regs;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	/* firmware */
1928c2ecf20Sopenharmony_ci	const struct firmware *fw_helper;
1938c2ecf20Sopenharmony_ci	const struct firmware *fw_ucode;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	/* hardware/firmware parameters */
1968c2ecf20Sopenharmony_ci	bool ap_fw;
1978c2ecf20Sopenharmony_ci	struct rxd_ops *rxd_ops;
1988c2ecf20Sopenharmony_ci	struct ieee80211_supported_band band_24;
1998c2ecf20Sopenharmony_ci	struct ieee80211_channel channels_24[14];
2008c2ecf20Sopenharmony_ci	struct ieee80211_rate rates_24[13];
2018c2ecf20Sopenharmony_ci	struct ieee80211_supported_band band_50;
2028c2ecf20Sopenharmony_ci	struct ieee80211_channel channels_50[9];
2038c2ecf20Sopenharmony_ci	struct ieee80211_rate rates_50[8];
2048c2ecf20Sopenharmony_ci	u32 ap_macids_supported;
2058c2ecf20Sopenharmony_ci	u32 sta_macids_supported;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/* Ampdu stream information */
2088c2ecf20Sopenharmony_ci	u8 num_ampdu_queues;
2098c2ecf20Sopenharmony_ci	spinlock_t stream_lock;
2108c2ecf20Sopenharmony_ci	struct mwl8k_ampdu_stream ampdu[MWL8K_MAX_AMPDU_QUEUES];
2118c2ecf20Sopenharmony_ci	struct work_struct watchdog_ba_handle;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/* firmware access */
2148c2ecf20Sopenharmony_ci	struct mutex fw_mutex;
2158c2ecf20Sopenharmony_ci	struct task_struct *fw_mutex_owner;
2168c2ecf20Sopenharmony_ci	struct task_struct *hw_restart_owner;
2178c2ecf20Sopenharmony_ci	int fw_mutex_depth;
2188c2ecf20Sopenharmony_ci	struct completion *hostcmd_wait;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	atomic_t watchdog_event_pending;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	/* lock held over TX and TX reap */
2238c2ecf20Sopenharmony_ci	spinlock_t tx_lock;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* TX quiesce completion, protected by fw_mutex and tx_lock */
2268c2ecf20Sopenharmony_ci	struct completion *tx_wait;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/* List of interfaces.  */
2298c2ecf20Sopenharmony_ci	u32 macids_used;
2308c2ecf20Sopenharmony_ci	struct list_head vif_list;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	/* power management status cookie from firmware */
2338c2ecf20Sopenharmony_ci	u32 *cookie;
2348c2ecf20Sopenharmony_ci	dma_addr_t cookie_dma;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	u16 num_mcaddrs;
2378c2ecf20Sopenharmony_ci	u8 hw_rev;
2388c2ecf20Sopenharmony_ci	u32 fw_rev;
2398c2ecf20Sopenharmony_ci	u32 caps;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	/*
2428c2ecf20Sopenharmony_ci	 * Running count of TX packets in flight, to avoid
2438c2ecf20Sopenharmony_ci	 * iterating over the transmit rings each time.
2448c2ecf20Sopenharmony_ci	 */
2458c2ecf20Sopenharmony_ci	int pending_tx_pkts;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	struct mwl8k_rx_queue rxq[MWL8K_RX_QUEUES];
2488c2ecf20Sopenharmony_ci	struct mwl8k_tx_queue txq[MWL8K_MAX_TX_QUEUES];
2498c2ecf20Sopenharmony_ci	u32 txq_offset[MWL8K_MAX_TX_QUEUES];
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	bool radio_on;
2528c2ecf20Sopenharmony_ci	bool radio_short_preamble;
2538c2ecf20Sopenharmony_ci	bool sniffer_enabled;
2548c2ecf20Sopenharmony_ci	bool wmm_enabled;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	/* XXX need to convert this to handle multiple interfaces */
2578c2ecf20Sopenharmony_ci	bool capture_beacon;
2588c2ecf20Sopenharmony_ci	u8 capture_bssid[ETH_ALEN];
2598c2ecf20Sopenharmony_ci	struct sk_buff *beacon_skb;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	/*
2628c2ecf20Sopenharmony_ci	 * This FJ worker has to be global as it is scheduled from the
2638c2ecf20Sopenharmony_ci	 * RX handler.  At this point we don't know which interface it
2648c2ecf20Sopenharmony_ci	 * belongs to until the list of bssids waiting to complete join
2658c2ecf20Sopenharmony_ci	 * is checked.
2668c2ecf20Sopenharmony_ci	 */
2678c2ecf20Sopenharmony_ci	struct work_struct finalize_join_worker;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	/* Tasklet to perform TX reclaim.  */
2708c2ecf20Sopenharmony_ci	struct tasklet_struct poll_tx_task;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	/* Tasklet to perform RX.  */
2738c2ecf20Sopenharmony_ci	struct tasklet_struct poll_rx_task;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* Most recently reported noise in dBm */
2768c2ecf20Sopenharmony_ci	s8 noise;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/*
2798c2ecf20Sopenharmony_ci	 * preserve the queue configurations so they can be restored if/when
2808c2ecf20Sopenharmony_ci	 * the firmware image is swapped.
2818c2ecf20Sopenharmony_ci	 */
2828c2ecf20Sopenharmony_ci	struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_WMM_QUEUES];
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	/* To perform the task of reloading the firmware */
2858c2ecf20Sopenharmony_ci	struct work_struct fw_reload;
2868c2ecf20Sopenharmony_ci	bool hw_restart_in_progress;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/* async firmware loading state */
2898c2ecf20Sopenharmony_ci	unsigned fw_state;
2908c2ecf20Sopenharmony_ci	char *fw_pref;
2918c2ecf20Sopenharmony_ci	char *fw_alt;
2928c2ecf20Sopenharmony_ci	bool is_8764;
2938c2ecf20Sopenharmony_ci	struct completion firmware_loading_complete;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/* bitmap of running BSSes */
2968c2ecf20Sopenharmony_ci	u32 running_bsses;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* ACS related */
2998c2ecf20Sopenharmony_ci	bool sw_scan_start;
3008c2ecf20Sopenharmony_ci	struct ieee80211_channel *acs_chan;
3018c2ecf20Sopenharmony_ci	unsigned long channel_time;
3028c2ecf20Sopenharmony_ci	struct survey_info survey[MWL8K_NUM_CHANS];
3038c2ecf20Sopenharmony_ci};
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci#define MAX_WEP_KEY_LEN         13
3068c2ecf20Sopenharmony_ci#define NUM_WEP_KEYS            4
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci/* Per interface specific private data */
3098c2ecf20Sopenharmony_cistruct mwl8k_vif {
3108c2ecf20Sopenharmony_ci	struct list_head list;
3118c2ecf20Sopenharmony_ci	struct ieee80211_vif *vif;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/* Firmware macid for this vif.  */
3148c2ecf20Sopenharmony_ci	int macid;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* Non AMPDU sequence number assigned by driver.  */
3178c2ecf20Sopenharmony_ci	u16 seqno;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	/* Saved WEP keys */
3208c2ecf20Sopenharmony_ci	struct {
3218c2ecf20Sopenharmony_ci		u8 enabled;
3228c2ecf20Sopenharmony_ci		u8 key[sizeof(struct ieee80211_key_conf) + MAX_WEP_KEY_LEN];
3238c2ecf20Sopenharmony_ci	} wep_key_conf[NUM_WEP_KEYS];
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	/* BSSID */
3268c2ecf20Sopenharmony_ci	u8 bssid[ETH_ALEN];
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	/* A flag to indicate is HW crypto is enabled for this bssid */
3298c2ecf20Sopenharmony_ci	bool is_hw_crypto_enabled;
3308c2ecf20Sopenharmony_ci};
3318c2ecf20Sopenharmony_ci#define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv))
3328c2ecf20Sopenharmony_ci#define IEEE80211_KEY_CONF(_u8) ((struct ieee80211_key_conf *)(_u8))
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistruct tx_traffic_info {
3358c2ecf20Sopenharmony_ci	u32 start_time;
3368c2ecf20Sopenharmony_ci	u32 pkts;
3378c2ecf20Sopenharmony_ci};
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci#define MWL8K_MAX_TID 8
3408c2ecf20Sopenharmony_cistruct mwl8k_sta {
3418c2ecf20Sopenharmony_ci	/* Index into station database. Returned by UPDATE_STADB.  */
3428c2ecf20Sopenharmony_ci	u8 peer_id;
3438c2ecf20Sopenharmony_ci	u8 is_ampdu_allowed;
3448c2ecf20Sopenharmony_ci	struct tx_traffic_info tx_stats[MWL8K_MAX_TID];
3458c2ecf20Sopenharmony_ci};
3468c2ecf20Sopenharmony_ci#define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv))
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic const struct ieee80211_channel mwl8k_channels_24[] = {
3498c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2412, .hw_value = 1, },
3508c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2417, .hw_value = 2, },
3518c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2422, .hw_value = 3, },
3528c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2427, .hw_value = 4, },
3538c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2432, .hw_value = 5, },
3548c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2437, .hw_value = 6, },
3558c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2442, .hw_value = 7, },
3568c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2447, .hw_value = 8, },
3578c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2452, .hw_value = 9, },
3588c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2457, .hw_value = 10, },
3598c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2462, .hw_value = 11, },
3608c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2467, .hw_value = 12, },
3618c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2472, .hw_value = 13, },
3628c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_2GHZ, .center_freq = 2484, .hw_value = 14, },
3638c2ecf20Sopenharmony_ci};
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic const struct ieee80211_rate mwl8k_rates_24[] = {
3668c2ecf20Sopenharmony_ci	{ .bitrate = 10, .hw_value = 2, },
3678c2ecf20Sopenharmony_ci	{ .bitrate = 20, .hw_value = 4, },
3688c2ecf20Sopenharmony_ci	{ .bitrate = 55, .hw_value = 11, },
3698c2ecf20Sopenharmony_ci	{ .bitrate = 110, .hw_value = 22, },
3708c2ecf20Sopenharmony_ci	{ .bitrate = 220, .hw_value = 44, },
3718c2ecf20Sopenharmony_ci	{ .bitrate = 60, .hw_value = 12, },
3728c2ecf20Sopenharmony_ci	{ .bitrate = 90, .hw_value = 18, },
3738c2ecf20Sopenharmony_ci	{ .bitrate = 120, .hw_value = 24, },
3748c2ecf20Sopenharmony_ci	{ .bitrate = 180, .hw_value = 36, },
3758c2ecf20Sopenharmony_ci	{ .bitrate = 240, .hw_value = 48, },
3768c2ecf20Sopenharmony_ci	{ .bitrate = 360, .hw_value = 72, },
3778c2ecf20Sopenharmony_ci	{ .bitrate = 480, .hw_value = 96, },
3788c2ecf20Sopenharmony_ci	{ .bitrate = 540, .hw_value = 108, },
3798c2ecf20Sopenharmony_ci};
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic const struct ieee80211_channel mwl8k_channels_50[] = {
3828c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5180, .hw_value = 36, },
3838c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5200, .hw_value = 40, },
3848c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5220, .hw_value = 44, },
3858c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5240, .hw_value = 48, },
3868c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5745, .hw_value = 149, },
3878c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5765, .hw_value = 153, },
3888c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5785, .hw_value = 157, },
3898c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5805, .hw_value = 161, },
3908c2ecf20Sopenharmony_ci	{ .band = NL80211_BAND_5GHZ, .center_freq = 5825, .hw_value = 165, },
3918c2ecf20Sopenharmony_ci};
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic const struct ieee80211_rate mwl8k_rates_50[] = {
3948c2ecf20Sopenharmony_ci	{ .bitrate = 60, .hw_value = 12, },
3958c2ecf20Sopenharmony_ci	{ .bitrate = 90, .hw_value = 18, },
3968c2ecf20Sopenharmony_ci	{ .bitrate = 120, .hw_value = 24, },
3978c2ecf20Sopenharmony_ci	{ .bitrate = 180, .hw_value = 36, },
3988c2ecf20Sopenharmony_ci	{ .bitrate = 240, .hw_value = 48, },
3998c2ecf20Sopenharmony_ci	{ .bitrate = 360, .hw_value = 72, },
4008c2ecf20Sopenharmony_ci	{ .bitrate = 480, .hw_value = 96, },
4018c2ecf20Sopenharmony_ci	{ .bitrate = 540, .hw_value = 108, },
4028c2ecf20Sopenharmony_ci};
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci/* Set or get info from Firmware */
4058c2ecf20Sopenharmony_ci#define MWL8K_CMD_GET			0x0000
4068c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET			0x0001
4078c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET_LIST		0x0002
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci/* Firmware command codes */
4108c2ecf20Sopenharmony_ci#define MWL8K_CMD_CODE_DNLD		0x0001
4118c2ecf20Sopenharmony_ci#define MWL8K_CMD_GET_HW_SPEC		0x0003
4128c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET_HW_SPEC		0x0004
4138c2ecf20Sopenharmony_ci#define MWL8K_CMD_MAC_MULTICAST_ADR	0x0010
4148c2ecf20Sopenharmony_ci#define MWL8K_CMD_GET_STAT		0x0014
4158c2ecf20Sopenharmony_ci#define MWL8K_CMD_BBP_REG_ACCESS	0x001a
4168c2ecf20Sopenharmony_ci#define MWL8K_CMD_RADIO_CONTROL		0x001c
4178c2ecf20Sopenharmony_ci#define MWL8K_CMD_RF_TX_POWER		0x001e
4188c2ecf20Sopenharmony_ci#define MWL8K_CMD_TX_POWER		0x001f
4198c2ecf20Sopenharmony_ci#define MWL8K_CMD_RF_ANTENNA		0x0020
4208c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET_BEACON		0x0100		/* per-vif */
4218c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET_PRE_SCAN		0x0107
4228c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET_POST_SCAN		0x0108
4238c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET_RF_CHANNEL	0x010a
4248c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET_AID		0x010d
4258c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET_RATE		0x0110
4268c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET_FINALIZE_JOIN	0x0111
4278c2ecf20Sopenharmony_ci#define MWL8K_CMD_RTS_THRESHOLD		0x0113
4288c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET_SLOT		0x0114
4298c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET_EDCA_PARAMS	0x0115
4308c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET_WMM_MODE		0x0123
4318c2ecf20Sopenharmony_ci#define MWL8K_CMD_MIMO_CONFIG		0x0125
4328c2ecf20Sopenharmony_ci#define MWL8K_CMD_USE_FIXED_RATE	0x0126
4338c2ecf20Sopenharmony_ci#define MWL8K_CMD_ENABLE_SNIFFER	0x0150
4348c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET_MAC_ADDR		0x0202		/* per-vif */
4358c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET_RATEADAPT_MODE	0x0203
4368c2ecf20Sopenharmony_ci#define MWL8K_CMD_GET_WATCHDOG_BITMAP	0x0205
4378c2ecf20Sopenharmony_ci#define MWL8K_CMD_DEL_MAC_ADDR		0x0206		/* per-vif */
4388c2ecf20Sopenharmony_ci#define MWL8K_CMD_BSS_START		0x1100		/* per-vif */
4398c2ecf20Sopenharmony_ci#define MWL8K_CMD_SET_NEW_STN		0x1111		/* per-vif */
4408c2ecf20Sopenharmony_ci#define MWL8K_CMD_UPDATE_ENCRYPTION	0x1122		/* per-vif */
4418c2ecf20Sopenharmony_ci#define MWL8K_CMD_UPDATE_STADB		0x1123
4428c2ecf20Sopenharmony_ci#define MWL8K_CMD_BASTREAM		0x1125
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci#define MWL8K_LEGACY_5G_RATE_OFFSET \
4458c2ecf20Sopenharmony_ci	(ARRAY_SIZE(mwl8k_rates_24) - ARRAY_SIZE(mwl8k_rates_50))
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	u16 command = le16_to_cpu(cmd);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci#define MWL8K_CMDNAME(x)	case MWL8K_CMD_##x: do {\
4528c2ecf20Sopenharmony_ci					snprintf(buf, bufsize, "%s", #x);\
4538c2ecf20Sopenharmony_ci					return buf;\
4548c2ecf20Sopenharmony_ci					} while (0)
4558c2ecf20Sopenharmony_ci	switch (command & ~0x8000) {
4568c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(CODE_DNLD);
4578c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(GET_HW_SPEC);
4588c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(SET_HW_SPEC);
4598c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(MAC_MULTICAST_ADR);
4608c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(GET_STAT);
4618c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(RADIO_CONTROL);
4628c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(RF_TX_POWER);
4638c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(TX_POWER);
4648c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(RF_ANTENNA);
4658c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(SET_BEACON);
4668c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(SET_PRE_SCAN);
4678c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(SET_POST_SCAN);
4688c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(SET_RF_CHANNEL);
4698c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(SET_AID);
4708c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(SET_RATE);
4718c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(SET_FINALIZE_JOIN);
4728c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(RTS_THRESHOLD);
4738c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(SET_SLOT);
4748c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(SET_EDCA_PARAMS);
4758c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(SET_WMM_MODE);
4768c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(MIMO_CONFIG);
4778c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(USE_FIXED_RATE);
4788c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(ENABLE_SNIFFER);
4798c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(SET_MAC_ADDR);
4808c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(SET_RATEADAPT_MODE);
4818c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(BSS_START);
4828c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(SET_NEW_STN);
4838c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(UPDATE_ENCRYPTION);
4848c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(UPDATE_STADB);
4858c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(BASTREAM);
4868c2ecf20Sopenharmony_ci		MWL8K_CMDNAME(GET_WATCHDOG_BITMAP);
4878c2ecf20Sopenharmony_ci	default:
4888c2ecf20Sopenharmony_ci		snprintf(buf, bufsize, "0x%x", cmd);
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci#undef MWL8K_CMDNAME
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	return buf;
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci/* Hardware and firmware reset */
4968c2ecf20Sopenharmony_cistatic void mwl8k_hw_reset(struct mwl8k_priv *priv)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_RESET,
4998c2ecf20Sopenharmony_ci		priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
5008c2ecf20Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_RESET,
5018c2ecf20Sopenharmony_ci		priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
5028c2ecf20Sopenharmony_ci	msleep(20);
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci/* Release fw image */
5068c2ecf20Sopenharmony_cistatic void mwl8k_release_fw(const struct firmware **fw)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	if (*fw == NULL)
5098c2ecf20Sopenharmony_ci		return;
5108c2ecf20Sopenharmony_ci	release_firmware(*fw);
5118c2ecf20Sopenharmony_ci	*fw = NULL;
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic void mwl8k_release_firmware(struct mwl8k_priv *priv)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	mwl8k_release_fw(&priv->fw_ucode);
5178c2ecf20Sopenharmony_ci	mwl8k_release_fw(&priv->fw_helper);
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci/* states for asynchronous f/w loading */
5218c2ecf20Sopenharmony_cistatic void mwl8k_fw_state_machine(const struct firmware *fw, void *context);
5228c2ecf20Sopenharmony_cienum {
5238c2ecf20Sopenharmony_ci	FW_STATE_INIT = 0,
5248c2ecf20Sopenharmony_ci	FW_STATE_LOADING_PREF,
5258c2ecf20Sopenharmony_ci	FW_STATE_LOADING_ALT,
5268c2ecf20Sopenharmony_ci	FW_STATE_ERROR,
5278c2ecf20Sopenharmony_ci};
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci/* Request fw image */
5308c2ecf20Sopenharmony_cistatic int mwl8k_request_fw(struct mwl8k_priv *priv,
5318c2ecf20Sopenharmony_ci			    const char *fname, const struct firmware **fw,
5328c2ecf20Sopenharmony_ci			    bool nowait)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	/* release current image */
5358c2ecf20Sopenharmony_ci	if (*fw != NULL)
5368c2ecf20Sopenharmony_ci		mwl8k_release_fw(fw);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	if (nowait)
5398c2ecf20Sopenharmony_ci		return request_firmware_nowait(THIS_MODULE, 1, fname,
5408c2ecf20Sopenharmony_ci					       &priv->pdev->dev, GFP_KERNEL,
5418c2ecf20Sopenharmony_ci					       priv, mwl8k_fw_state_machine);
5428c2ecf20Sopenharmony_ci	else
5438c2ecf20Sopenharmony_ci		return request_firmware(fw, fname, &priv->pdev->dev);
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic int mwl8k_request_firmware(struct mwl8k_priv *priv, char *fw_image,
5478c2ecf20Sopenharmony_ci				  bool nowait)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	struct mwl8k_device_info *di = priv->device_info;
5508c2ecf20Sopenharmony_ci	int rc;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	if (di->helper_image != NULL) {
5538c2ecf20Sopenharmony_ci		if (nowait)
5548c2ecf20Sopenharmony_ci			rc = mwl8k_request_fw(priv, di->helper_image,
5558c2ecf20Sopenharmony_ci					      &priv->fw_helper, true);
5568c2ecf20Sopenharmony_ci		else
5578c2ecf20Sopenharmony_ci			rc = mwl8k_request_fw(priv, di->helper_image,
5588c2ecf20Sopenharmony_ci					      &priv->fw_helper, false);
5598c2ecf20Sopenharmony_ci		if (rc)
5608c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: Error requesting helper fw %s\n",
5618c2ecf20Sopenharmony_ci			       pci_name(priv->pdev), di->helper_image);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci		if (rc || nowait)
5648c2ecf20Sopenharmony_ci			return rc;
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	if (nowait) {
5688c2ecf20Sopenharmony_ci		/*
5698c2ecf20Sopenharmony_ci		 * if we get here, no helper image is needed.  Skip the
5708c2ecf20Sopenharmony_ci		 * FW_STATE_INIT state.
5718c2ecf20Sopenharmony_ci		 */
5728c2ecf20Sopenharmony_ci		priv->fw_state = FW_STATE_LOADING_PREF;
5738c2ecf20Sopenharmony_ci		rc = mwl8k_request_fw(priv, fw_image,
5748c2ecf20Sopenharmony_ci				      &priv->fw_ucode,
5758c2ecf20Sopenharmony_ci				      true);
5768c2ecf20Sopenharmony_ci	} else
5778c2ecf20Sopenharmony_ci		rc = mwl8k_request_fw(priv, fw_image,
5788c2ecf20Sopenharmony_ci				      &priv->fw_ucode, false);
5798c2ecf20Sopenharmony_ci	if (rc) {
5808c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: Error requesting firmware file %s\n",
5818c2ecf20Sopenharmony_ci		       pci_name(priv->pdev), fw_image);
5828c2ecf20Sopenharmony_ci		mwl8k_release_fw(&priv->fw_helper);
5838c2ecf20Sopenharmony_ci		return rc;
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	return 0;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistruct mwl8k_cmd_pkt {
5908c2ecf20Sopenharmony_ci	__le16	code;
5918c2ecf20Sopenharmony_ci	__le16	length;
5928c2ecf20Sopenharmony_ci	__u8	seq_num;
5938c2ecf20Sopenharmony_ci	__u8	macid;
5948c2ecf20Sopenharmony_ci	__le16	result;
5958c2ecf20Sopenharmony_ci	char	payload[];
5968c2ecf20Sopenharmony_ci} __packed;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci/*
5998c2ecf20Sopenharmony_ci * Firmware loading.
6008c2ecf20Sopenharmony_ci */
6018c2ecf20Sopenharmony_cistatic int
6028c2ecf20Sopenharmony_cimwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	void __iomem *regs = priv->regs;
6058c2ecf20Sopenharmony_ci	dma_addr_t dma_addr;
6068c2ecf20Sopenharmony_ci	int loops;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	dma_addr = pci_map_single(priv->pdev, data, length, PCI_DMA_TODEVICE);
6098c2ecf20Sopenharmony_ci	if (pci_dma_mapping_error(priv->pdev, dma_addr))
6108c2ecf20Sopenharmony_ci		return -ENOMEM;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR);
6138c2ecf20Sopenharmony_ci	iowrite32(0, regs + MWL8K_HIU_INT_CODE);
6148c2ecf20Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_DOORBELL,
6158c2ecf20Sopenharmony_ci		regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
6168c2ecf20Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_DUMMY,
6178c2ecf20Sopenharmony_ci		regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	loops = 1000;
6208c2ecf20Sopenharmony_ci	do {
6218c2ecf20Sopenharmony_ci		u32 int_code;
6228c2ecf20Sopenharmony_ci		if (priv->is_8764) {
6238c2ecf20Sopenharmony_ci			int_code = ioread32(regs +
6248c2ecf20Sopenharmony_ci					    MWL8K_HIU_H2A_INTERRUPT_STATUS);
6258c2ecf20Sopenharmony_ci			if (int_code == 0)
6268c2ecf20Sopenharmony_ci				break;
6278c2ecf20Sopenharmony_ci		} else {
6288c2ecf20Sopenharmony_ci			int_code = ioread32(regs + MWL8K_HIU_INT_CODE);
6298c2ecf20Sopenharmony_ci			if (int_code == MWL8K_INT_CODE_CMD_FINISHED) {
6308c2ecf20Sopenharmony_ci				iowrite32(0, regs + MWL8K_HIU_INT_CODE);
6318c2ecf20Sopenharmony_ci				break;
6328c2ecf20Sopenharmony_ci			}
6338c2ecf20Sopenharmony_ci		}
6348c2ecf20Sopenharmony_ci		cond_resched();
6358c2ecf20Sopenharmony_ci		udelay(1);
6368c2ecf20Sopenharmony_ci	} while (--loops);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	pci_unmap_single(priv->pdev, dma_addr, length, PCI_DMA_TODEVICE);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	return loops ? 0 : -ETIMEDOUT;
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cistatic int mwl8k_load_fw_image(struct mwl8k_priv *priv,
6448c2ecf20Sopenharmony_ci				const u8 *data, size_t length)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt *cmd;
6478c2ecf20Sopenharmony_ci	int done;
6488c2ecf20Sopenharmony_ci	int rc = 0;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	cmd = kmalloc(sizeof(*cmd) + 256, GFP_KERNEL);
6518c2ecf20Sopenharmony_ci	if (cmd == NULL)
6528c2ecf20Sopenharmony_ci		return -ENOMEM;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	cmd->code = cpu_to_le16(MWL8K_CMD_CODE_DNLD);
6558c2ecf20Sopenharmony_ci	cmd->seq_num = 0;
6568c2ecf20Sopenharmony_ci	cmd->macid = 0;
6578c2ecf20Sopenharmony_ci	cmd->result = 0;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	done = 0;
6608c2ecf20Sopenharmony_ci	while (length) {
6618c2ecf20Sopenharmony_ci		int block_size = length > 256 ? 256 : length;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci		memcpy(cmd->payload, data + done, block_size);
6648c2ecf20Sopenharmony_ci		cmd->length = cpu_to_le16(block_size);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci		rc = mwl8k_send_fw_load_cmd(priv, cmd,
6678c2ecf20Sopenharmony_ci						sizeof(*cmd) + block_size);
6688c2ecf20Sopenharmony_ci		if (rc)
6698c2ecf20Sopenharmony_ci			break;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci		done += block_size;
6728c2ecf20Sopenharmony_ci		length -= block_size;
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	if (!rc) {
6768c2ecf20Sopenharmony_ci		cmd->length = 0;
6778c2ecf20Sopenharmony_ci		rc = mwl8k_send_fw_load_cmd(priv, cmd, sizeof(*cmd));
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	kfree(cmd);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	return rc;
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic int mwl8k_feed_fw_image(struct mwl8k_priv *priv,
6868c2ecf20Sopenharmony_ci				const u8 *data, size_t length)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	unsigned char *buffer;
6898c2ecf20Sopenharmony_ci	int may_continue, rc = 0;
6908c2ecf20Sopenharmony_ci	u32 done, prev_block_size;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	buffer = kmalloc(1024, GFP_KERNEL);
6938c2ecf20Sopenharmony_ci	if (buffer == NULL)
6948c2ecf20Sopenharmony_ci		return -ENOMEM;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	done = 0;
6978c2ecf20Sopenharmony_ci	prev_block_size = 0;
6988c2ecf20Sopenharmony_ci	may_continue = 1000;
6998c2ecf20Sopenharmony_ci	while (may_continue > 0) {
7008c2ecf20Sopenharmony_ci		u32 block_size;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci		block_size = ioread32(priv->regs + MWL8K_HIU_SCRATCH);
7038c2ecf20Sopenharmony_ci		if (block_size & 1) {
7048c2ecf20Sopenharmony_ci			block_size &= ~1;
7058c2ecf20Sopenharmony_ci			may_continue--;
7068c2ecf20Sopenharmony_ci		} else {
7078c2ecf20Sopenharmony_ci			done += prev_block_size;
7088c2ecf20Sopenharmony_ci			length -= prev_block_size;
7098c2ecf20Sopenharmony_ci		}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci		if (block_size > 1024 || block_size > length) {
7128c2ecf20Sopenharmony_ci			rc = -EOVERFLOW;
7138c2ecf20Sopenharmony_ci			break;
7148c2ecf20Sopenharmony_ci		}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci		if (length == 0) {
7178c2ecf20Sopenharmony_ci			rc = 0;
7188c2ecf20Sopenharmony_ci			break;
7198c2ecf20Sopenharmony_ci		}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci		if (block_size == 0) {
7228c2ecf20Sopenharmony_ci			rc = -EPROTO;
7238c2ecf20Sopenharmony_ci			may_continue--;
7248c2ecf20Sopenharmony_ci			udelay(1);
7258c2ecf20Sopenharmony_ci			continue;
7268c2ecf20Sopenharmony_ci		}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci		prev_block_size = block_size;
7298c2ecf20Sopenharmony_ci		memcpy(buffer, data + done, block_size);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci		rc = mwl8k_send_fw_load_cmd(priv, buffer, block_size);
7328c2ecf20Sopenharmony_ci		if (rc)
7338c2ecf20Sopenharmony_ci			break;
7348c2ecf20Sopenharmony_ci	}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	if (!rc && length != 0)
7378c2ecf20Sopenharmony_ci		rc = -EREMOTEIO;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	kfree(buffer);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	return rc;
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_cistatic int mwl8k_load_firmware(struct ieee80211_hw *hw)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
7478c2ecf20Sopenharmony_ci	const struct firmware *fw = priv->fw_ucode;
7488c2ecf20Sopenharmony_ci	int rc;
7498c2ecf20Sopenharmony_ci	int loops;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	if (!memcmp(fw->data, "\x01\x00\x00\x00", 4) && !priv->is_8764) {
7528c2ecf20Sopenharmony_ci		const struct firmware *helper = priv->fw_helper;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci		if (helper == NULL) {
7558c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: helper image needed but none "
7568c2ecf20Sopenharmony_ci			       "given\n", pci_name(priv->pdev));
7578c2ecf20Sopenharmony_ci			return -EINVAL;
7588c2ecf20Sopenharmony_ci		}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci		rc = mwl8k_load_fw_image(priv, helper->data, helper->size);
7618c2ecf20Sopenharmony_ci		if (rc) {
7628c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: unable to load firmware "
7638c2ecf20Sopenharmony_ci			       "helper image\n", pci_name(priv->pdev));
7648c2ecf20Sopenharmony_ci			return rc;
7658c2ecf20Sopenharmony_ci		}
7668c2ecf20Sopenharmony_ci		msleep(20);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci		rc = mwl8k_feed_fw_image(priv, fw->data, fw->size);
7698c2ecf20Sopenharmony_ci	} else {
7708c2ecf20Sopenharmony_ci		if (priv->is_8764)
7718c2ecf20Sopenharmony_ci			rc = mwl8k_feed_fw_image(priv, fw->data, fw->size);
7728c2ecf20Sopenharmony_ci		else
7738c2ecf20Sopenharmony_ci			rc = mwl8k_load_fw_image(priv, fw->data, fw->size);
7748c2ecf20Sopenharmony_ci	}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	if (rc) {
7778c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: unable to load firmware image\n",
7788c2ecf20Sopenharmony_ci		       pci_name(priv->pdev));
7798c2ecf20Sopenharmony_ci		return rc;
7808c2ecf20Sopenharmony_ci	}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR);
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	loops = 500000;
7858c2ecf20Sopenharmony_ci	do {
7868c2ecf20Sopenharmony_ci		u32 ready_code;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci		ready_code = ioread32(priv->regs + MWL8K_HIU_INT_CODE);
7898c2ecf20Sopenharmony_ci		if (ready_code == MWL8K_FWAP_READY) {
7908c2ecf20Sopenharmony_ci			priv->ap_fw = true;
7918c2ecf20Sopenharmony_ci			break;
7928c2ecf20Sopenharmony_ci		} else if (ready_code == MWL8K_FWSTA_READY) {
7938c2ecf20Sopenharmony_ci			priv->ap_fw = false;
7948c2ecf20Sopenharmony_ci			break;
7958c2ecf20Sopenharmony_ci		}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci		cond_resched();
7988c2ecf20Sopenharmony_ci		udelay(1);
7998c2ecf20Sopenharmony_ci	} while (--loops);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	return loops ? 0 : -ETIMEDOUT;
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci/* DMA header used by firmware and hardware.  */
8068c2ecf20Sopenharmony_cistruct mwl8k_dma_data {
8078c2ecf20Sopenharmony_ci	__le16 fwlen;
8088c2ecf20Sopenharmony_ci	struct ieee80211_hdr wh;
8098c2ecf20Sopenharmony_ci	char data[];
8108c2ecf20Sopenharmony_ci} __packed;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci/* Routines to add/remove DMA header from skb.  */
8138c2ecf20Sopenharmony_cistatic inline void mwl8k_remove_dma_header(struct sk_buff *skb, __le16 qos)
8148c2ecf20Sopenharmony_ci{
8158c2ecf20Sopenharmony_ci	struct mwl8k_dma_data *tr;
8168c2ecf20Sopenharmony_ci	int hdrlen;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	tr = (struct mwl8k_dma_data *)skb->data;
8198c2ecf20Sopenharmony_ci	hdrlen = ieee80211_hdrlen(tr->wh.frame_control);
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	if (hdrlen != sizeof(tr->wh)) {
8228c2ecf20Sopenharmony_ci		if (ieee80211_is_data_qos(tr->wh.frame_control)) {
8238c2ecf20Sopenharmony_ci			memmove(tr->data - hdrlen, &tr->wh, hdrlen - 2);
8248c2ecf20Sopenharmony_ci			*((__le16 *)(tr->data - 2)) = qos;
8258c2ecf20Sopenharmony_ci		} else {
8268c2ecf20Sopenharmony_ci			memmove(tr->data - hdrlen, &tr->wh, hdrlen);
8278c2ecf20Sopenharmony_ci		}
8288c2ecf20Sopenharmony_ci	}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (hdrlen != sizeof(*tr))
8318c2ecf20Sopenharmony_ci		skb_pull(skb, sizeof(*tr) - hdrlen);
8328c2ecf20Sopenharmony_ci}
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci#define REDUCED_TX_HEADROOM	8
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_cistatic void
8378c2ecf20Sopenharmony_cimwl8k_add_dma_header(struct mwl8k_priv *priv, struct sk_buff *skb,
8388c2ecf20Sopenharmony_ci						int head_pad, int tail_pad)
8398c2ecf20Sopenharmony_ci{
8408c2ecf20Sopenharmony_ci	struct ieee80211_hdr *wh;
8418c2ecf20Sopenharmony_ci	int hdrlen;
8428c2ecf20Sopenharmony_ci	int reqd_hdrlen;
8438c2ecf20Sopenharmony_ci	struct mwl8k_dma_data *tr;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	/*
8468c2ecf20Sopenharmony_ci	 * Add a firmware DMA header; the firmware requires that we
8478c2ecf20Sopenharmony_ci	 * present a 2-byte payload length followed by a 4-address
8488c2ecf20Sopenharmony_ci	 * header (without QoS field), followed (optionally) by any
8498c2ecf20Sopenharmony_ci	 * WEP/ExtIV header (but only filled in for CCMP).
8508c2ecf20Sopenharmony_ci	 */
8518c2ecf20Sopenharmony_ci	wh = (struct ieee80211_hdr *)skb->data;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	hdrlen = ieee80211_hdrlen(wh->frame_control);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	/*
8568c2ecf20Sopenharmony_ci	 * Check if skb_resize is required because of
8578c2ecf20Sopenharmony_ci	 * tx_headroom adjustment.
8588c2ecf20Sopenharmony_ci	 */
8598c2ecf20Sopenharmony_ci	if (priv->ap_fw && (hdrlen < (sizeof(struct ieee80211_cts)
8608c2ecf20Sopenharmony_ci						+ REDUCED_TX_HEADROOM))) {
8618c2ecf20Sopenharmony_ci		if (pskb_expand_head(skb, REDUCED_TX_HEADROOM, 0, GFP_ATOMIC)) {
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci			wiphy_err(priv->hw->wiphy,
8648c2ecf20Sopenharmony_ci					"Failed to reallocate TX buffer\n");
8658c2ecf20Sopenharmony_ci			return;
8668c2ecf20Sopenharmony_ci		}
8678c2ecf20Sopenharmony_ci		skb->truesize += REDUCED_TX_HEADROOM;
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	reqd_hdrlen = sizeof(*tr) + head_pad;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	if (hdrlen != reqd_hdrlen)
8738c2ecf20Sopenharmony_ci		skb_push(skb, reqd_hdrlen - hdrlen);
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	if (ieee80211_is_data_qos(wh->frame_control))
8768c2ecf20Sopenharmony_ci		hdrlen -= IEEE80211_QOS_CTL_LEN;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	tr = (struct mwl8k_dma_data *)skb->data;
8798c2ecf20Sopenharmony_ci	if (wh != &tr->wh)
8808c2ecf20Sopenharmony_ci		memmove(&tr->wh, wh, hdrlen);
8818c2ecf20Sopenharmony_ci	if (hdrlen != sizeof(tr->wh))
8828c2ecf20Sopenharmony_ci		memset(((void *)&tr->wh) + hdrlen, 0, sizeof(tr->wh) - hdrlen);
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	/*
8858c2ecf20Sopenharmony_ci	 * Firmware length is the length of the fully formed "802.11
8868c2ecf20Sopenharmony_ci	 * payload".  That is, everything except for the 802.11 header.
8878c2ecf20Sopenharmony_ci	 * This includes all crypto material including the MIC.
8888c2ecf20Sopenharmony_ci	 */
8898c2ecf20Sopenharmony_ci	tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr) + tail_pad);
8908c2ecf20Sopenharmony_ci}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_cistatic void mwl8k_encapsulate_tx_frame(struct mwl8k_priv *priv,
8938c2ecf20Sopenharmony_ci		struct sk_buff *skb)
8948c2ecf20Sopenharmony_ci{
8958c2ecf20Sopenharmony_ci	struct ieee80211_hdr *wh;
8968c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *tx_info;
8978c2ecf20Sopenharmony_ci	struct ieee80211_key_conf *key_conf;
8988c2ecf20Sopenharmony_ci	int data_pad;
8998c2ecf20Sopenharmony_ci	int head_pad = 0;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	wh = (struct ieee80211_hdr *)skb->data;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	tx_info = IEEE80211_SKB_CB(skb);
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	key_conf = NULL;
9068c2ecf20Sopenharmony_ci	if (ieee80211_is_data(wh->frame_control))
9078c2ecf20Sopenharmony_ci		key_conf = tx_info->control.hw_key;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	/*
9108c2ecf20Sopenharmony_ci	 * Make sure the packet header is in the DMA header format (4-address
9118c2ecf20Sopenharmony_ci	 * without QoS), and add head & tail padding when HW crypto is enabled.
9128c2ecf20Sopenharmony_ci	 *
9138c2ecf20Sopenharmony_ci	 * We have the following trailer padding requirements:
9148c2ecf20Sopenharmony_ci	 * - WEP: 4 trailer bytes (ICV)
9158c2ecf20Sopenharmony_ci	 * - TKIP: 12 trailer bytes (8 MIC + 4 ICV)
9168c2ecf20Sopenharmony_ci	 * - CCMP: 8 trailer bytes (MIC)
9178c2ecf20Sopenharmony_ci	 */
9188c2ecf20Sopenharmony_ci	data_pad = 0;
9198c2ecf20Sopenharmony_ci	if (key_conf != NULL) {
9208c2ecf20Sopenharmony_ci		head_pad = key_conf->iv_len;
9218c2ecf20Sopenharmony_ci		switch (key_conf->cipher) {
9228c2ecf20Sopenharmony_ci		case WLAN_CIPHER_SUITE_WEP40:
9238c2ecf20Sopenharmony_ci		case WLAN_CIPHER_SUITE_WEP104:
9248c2ecf20Sopenharmony_ci			data_pad = 4;
9258c2ecf20Sopenharmony_ci			break;
9268c2ecf20Sopenharmony_ci		case WLAN_CIPHER_SUITE_TKIP:
9278c2ecf20Sopenharmony_ci			data_pad = 12;
9288c2ecf20Sopenharmony_ci			break;
9298c2ecf20Sopenharmony_ci		case WLAN_CIPHER_SUITE_CCMP:
9308c2ecf20Sopenharmony_ci			data_pad = 8;
9318c2ecf20Sopenharmony_ci			break;
9328c2ecf20Sopenharmony_ci		}
9338c2ecf20Sopenharmony_ci	}
9348c2ecf20Sopenharmony_ci	mwl8k_add_dma_header(priv, skb, head_pad, data_pad);
9358c2ecf20Sopenharmony_ci}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci/*
9388c2ecf20Sopenharmony_ci * Packet reception for 88w8366/88w8764 AP firmware.
9398c2ecf20Sopenharmony_ci */
9408c2ecf20Sopenharmony_cistruct mwl8k_rxd_ap {
9418c2ecf20Sopenharmony_ci	__le16 pkt_len;
9428c2ecf20Sopenharmony_ci	__u8 sq2;
9438c2ecf20Sopenharmony_ci	__u8 rate;
9448c2ecf20Sopenharmony_ci	__le32 pkt_phys_addr;
9458c2ecf20Sopenharmony_ci	__le32 next_rxd_phys_addr;
9468c2ecf20Sopenharmony_ci	__le16 qos_control;
9478c2ecf20Sopenharmony_ci	__le16 htsig2;
9488c2ecf20Sopenharmony_ci	__le32 hw_rssi_info;
9498c2ecf20Sopenharmony_ci	__le32 hw_noise_floor_info;
9508c2ecf20Sopenharmony_ci	__u8 noise_floor;
9518c2ecf20Sopenharmony_ci	__u8 pad0[3];
9528c2ecf20Sopenharmony_ci	__u8 rssi;
9538c2ecf20Sopenharmony_ci	__u8 rx_status;
9548c2ecf20Sopenharmony_ci	__u8 channel;
9558c2ecf20Sopenharmony_ci	__u8 rx_ctrl;
9568c2ecf20Sopenharmony_ci} __packed;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci#define MWL8K_AP_RATE_INFO_MCS_FORMAT		0x80
9598c2ecf20Sopenharmony_ci#define MWL8K_AP_RATE_INFO_40MHZ		0x40
9608c2ecf20Sopenharmony_ci#define MWL8K_AP_RATE_INFO_RATEID(x)		((x) & 0x3f)
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci#define MWL8K_AP_RX_CTRL_OWNED_BY_HOST		0x80
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci/* 8366/8764 AP rx_status bits */
9658c2ecf20Sopenharmony_ci#define MWL8K_AP_RXSTAT_DECRYPT_ERR_MASK		0x80
9668c2ecf20Sopenharmony_ci#define MWL8K_AP_RXSTAT_GENERAL_DECRYPT_ERR		0xFF
9678c2ecf20Sopenharmony_ci#define MWL8K_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR		0x02
9688c2ecf20Sopenharmony_ci#define MWL8K_AP_RXSTAT_WEP_DECRYPT_ICV_ERR		0x04
9698c2ecf20Sopenharmony_ci#define MWL8K_AP_RXSTAT_TKIP_DECRYPT_ICV_ERR		0x08
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_cistatic void mwl8k_rxd_ap_init(void *_rxd, dma_addr_t next_dma_addr)
9728c2ecf20Sopenharmony_ci{
9738c2ecf20Sopenharmony_ci	struct mwl8k_rxd_ap *rxd = _rxd;
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr);
9768c2ecf20Sopenharmony_ci	rxd->rx_ctrl = MWL8K_AP_RX_CTRL_OWNED_BY_HOST;
9778c2ecf20Sopenharmony_ci}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_cistatic void mwl8k_rxd_ap_refill(void *_rxd, dma_addr_t addr, int len)
9808c2ecf20Sopenharmony_ci{
9818c2ecf20Sopenharmony_ci	struct mwl8k_rxd_ap *rxd = _rxd;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	rxd->pkt_len = cpu_to_le16(len);
9848c2ecf20Sopenharmony_ci	rxd->pkt_phys_addr = cpu_to_le32(addr);
9858c2ecf20Sopenharmony_ci	wmb();
9868c2ecf20Sopenharmony_ci	rxd->rx_ctrl = 0;
9878c2ecf20Sopenharmony_ci}
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_cistatic int
9908c2ecf20Sopenharmony_cimwl8k_rxd_ap_process(void *_rxd, struct ieee80211_rx_status *status,
9918c2ecf20Sopenharmony_ci		     __le16 *qos, s8 *noise)
9928c2ecf20Sopenharmony_ci{
9938c2ecf20Sopenharmony_ci	struct mwl8k_rxd_ap *rxd = _rxd;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	if (!(rxd->rx_ctrl & MWL8K_AP_RX_CTRL_OWNED_BY_HOST))
9968c2ecf20Sopenharmony_ci		return -1;
9978c2ecf20Sopenharmony_ci	rmb();
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	memset(status, 0, sizeof(*status));
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	status->signal = -rxd->rssi;
10028c2ecf20Sopenharmony_ci	*noise = -rxd->noise_floor;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	if (rxd->rate & MWL8K_AP_RATE_INFO_MCS_FORMAT) {
10058c2ecf20Sopenharmony_ci		status->encoding = RX_ENC_HT;
10068c2ecf20Sopenharmony_ci		if (rxd->rate & MWL8K_AP_RATE_INFO_40MHZ)
10078c2ecf20Sopenharmony_ci			status->bw = RATE_INFO_BW_40;
10088c2ecf20Sopenharmony_ci		status->rate_idx = MWL8K_AP_RATE_INFO_RATEID(rxd->rate);
10098c2ecf20Sopenharmony_ci	} else {
10108c2ecf20Sopenharmony_ci		int i;
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(mwl8k_rates_24); i++) {
10138c2ecf20Sopenharmony_ci			if (mwl8k_rates_24[i].hw_value == rxd->rate) {
10148c2ecf20Sopenharmony_ci				status->rate_idx = i;
10158c2ecf20Sopenharmony_ci				break;
10168c2ecf20Sopenharmony_ci			}
10178c2ecf20Sopenharmony_ci		}
10188c2ecf20Sopenharmony_ci	}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	if (rxd->channel > 14) {
10218c2ecf20Sopenharmony_ci		status->band = NL80211_BAND_5GHZ;
10228c2ecf20Sopenharmony_ci		if (!(status->encoding == RX_ENC_HT) &&
10238c2ecf20Sopenharmony_ci		    status->rate_idx >= MWL8K_LEGACY_5G_RATE_OFFSET)
10248c2ecf20Sopenharmony_ci			status->rate_idx -= MWL8K_LEGACY_5G_RATE_OFFSET;
10258c2ecf20Sopenharmony_ci	} else {
10268c2ecf20Sopenharmony_ci		status->band = NL80211_BAND_2GHZ;
10278c2ecf20Sopenharmony_ci	}
10288c2ecf20Sopenharmony_ci	status->freq = ieee80211_channel_to_frequency(rxd->channel,
10298c2ecf20Sopenharmony_ci						      status->band);
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	*qos = rxd->qos_control;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	if ((rxd->rx_status != MWL8K_AP_RXSTAT_GENERAL_DECRYPT_ERR) &&
10348c2ecf20Sopenharmony_ci	    (rxd->rx_status & MWL8K_AP_RXSTAT_DECRYPT_ERR_MASK) &&
10358c2ecf20Sopenharmony_ci	    (rxd->rx_status & MWL8K_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR))
10368c2ecf20Sopenharmony_ci		status->flag |= RX_FLAG_MMIC_ERROR;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	return le16_to_cpu(rxd->pkt_len);
10398c2ecf20Sopenharmony_ci}
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_cistatic struct rxd_ops rxd_ap_ops = {
10428c2ecf20Sopenharmony_ci	.rxd_size	= sizeof(struct mwl8k_rxd_ap),
10438c2ecf20Sopenharmony_ci	.rxd_init	= mwl8k_rxd_ap_init,
10448c2ecf20Sopenharmony_ci	.rxd_refill	= mwl8k_rxd_ap_refill,
10458c2ecf20Sopenharmony_ci	.rxd_process	= mwl8k_rxd_ap_process,
10468c2ecf20Sopenharmony_ci};
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci/*
10498c2ecf20Sopenharmony_ci * Packet reception for STA firmware.
10508c2ecf20Sopenharmony_ci */
10518c2ecf20Sopenharmony_cistruct mwl8k_rxd_sta {
10528c2ecf20Sopenharmony_ci	__le16 pkt_len;
10538c2ecf20Sopenharmony_ci	__u8 link_quality;
10548c2ecf20Sopenharmony_ci	__u8 noise_level;
10558c2ecf20Sopenharmony_ci	__le32 pkt_phys_addr;
10568c2ecf20Sopenharmony_ci	__le32 next_rxd_phys_addr;
10578c2ecf20Sopenharmony_ci	__le16 qos_control;
10588c2ecf20Sopenharmony_ci	__le16 rate_info;
10598c2ecf20Sopenharmony_ci	__le32 pad0[4];
10608c2ecf20Sopenharmony_ci	__u8 rssi;
10618c2ecf20Sopenharmony_ci	__u8 channel;
10628c2ecf20Sopenharmony_ci	__le16 pad1;
10638c2ecf20Sopenharmony_ci	__u8 rx_ctrl;
10648c2ecf20Sopenharmony_ci	__u8 rx_status;
10658c2ecf20Sopenharmony_ci	__u8 pad2[2];
10668c2ecf20Sopenharmony_ci} __packed;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci#define MWL8K_STA_RATE_INFO_SHORTPRE		0x8000
10698c2ecf20Sopenharmony_ci#define MWL8K_STA_RATE_INFO_ANTSELECT(x)	(((x) >> 11) & 0x3)
10708c2ecf20Sopenharmony_ci#define MWL8K_STA_RATE_INFO_RATEID(x)		(((x) >> 3) & 0x3f)
10718c2ecf20Sopenharmony_ci#define MWL8K_STA_RATE_INFO_40MHZ		0x0004
10728c2ecf20Sopenharmony_ci#define MWL8K_STA_RATE_INFO_SHORTGI		0x0002
10738c2ecf20Sopenharmony_ci#define MWL8K_STA_RATE_INFO_MCS_FORMAT		0x0001
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci#define MWL8K_STA_RX_CTRL_OWNED_BY_HOST		0x02
10768c2ecf20Sopenharmony_ci#define MWL8K_STA_RX_CTRL_DECRYPT_ERROR		0x04
10778c2ecf20Sopenharmony_ci/* ICV=0 or MIC=1 */
10788c2ecf20Sopenharmony_ci#define MWL8K_STA_RX_CTRL_DEC_ERR_TYPE		0x08
10798c2ecf20Sopenharmony_ci/* Key is uploaded only in failure case */
10808c2ecf20Sopenharmony_ci#define MWL8K_STA_RX_CTRL_KEY_INDEX			0x30
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_cistatic void mwl8k_rxd_sta_init(void *_rxd, dma_addr_t next_dma_addr)
10838c2ecf20Sopenharmony_ci{
10848c2ecf20Sopenharmony_ci	struct mwl8k_rxd_sta *rxd = _rxd;
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr);
10878c2ecf20Sopenharmony_ci	rxd->rx_ctrl = MWL8K_STA_RX_CTRL_OWNED_BY_HOST;
10888c2ecf20Sopenharmony_ci}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_cistatic void mwl8k_rxd_sta_refill(void *_rxd, dma_addr_t addr, int len)
10918c2ecf20Sopenharmony_ci{
10928c2ecf20Sopenharmony_ci	struct mwl8k_rxd_sta *rxd = _rxd;
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	rxd->pkt_len = cpu_to_le16(len);
10958c2ecf20Sopenharmony_ci	rxd->pkt_phys_addr = cpu_to_le32(addr);
10968c2ecf20Sopenharmony_ci	wmb();
10978c2ecf20Sopenharmony_ci	rxd->rx_ctrl = 0;
10988c2ecf20Sopenharmony_ci}
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_cistatic int
11018c2ecf20Sopenharmony_cimwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status,
11028c2ecf20Sopenharmony_ci		       __le16 *qos, s8 *noise)
11038c2ecf20Sopenharmony_ci{
11048c2ecf20Sopenharmony_ci	struct mwl8k_rxd_sta *rxd = _rxd;
11058c2ecf20Sopenharmony_ci	u16 rate_info;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	if (!(rxd->rx_ctrl & MWL8K_STA_RX_CTRL_OWNED_BY_HOST))
11088c2ecf20Sopenharmony_ci		return -1;
11098c2ecf20Sopenharmony_ci	rmb();
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	rate_info = le16_to_cpu(rxd->rate_info);
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	memset(status, 0, sizeof(*status));
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	status->signal = -rxd->rssi;
11168c2ecf20Sopenharmony_ci	*noise = -rxd->noise_level;
11178c2ecf20Sopenharmony_ci	status->antenna = MWL8K_STA_RATE_INFO_ANTSELECT(rate_info);
11188c2ecf20Sopenharmony_ci	status->rate_idx = MWL8K_STA_RATE_INFO_RATEID(rate_info);
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	if (rate_info & MWL8K_STA_RATE_INFO_SHORTPRE)
11218c2ecf20Sopenharmony_ci		status->enc_flags |= RX_ENC_FLAG_SHORTPRE;
11228c2ecf20Sopenharmony_ci	if (rate_info & MWL8K_STA_RATE_INFO_40MHZ)
11238c2ecf20Sopenharmony_ci		status->bw = RATE_INFO_BW_40;
11248c2ecf20Sopenharmony_ci	if (rate_info & MWL8K_STA_RATE_INFO_SHORTGI)
11258c2ecf20Sopenharmony_ci		status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
11268c2ecf20Sopenharmony_ci	if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT)
11278c2ecf20Sopenharmony_ci		status->encoding = RX_ENC_HT;
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	if (rxd->channel > 14) {
11308c2ecf20Sopenharmony_ci		status->band = NL80211_BAND_5GHZ;
11318c2ecf20Sopenharmony_ci		if (!(status->encoding == RX_ENC_HT) &&
11328c2ecf20Sopenharmony_ci		    status->rate_idx >= MWL8K_LEGACY_5G_RATE_OFFSET)
11338c2ecf20Sopenharmony_ci			status->rate_idx -= MWL8K_LEGACY_5G_RATE_OFFSET;
11348c2ecf20Sopenharmony_ci	} else {
11358c2ecf20Sopenharmony_ci		status->band = NL80211_BAND_2GHZ;
11368c2ecf20Sopenharmony_ci	}
11378c2ecf20Sopenharmony_ci	status->freq = ieee80211_channel_to_frequency(rxd->channel,
11388c2ecf20Sopenharmony_ci						      status->band);
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	*qos = rxd->qos_control;
11418c2ecf20Sopenharmony_ci	if ((rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DECRYPT_ERROR) &&
11428c2ecf20Sopenharmony_ci	    (rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DEC_ERR_TYPE))
11438c2ecf20Sopenharmony_ci		status->flag |= RX_FLAG_MMIC_ERROR;
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	return le16_to_cpu(rxd->pkt_len);
11468c2ecf20Sopenharmony_ci}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_cistatic struct rxd_ops rxd_sta_ops = {
11498c2ecf20Sopenharmony_ci	.rxd_size	= sizeof(struct mwl8k_rxd_sta),
11508c2ecf20Sopenharmony_ci	.rxd_init	= mwl8k_rxd_sta_init,
11518c2ecf20Sopenharmony_ci	.rxd_refill	= mwl8k_rxd_sta_refill,
11528c2ecf20Sopenharmony_ci	.rxd_process	= mwl8k_rxd_sta_process,
11538c2ecf20Sopenharmony_ci};
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci#define MWL8K_RX_DESCS		256
11578c2ecf20Sopenharmony_ci#define MWL8K_RX_MAXSZ		3800
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_cistatic int mwl8k_rxq_init(struct ieee80211_hw *hw, int index)
11608c2ecf20Sopenharmony_ci{
11618c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
11628c2ecf20Sopenharmony_ci	struct mwl8k_rx_queue *rxq = priv->rxq + index;
11638c2ecf20Sopenharmony_ci	int size;
11648c2ecf20Sopenharmony_ci	int i;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	rxq->rxd_count = 0;
11678c2ecf20Sopenharmony_ci	rxq->head = 0;
11688c2ecf20Sopenharmony_ci	rxq->tail = 0;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	size = MWL8K_RX_DESCS * priv->rxd_ops->rxd_size;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	rxq->rxd = pci_zalloc_consistent(priv->pdev, size, &rxq->rxd_dma);
11738c2ecf20Sopenharmony_ci	if (rxq->rxd == NULL) {
11748c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy, "failed to alloc RX descriptors\n");
11758c2ecf20Sopenharmony_ci		return -ENOMEM;
11768c2ecf20Sopenharmony_ci	}
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	rxq->buf = kcalloc(MWL8K_RX_DESCS, sizeof(*rxq->buf), GFP_KERNEL);
11798c2ecf20Sopenharmony_ci	if (rxq->buf == NULL) {
11808c2ecf20Sopenharmony_ci		pci_free_consistent(priv->pdev, size, rxq->rxd, rxq->rxd_dma);
11818c2ecf20Sopenharmony_ci		return -ENOMEM;
11828c2ecf20Sopenharmony_ci	}
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	for (i = 0; i < MWL8K_RX_DESCS; i++) {
11858c2ecf20Sopenharmony_ci		int desc_size;
11868c2ecf20Sopenharmony_ci		void *rxd;
11878c2ecf20Sopenharmony_ci		int nexti;
11888c2ecf20Sopenharmony_ci		dma_addr_t next_dma_addr;
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci		desc_size = priv->rxd_ops->rxd_size;
11918c2ecf20Sopenharmony_ci		rxd = rxq->rxd + (i * priv->rxd_ops->rxd_size);
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci		nexti = i + 1;
11948c2ecf20Sopenharmony_ci		if (nexti == MWL8K_RX_DESCS)
11958c2ecf20Sopenharmony_ci			nexti = 0;
11968c2ecf20Sopenharmony_ci		next_dma_addr = rxq->rxd_dma + (nexti * desc_size);
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci		priv->rxd_ops->rxd_init(rxd, next_dma_addr);
11998c2ecf20Sopenharmony_ci	}
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	return 0;
12028c2ecf20Sopenharmony_ci}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_cistatic int rxq_refill(struct ieee80211_hw *hw, int index, int limit)
12058c2ecf20Sopenharmony_ci{
12068c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
12078c2ecf20Sopenharmony_ci	struct mwl8k_rx_queue *rxq = priv->rxq + index;
12088c2ecf20Sopenharmony_ci	int refilled;
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	refilled = 0;
12118c2ecf20Sopenharmony_ci	while (rxq->rxd_count < MWL8K_RX_DESCS && limit--) {
12128c2ecf20Sopenharmony_ci		struct sk_buff *skb;
12138c2ecf20Sopenharmony_ci		dma_addr_t addr;
12148c2ecf20Sopenharmony_ci		int rx;
12158c2ecf20Sopenharmony_ci		void *rxd;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci		skb = dev_alloc_skb(MWL8K_RX_MAXSZ);
12188c2ecf20Sopenharmony_ci		if (skb == NULL)
12198c2ecf20Sopenharmony_ci			break;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci		addr = pci_map_single(priv->pdev, skb->data,
12228c2ecf20Sopenharmony_ci				      MWL8K_RX_MAXSZ, DMA_FROM_DEVICE);
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci		rxq->rxd_count++;
12258c2ecf20Sopenharmony_ci		rx = rxq->tail++;
12268c2ecf20Sopenharmony_ci		if (rxq->tail == MWL8K_RX_DESCS)
12278c2ecf20Sopenharmony_ci			rxq->tail = 0;
12288c2ecf20Sopenharmony_ci		rxq->buf[rx].skb = skb;
12298c2ecf20Sopenharmony_ci		dma_unmap_addr_set(&rxq->buf[rx], dma, addr);
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci		rxd = rxq->rxd + (rx * priv->rxd_ops->rxd_size);
12328c2ecf20Sopenharmony_ci		priv->rxd_ops->rxd_refill(rxd, addr, MWL8K_RX_MAXSZ);
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci		refilled++;
12358c2ecf20Sopenharmony_ci	}
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	return refilled;
12388c2ecf20Sopenharmony_ci}
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci/* Must be called only when the card's reception is completely halted */
12418c2ecf20Sopenharmony_cistatic void mwl8k_rxq_deinit(struct ieee80211_hw *hw, int index)
12428c2ecf20Sopenharmony_ci{
12438c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
12448c2ecf20Sopenharmony_ci	struct mwl8k_rx_queue *rxq = priv->rxq + index;
12458c2ecf20Sopenharmony_ci	int i;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	if (rxq->rxd == NULL)
12488c2ecf20Sopenharmony_ci		return;
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	for (i = 0; i < MWL8K_RX_DESCS; i++) {
12518c2ecf20Sopenharmony_ci		if (rxq->buf[i].skb != NULL) {
12528c2ecf20Sopenharmony_ci			pci_unmap_single(priv->pdev,
12538c2ecf20Sopenharmony_ci					 dma_unmap_addr(&rxq->buf[i], dma),
12548c2ecf20Sopenharmony_ci					 MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE);
12558c2ecf20Sopenharmony_ci			dma_unmap_addr_set(&rxq->buf[i], dma, 0);
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci			kfree_skb(rxq->buf[i].skb);
12588c2ecf20Sopenharmony_ci			rxq->buf[i].skb = NULL;
12598c2ecf20Sopenharmony_ci		}
12608c2ecf20Sopenharmony_ci	}
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	kfree(rxq->buf);
12638c2ecf20Sopenharmony_ci	rxq->buf = NULL;
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	pci_free_consistent(priv->pdev,
12668c2ecf20Sopenharmony_ci			    MWL8K_RX_DESCS * priv->rxd_ops->rxd_size,
12678c2ecf20Sopenharmony_ci			    rxq->rxd, rxq->rxd_dma);
12688c2ecf20Sopenharmony_ci	rxq->rxd = NULL;
12698c2ecf20Sopenharmony_ci}
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci/*
12738c2ecf20Sopenharmony_ci * Scan a list of BSSIDs to process for finalize join.
12748c2ecf20Sopenharmony_ci * Allows for extension to process multiple BSSIDs.
12758c2ecf20Sopenharmony_ci */
12768c2ecf20Sopenharmony_cistatic inline int
12778c2ecf20Sopenharmony_cimwl8k_capture_bssid(struct mwl8k_priv *priv, struct ieee80211_hdr *wh)
12788c2ecf20Sopenharmony_ci{
12798c2ecf20Sopenharmony_ci	return priv->capture_beacon &&
12808c2ecf20Sopenharmony_ci		ieee80211_is_beacon(wh->frame_control) &&
12818c2ecf20Sopenharmony_ci		ether_addr_equal_64bits(wh->addr3, priv->capture_bssid);
12828c2ecf20Sopenharmony_ci}
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_cistatic inline void mwl8k_save_beacon(struct ieee80211_hw *hw,
12858c2ecf20Sopenharmony_ci				     struct sk_buff *skb)
12868c2ecf20Sopenharmony_ci{
12878c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	priv->capture_beacon = false;
12908c2ecf20Sopenharmony_ci	eth_zero_addr(priv->capture_bssid);
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	/*
12938c2ecf20Sopenharmony_ci	 * Use GFP_ATOMIC as rxq_process is called from
12948c2ecf20Sopenharmony_ci	 * the primary interrupt handler, memory allocation call
12958c2ecf20Sopenharmony_ci	 * must not sleep.
12968c2ecf20Sopenharmony_ci	 */
12978c2ecf20Sopenharmony_ci	priv->beacon_skb = skb_copy(skb, GFP_ATOMIC);
12988c2ecf20Sopenharmony_ci	if (priv->beacon_skb != NULL)
12998c2ecf20Sopenharmony_ci		ieee80211_queue_work(hw, &priv->finalize_join_worker);
13008c2ecf20Sopenharmony_ci}
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_cistatic inline struct mwl8k_vif *mwl8k_find_vif_bss(struct list_head *vif_list,
13038c2ecf20Sopenharmony_ci						   u8 *bssid)
13048c2ecf20Sopenharmony_ci{
13058c2ecf20Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif;
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	list_for_each_entry(mwl8k_vif,
13088c2ecf20Sopenharmony_ci			    vif_list, list) {
13098c2ecf20Sopenharmony_ci		if (memcmp(bssid, mwl8k_vif->bssid,
13108c2ecf20Sopenharmony_ci			   ETH_ALEN) == 0)
13118c2ecf20Sopenharmony_ci			return mwl8k_vif;
13128c2ecf20Sopenharmony_ci	}
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci	return NULL;
13158c2ecf20Sopenharmony_ci}
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_cistatic int rxq_process(struct ieee80211_hw *hw, int index, int limit)
13188c2ecf20Sopenharmony_ci{
13198c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
13208c2ecf20Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = NULL;
13218c2ecf20Sopenharmony_ci	struct mwl8k_rx_queue *rxq = priv->rxq + index;
13228c2ecf20Sopenharmony_ci	int processed;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	processed = 0;
13258c2ecf20Sopenharmony_ci	while (rxq->rxd_count && limit--) {
13268c2ecf20Sopenharmony_ci		struct sk_buff *skb;
13278c2ecf20Sopenharmony_ci		void *rxd;
13288c2ecf20Sopenharmony_ci		int pkt_len;
13298c2ecf20Sopenharmony_ci		struct ieee80211_rx_status status;
13308c2ecf20Sopenharmony_ci		struct ieee80211_hdr *wh;
13318c2ecf20Sopenharmony_ci		__le16 qos;
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci		skb = rxq->buf[rxq->head].skb;
13348c2ecf20Sopenharmony_ci		if (skb == NULL)
13358c2ecf20Sopenharmony_ci			break;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci		rxd = rxq->rxd + (rxq->head * priv->rxd_ops->rxd_size);
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci		pkt_len = priv->rxd_ops->rxd_process(rxd, &status, &qos,
13408c2ecf20Sopenharmony_ci							&priv->noise);
13418c2ecf20Sopenharmony_ci		if (pkt_len < 0)
13428c2ecf20Sopenharmony_ci			break;
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci		rxq->buf[rxq->head].skb = NULL;
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci		pci_unmap_single(priv->pdev,
13478c2ecf20Sopenharmony_ci				 dma_unmap_addr(&rxq->buf[rxq->head], dma),
13488c2ecf20Sopenharmony_ci				 MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE);
13498c2ecf20Sopenharmony_ci		dma_unmap_addr_set(&rxq->buf[rxq->head], dma, 0);
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci		rxq->head++;
13528c2ecf20Sopenharmony_ci		if (rxq->head == MWL8K_RX_DESCS)
13538c2ecf20Sopenharmony_ci			rxq->head = 0;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci		rxq->rxd_count--;
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci		wh = &((struct mwl8k_dma_data *)skb->data)->wh;
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci		/*
13608c2ecf20Sopenharmony_ci		 * Check for a pending join operation.  Save a
13618c2ecf20Sopenharmony_ci		 * copy of the beacon and schedule a tasklet to
13628c2ecf20Sopenharmony_ci		 * send a FINALIZE_JOIN command to the firmware.
13638c2ecf20Sopenharmony_ci		 */
13648c2ecf20Sopenharmony_ci		if (mwl8k_capture_bssid(priv, (void *)skb->data))
13658c2ecf20Sopenharmony_ci			mwl8k_save_beacon(hw, skb);
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci		if (ieee80211_has_protected(wh->frame_control)) {
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci			/* Check if hw crypto has been enabled for
13708c2ecf20Sopenharmony_ci			 * this bss. If yes, set the status flags
13718c2ecf20Sopenharmony_ci			 * accordingly
13728c2ecf20Sopenharmony_ci			 */
13738c2ecf20Sopenharmony_ci			mwl8k_vif = mwl8k_find_vif_bss(&priv->vif_list,
13748c2ecf20Sopenharmony_ci								wh->addr1);
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci			if (mwl8k_vif != NULL &&
13778c2ecf20Sopenharmony_ci			    mwl8k_vif->is_hw_crypto_enabled) {
13788c2ecf20Sopenharmony_ci				/*
13798c2ecf20Sopenharmony_ci				 * When MMIC ERROR is encountered
13808c2ecf20Sopenharmony_ci				 * by the firmware, payload is
13818c2ecf20Sopenharmony_ci				 * dropped and only 32 bytes of
13828c2ecf20Sopenharmony_ci				 * mwl8k Firmware header is sent
13838c2ecf20Sopenharmony_ci				 * to the host.
13848c2ecf20Sopenharmony_ci				 *
13858c2ecf20Sopenharmony_ci				 * We need to add four bytes of
13868c2ecf20Sopenharmony_ci				 * key information.  In it
13878c2ecf20Sopenharmony_ci				 * MAC80211 expects keyidx set to
13888c2ecf20Sopenharmony_ci				 * 0 for triggering Counter
13898c2ecf20Sopenharmony_ci				 * Measure of MMIC failure.
13908c2ecf20Sopenharmony_ci				 */
13918c2ecf20Sopenharmony_ci				if (status.flag & RX_FLAG_MMIC_ERROR) {
13928c2ecf20Sopenharmony_ci					struct mwl8k_dma_data *tr;
13938c2ecf20Sopenharmony_ci					tr = (struct mwl8k_dma_data *)skb->data;
13948c2ecf20Sopenharmony_ci					memset((void *)&(tr->data), 0, 4);
13958c2ecf20Sopenharmony_ci					pkt_len += 4;
13968c2ecf20Sopenharmony_ci				}
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci				if (!ieee80211_is_auth(wh->frame_control))
13998c2ecf20Sopenharmony_ci					status.flag |= RX_FLAG_IV_STRIPPED |
14008c2ecf20Sopenharmony_ci						       RX_FLAG_DECRYPTED |
14018c2ecf20Sopenharmony_ci						       RX_FLAG_MMIC_STRIPPED;
14028c2ecf20Sopenharmony_ci			}
14038c2ecf20Sopenharmony_ci		}
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci		skb_put(skb, pkt_len);
14068c2ecf20Sopenharmony_ci		mwl8k_remove_dma_header(skb, qos);
14078c2ecf20Sopenharmony_ci		memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
14088c2ecf20Sopenharmony_ci		ieee80211_rx_irqsafe(hw, skb);
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci		processed++;
14118c2ecf20Sopenharmony_ci	}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	return processed;
14148c2ecf20Sopenharmony_ci}
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci/*
14188c2ecf20Sopenharmony_ci * Packet transmission.
14198c2ecf20Sopenharmony_ci */
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci#define MWL8K_TXD_STATUS_OK			0x00000001
14228c2ecf20Sopenharmony_ci#define MWL8K_TXD_STATUS_OK_RETRY		0x00000002
14238c2ecf20Sopenharmony_ci#define MWL8K_TXD_STATUS_OK_MORE_RETRY		0x00000004
14248c2ecf20Sopenharmony_ci#define MWL8K_TXD_STATUS_MULTICAST_TX		0x00000008
14258c2ecf20Sopenharmony_ci#define MWL8K_TXD_STATUS_FW_OWNED		0x80000000
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci#define MWL8K_QOS_QLEN_UNSPEC			0xff00
14288c2ecf20Sopenharmony_ci#define MWL8K_QOS_ACK_POLICY_MASK		0x0060
14298c2ecf20Sopenharmony_ci#define MWL8K_QOS_ACK_POLICY_NORMAL		0x0000
14308c2ecf20Sopenharmony_ci#define MWL8K_QOS_ACK_POLICY_BLOCKACK		0x0060
14318c2ecf20Sopenharmony_ci#define MWL8K_QOS_EOSP				0x0010
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_cistruct mwl8k_tx_desc {
14348c2ecf20Sopenharmony_ci	__le32 status;
14358c2ecf20Sopenharmony_ci	__u8 data_rate;
14368c2ecf20Sopenharmony_ci	__u8 tx_priority;
14378c2ecf20Sopenharmony_ci	__le16 qos_control;
14388c2ecf20Sopenharmony_ci	__le32 pkt_phys_addr;
14398c2ecf20Sopenharmony_ci	__le16 pkt_len;
14408c2ecf20Sopenharmony_ci	__u8 dest_MAC_addr[ETH_ALEN];
14418c2ecf20Sopenharmony_ci	__le32 next_txd_phys_addr;
14428c2ecf20Sopenharmony_ci	__le32 timestamp;
14438c2ecf20Sopenharmony_ci	__le16 rate_info;
14448c2ecf20Sopenharmony_ci	__u8 peer_id;
14458c2ecf20Sopenharmony_ci	__u8 tx_frag_cnt;
14468c2ecf20Sopenharmony_ci} __packed;
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci#define MWL8K_TX_DESCS		128
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_cistatic int mwl8k_txq_init(struct ieee80211_hw *hw, int index)
14518c2ecf20Sopenharmony_ci{
14528c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
14538c2ecf20Sopenharmony_ci	struct mwl8k_tx_queue *txq = priv->txq + index;
14548c2ecf20Sopenharmony_ci	int size;
14558c2ecf20Sopenharmony_ci	int i;
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	txq->len = 0;
14588c2ecf20Sopenharmony_ci	txq->head = 0;
14598c2ecf20Sopenharmony_ci	txq->tail = 0;
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	size = MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc);
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	txq->txd = pci_zalloc_consistent(priv->pdev, size, &txq->txd_dma);
14648c2ecf20Sopenharmony_ci	if (txq->txd == NULL) {
14658c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy, "failed to alloc TX descriptors\n");
14668c2ecf20Sopenharmony_ci		return -ENOMEM;
14678c2ecf20Sopenharmony_ci	}
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	txq->skb = kcalloc(MWL8K_TX_DESCS, sizeof(*txq->skb), GFP_KERNEL);
14708c2ecf20Sopenharmony_ci	if (txq->skb == NULL) {
14718c2ecf20Sopenharmony_ci		pci_free_consistent(priv->pdev, size, txq->txd, txq->txd_dma);
14728c2ecf20Sopenharmony_ci		txq->txd = NULL;
14738c2ecf20Sopenharmony_ci		return -ENOMEM;
14748c2ecf20Sopenharmony_ci	}
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	for (i = 0; i < MWL8K_TX_DESCS; i++) {
14778c2ecf20Sopenharmony_ci		struct mwl8k_tx_desc *tx_desc;
14788c2ecf20Sopenharmony_ci		int nexti;
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci		tx_desc = txq->txd + i;
14818c2ecf20Sopenharmony_ci		nexti = (i + 1) % MWL8K_TX_DESCS;
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci		tx_desc->status = 0;
14848c2ecf20Sopenharmony_ci		tx_desc->next_txd_phys_addr =
14858c2ecf20Sopenharmony_ci			cpu_to_le32(txq->txd_dma + nexti * sizeof(*tx_desc));
14868c2ecf20Sopenharmony_ci	}
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	return 0;
14898c2ecf20Sopenharmony_ci}
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_cistatic inline void mwl8k_tx_start(struct mwl8k_priv *priv)
14928c2ecf20Sopenharmony_ci{
14938c2ecf20Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_PPA_READY,
14948c2ecf20Sopenharmony_ci		priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
14958c2ecf20Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_DUMMY,
14968c2ecf20Sopenharmony_ci		priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
14978c2ecf20Sopenharmony_ci	ioread32(priv->regs + MWL8K_HIU_INT_CODE);
14988c2ecf20Sopenharmony_ci}
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_cistatic void mwl8k_dump_tx_rings(struct ieee80211_hw *hw)
15018c2ecf20Sopenharmony_ci{
15028c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
15038c2ecf20Sopenharmony_ci	int i;
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++) {
15068c2ecf20Sopenharmony_ci		struct mwl8k_tx_queue *txq = priv->txq + i;
15078c2ecf20Sopenharmony_ci		int fw_owned = 0;
15088c2ecf20Sopenharmony_ci		int drv_owned = 0;
15098c2ecf20Sopenharmony_ci		int unused = 0;
15108c2ecf20Sopenharmony_ci		int desc;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci		for (desc = 0; desc < MWL8K_TX_DESCS; desc++) {
15138c2ecf20Sopenharmony_ci			struct mwl8k_tx_desc *tx_desc = txq->txd + desc;
15148c2ecf20Sopenharmony_ci			u32 status;
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci			status = le32_to_cpu(tx_desc->status);
15178c2ecf20Sopenharmony_ci			if (status & MWL8K_TXD_STATUS_FW_OWNED)
15188c2ecf20Sopenharmony_ci				fw_owned++;
15198c2ecf20Sopenharmony_ci			else
15208c2ecf20Sopenharmony_ci				drv_owned++;
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci			if (tx_desc->pkt_len == 0)
15238c2ecf20Sopenharmony_ci				unused++;
15248c2ecf20Sopenharmony_ci		}
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy,
15278c2ecf20Sopenharmony_ci			  "txq[%d] len=%d head=%d tail=%d "
15288c2ecf20Sopenharmony_ci			  "fw_owned=%d drv_owned=%d unused=%d\n",
15298c2ecf20Sopenharmony_ci			  i,
15308c2ecf20Sopenharmony_ci			  txq->len, txq->head, txq->tail,
15318c2ecf20Sopenharmony_ci			  fw_owned, drv_owned, unused);
15328c2ecf20Sopenharmony_ci	}
15338c2ecf20Sopenharmony_ci}
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci/*
15368c2ecf20Sopenharmony_ci * Must be called with priv->fw_mutex held and tx queues stopped.
15378c2ecf20Sopenharmony_ci */
15388c2ecf20Sopenharmony_ci#define MWL8K_TX_WAIT_TIMEOUT_MS	5000
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_cistatic int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
15418c2ecf20Sopenharmony_ci{
15428c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
15438c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(tx_wait);
15448c2ecf20Sopenharmony_ci	int retry;
15458c2ecf20Sopenharmony_ci	int rc;
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_ci	might_sleep();
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	/* Since fw restart is in progress, allow only the firmware
15508c2ecf20Sopenharmony_ci	 * commands from the restart code and block the other
15518c2ecf20Sopenharmony_ci	 * commands since they are going to fail in any case since
15528c2ecf20Sopenharmony_ci	 * the firmware has crashed
15538c2ecf20Sopenharmony_ci	 */
15548c2ecf20Sopenharmony_ci	if (priv->hw_restart_in_progress) {
15558c2ecf20Sopenharmony_ci		if (priv->hw_restart_owner == current)
15568c2ecf20Sopenharmony_ci			return 0;
15578c2ecf20Sopenharmony_ci		else
15588c2ecf20Sopenharmony_ci			return -EBUSY;
15598c2ecf20Sopenharmony_ci	}
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci	if (atomic_read(&priv->watchdog_event_pending))
15628c2ecf20Sopenharmony_ci		return 0;
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	/*
15658c2ecf20Sopenharmony_ci	 * The TX queues are stopped at this point, so this test
15668c2ecf20Sopenharmony_ci	 * doesn't need to take ->tx_lock.
15678c2ecf20Sopenharmony_ci	 */
15688c2ecf20Sopenharmony_ci	if (!priv->pending_tx_pkts)
15698c2ecf20Sopenharmony_ci		return 0;
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci	retry = 1;
15728c2ecf20Sopenharmony_ci	rc = 0;
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	spin_lock_bh(&priv->tx_lock);
15758c2ecf20Sopenharmony_ci	priv->tx_wait = &tx_wait;
15768c2ecf20Sopenharmony_ci	while (!rc) {
15778c2ecf20Sopenharmony_ci		int oldcount;
15788c2ecf20Sopenharmony_ci		unsigned long timeout;
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci		oldcount = priv->pending_tx_pkts;
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci		spin_unlock_bh(&priv->tx_lock);
15838c2ecf20Sopenharmony_ci		timeout = wait_for_completion_timeout(&tx_wait,
15848c2ecf20Sopenharmony_ci			    msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS));
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci		if (atomic_read(&priv->watchdog_event_pending)) {
15878c2ecf20Sopenharmony_ci			spin_lock_bh(&priv->tx_lock);
15888c2ecf20Sopenharmony_ci			priv->tx_wait = NULL;
15898c2ecf20Sopenharmony_ci			spin_unlock_bh(&priv->tx_lock);
15908c2ecf20Sopenharmony_ci			return 0;
15918c2ecf20Sopenharmony_ci		}
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci		spin_lock_bh(&priv->tx_lock);
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci		if (timeout || !priv->pending_tx_pkts) {
15968c2ecf20Sopenharmony_ci			WARN_ON(priv->pending_tx_pkts);
15978c2ecf20Sopenharmony_ci			if (retry)
15988c2ecf20Sopenharmony_ci				wiphy_notice(hw->wiphy, "tx rings drained\n");
15998c2ecf20Sopenharmony_ci			break;
16008c2ecf20Sopenharmony_ci		}
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci		if (retry) {
16038c2ecf20Sopenharmony_ci			mwl8k_tx_start(priv);
16048c2ecf20Sopenharmony_ci			retry = 0;
16058c2ecf20Sopenharmony_ci			continue;
16068c2ecf20Sopenharmony_ci		}
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci		if (priv->pending_tx_pkts < oldcount) {
16098c2ecf20Sopenharmony_ci			wiphy_notice(hw->wiphy,
16108c2ecf20Sopenharmony_ci				     "waiting for tx rings to drain (%d -> %d pkts)\n",
16118c2ecf20Sopenharmony_ci				     oldcount, priv->pending_tx_pkts);
16128c2ecf20Sopenharmony_ci			retry = 1;
16138c2ecf20Sopenharmony_ci			continue;
16148c2ecf20Sopenharmony_ci		}
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci		priv->tx_wait = NULL;
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy, "tx rings stuck for %d ms\n",
16198c2ecf20Sopenharmony_ci			  MWL8K_TX_WAIT_TIMEOUT_MS);
16208c2ecf20Sopenharmony_ci		mwl8k_dump_tx_rings(hw);
16218c2ecf20Sopenharmony_ci		priv->hw_restart_in_progress = true;
16228c2ecf20Sopenharmony_ci		ieee80211_queue_work(hw, &priv->fw_reload);
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci		rc = -ETIMEDOUT;
16258c2ecf20Sopenharmony_ci	}
16268c2ecf20Sopenharmony_ci	priv->tx_wait = NULL;
16278c2ecf20Sopenharmony_ci	spin_unlock_bh(&priv->tx_lock);
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	return rc;
16308c2ecf20Sopenharmony_ci}
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci#define MWL8K_TXD_SUCCESS(status)				\
16338c2ecf20Sopenharmony_ci	((status) & (MWL8K_TXD_STATUS_OK |			\
16348c2ecf20Sopenharmony_ci		     MWL8K_TXD_STATUS_OK_RETRY |		\
16358c2ecf20Sopenharmony_ci		     MWL8K_TXD_STATUS_OK_MORE_RETRY))
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_cistatic int mwl8k_tid_queue_mapping(u8 tid)
16388c2ecf20Sopenharmony_ci{
16398c2ecf20Sopenharmony_ci	BUG_ON(tid > 7);
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	switch (tid) {
16428c2ecf20Sopenharmony_ci	case 0:
16438c2ecf20Sopenharmony_ci	case 3:
16448c2ecf20Sopenharmony_ci		return IEEE80211_AC_BE;
16458c2ecf20Sopenharmony_ci	case 1:
16468c2ecf20Sopenharmony_ci	case 2:
16478c2ecf20Sopenharmony_ci		return IEEE80211_AC_BK;
16488c2ecf20Sopenharmony_ci	case 4:
16498c2ecf20Sopenharmony_ci	case 5:
16508c2ecf20Sopenharmony_ci		return IEEE80211_AC_VI;
16518c2ecf20Sopenharmony_ci	case 6:
16528c2ecf20Sopenharmony_ci	case 7:
16538c2ecf20Sopenharmony_ci		return IEEE80211_AC_VO;
16548c2ecf20Sopenharmony_ci	default:
16558c2ecf20Sopenharmony_ci		return -1;
16568c2ecf20Sopenharmony_ci	}
16578c2ecf20Sopenharmony_ci}
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci/* The firmware will fill in the rate information
16608c2ecf20Sopenharmony_ci * for each packet that gets queued in the hardware
16618c2ecf20Sopenharmony_ci * and these macros will interpret that info.
16628c2ecf20Sopenharmony_ci */
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci#define RI_FORMAT(a)		  (a & 0x0001)
16658c2ecf20Sopenharmony_ci#define RI_RATE_ID_MCS(a)	 ((a & 0x01f8) >> 3)
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_cistatic int
16688c2ecf20Sopenharmony_cimwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force)
16698c2ecf20Sopenharmony_ci{
16708c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
16718c2ecf20Sopenharmony_ci	struct mwl8k_tx_queue *txq = priv->txq + index;
16728c2ecf20Sopenharmony_ci	int processed;
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	processed = 0;
16758c2ecf20Sopenharmony_ci	while (txq->len > 0 && limit--) {
16768c2ecf20Sopenharmony_ci		int tx;
16778c2ecf20Sopenharmony_ci		struct mwl8k_tx_desc *tx_desc;
16788c2ecf20Sopenharmony_ci		unsigned long addr;
16798c2ecf20Sopenharmony_ci		int size;
16808c2ecf20Sopenharmony_ci		struct sk_buff *skb;
16818c2ecf20Sopenharmony_ci		struct ieee80211_tx_info *info;
16828c2ecf20Sopenharmony_ci		u32 status;
16838c2ecf20Sopenharmony_ci		struct ieee80211_sta *sta;
16848c2ecf20Sopenharmony_ci		struct mwl8k_sta *sta_info = NULL;
16858c2ecf20Sopenharmony_ci		u16 rate_info;
16868c2ecf20Sopenharmony_ci		struct ieee80211_hdr *wh;
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci		tx = txq->head;
16898c2ecf20Sopenharmony_ci		tx_desc = txq->txd + tx;
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_ci		status = le32_to_cpu(tx_desc->status);
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci		if (status & MWL8K_TXD_STATUS_FW_OWNED) {
16948c2ecf20Sopenharmony_ci			if (!force)
16958c2ecf20Sopenharmony_ci				break;
16968c2ecf20Sopenharmony_ci			tx_desc->status &=
16978c2ecf20Sopenharmony_ci				~cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED);
16988c2ecf20Sopenharmony_ci		}
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci		txq->head = (tx + 1) % MWL8K_TX_DESCS;
17018c2ecf20Sopenharmony_ci		BUG_ON(txq->len == 0);
17028c2ecf20Sopenharmony_ci		txq->len--;
17038c2ecf20Sopenharmony_ci		priv->pending_tx_pkts--;
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci		addr = le32_to_cpu(tx_desc->pkt_phys_addr);
17068c2ecf20Sopenharmony_ci		size = le16_to_cpu(tx_desc->pkt_len);
17078c2ecf20Sopenharmony_ci		skb = txq->skb[tx];
17088c2ecf20Sopenharmony_ci		txq->skb[tx] = NULL;
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci		BUG_ON(skb == NULL);
17118c2ecf20Sopenharmony_ci		pci_unmap_single(priv->pdev, addr, size, PCI_DMA_TODEVICE);
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_ci		mwl8k_remove_dma_header(skb, tx_desc->qos_control);
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci		wh = (struct ieee80211_hdr *) skb->data;
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci		/* Mark descriptor as unused */
17188c2ecf20Sopenharmony_ci		tx_desc->pkt_phys_addr = 0;
17198c2ecf20Sopenharmony_ci		tx_desc->pkt_len = 0;
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci		info = IEEE80211_SKB_CB(skb);
17228c2ecf20Sopenharmony_ci		if (ieee80211_is_data(wh->frame_control)) {
17238c2ecf20Sopenharmony_ci			rcu_read_lock();
17248c2ecf20Sopenharmony_ci			sta = ieee80211_find_sta_by_ifaddr(hw, wh->addr1,
17258c2ecf20Sopenharmony_ci							   wh->addr2);
17268c2ecf20Sopenharmony_ci			if (sta) {
17278c2ecf20Sopenharmony_ci				sta_info = MWL8K_STA(sta);
17288c2ecf20Sopenharmony_ci				BUG_ON(sta_info == NULL);
17298c2ecf20Sopenharmony_ci				rate_info = le16_to_cpu(tx_desc->rate_info);
17308c2ecf20Sopenharmony_ci				/* If rate is < 6.5 Mpbs for an ht station
17318c2ecf20Sopenharmony_ci				 * do not form an ampdu. If the station is a
17328c2ecf20Sopenharmony_ci				 * legacy station (format = 0), do not form an
17338c2ecf20Sopenharmony_ci				 * ampdu
17348c2ecf20Sopenharmony_ci				 */
17358c2ecf20Sopenharmony_ci				if (RI_RATE_ID_MCS(rate_info) < 1 ||
17368c2ecf20Sopenharmony_ci				    RI_FORMAT(rate_info) == 0) {
17378c2ecf20Sopenharmony_ci					sta_info->is_ampdu_allowed = false;
17388c2ecf20Sopenharmony_ci				} else {
17398c2ecf20Sopenharmony_ci					sta_info->is_ampdu_allowed = true;
17408c2ecf20Sopenharmony_ci				}
17418c2ecf20Sopenharmony_ci			}
17428c2ecf20Sopenharmony_ci			rcu_read_unlock();
17438c2ecf20Sopenharmony_ci		}
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci		ieee80211_tx_info_clear_status(info);
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci		/* Rate control is happening in the firmware.
17488c2ecf20Sopenharmony_ci		 * Ensure no tx rate is being reported.
17498c2ecf20Sopenharmony_ci		 */
17508c2ecf20Sopenharmony_ci		info->status.rates[0].idx = -1;
17518c2ecf20Sopenharmony_ci		info->status.rates[0].count = 1;
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci		if (MWL8K_TXD_SUCCESS(status))
17548c2ecf20Sopenharmony_ci			info->flags |= IEEE80211_TX_STAT_ACK;
17558c2ecf20Sopenharmony_ci
17568c2ecf20Sopenharmony_ci		ieee80211_tx_status_irqsafe(hw, skb);
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci		processed++;
17598c2ecf20Sopenharmony_ci	}
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	return processed;
17628c2ecf20Sopenharmony_ci}
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci/* must be called only when the card's transmit is completely halted */
17658c2ecf20Sopenharmony_cistatic void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index)
17668c2ecf20Sopenharmony_ci{
17678c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
17688c2ecf20Sopenharmony_ci	struct mwl8k_tx_queue *txq = priv->txq + index;
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	if (txq->txd == NULL)
17718c2ecf20Sopenharmony_ci		return;
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci	mwl8k_txq_reclaim(hw, index, INT_MAX, 1);
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci	kfree(txq->skb);
17768c2ecf20Sopenharmony_ci	txq->skb = NULL;
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	pci_free_consistent(priv->pdev,
17798c2ecf20Sopenharmony_ci			    MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc),
17808c2ecf20Sopenharmony_ci			    txq->txd, txq->txd_dma);
17818c2ecf20Sopenharmony_ci	txq->txd = NULL;
17828c2ecf20Sopenharmony_ci}
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci/* caller must hold priv->stream_lock when calling the stream functions */
17858c2ecf20Sopenharmony_cistatic struct mwl8k_ampdu_stream *
17868c2ecf20Sopenharmony_cimwl8k_add_stream(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 tid)
17878c2ecf20Sopenharmony_ci{
17888c2ecf20Sopenharmony_ci	struct mwl8k_ampdu_stream *stream;
17898c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
17908c2ecf20Sopenharmony_ci	int i;
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_ci	for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) {
17938c2ecf20Sopenharmony_ci		stream = &priv->ampdu[i];
17948c2ecf20Sopenharmony_ci		if (stream->state == AMPDU_NO_STREAM) {
17958c2ecf20Sopenharmony_ci			stream->sta = sta;
17968c2ecf20Sopenharmony_ci			stream->state = AMPDU_STREAM_NEW;
17978c2ecf20Sopenharmony_ci			stream->tid = tid;
17988c2ecf20Sopenharmony_ci			stream->idx = i;
17998c2ecf20Sopenharmony_ci			wiphy_debug(hw->wiphy, "Added a new stream for %pM %d",
18008c2ecf20Sopenharmony_ci				    sta->addr, tid);
18018c2ecf20Sopenharmony_ci			return stream;
18028c2ecf20Sopenharmony_ci		}
18038c2ecf20Sopenharmony_ci	}
18048c2ecf20Sopenharmony_ci	return NULL;
18058c2ecf20Sopenharmony_ci}
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_cistatic int
18088c2ecf20Sopenharmony_cimwl8k_start_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream)
18098c2ecf20Sopenharmony_ci{
18108c2ecf20Sopenharmony_ci	int ret;
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	/* if the stream has already been started, don't start it again */
18138c2ecf20Sopenharmony_ci	if (stream->state != AMPDU_STREAM_NEW)
18148c2ecf20Sopenharmony_ci		return 0;
18158c2ecf20Sopenharmony_ci	ret = ieee80211_start_tx_ba_session(stream->sta, stream->tid, 0);
18168c2ecf20Sopenharmony_ci	if (ret)
18178c2ecf20Sopenharmony_ci		wiphy_debug(hw->wiphy, "Failed to start stream for %pM %d: "
18188c2ecf20Sopenharmony_ci			    "%d\n", stream->sta->addr, stream->tid, ret);
18198c2ecf20Sopenharmony_ci	else
18208c2ecf20Sopenharmony_ci		wiphy_debug(hw->wiphy, "Started stream for %pM %d\n",
18218c2ecf20Sopenharmony_ci			    stream->sta->addr, stream->tid);
18228c2ecf20Sopenharmony_ci	return ret;
18238c2ecf20Sopenharmony_ci}
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_cistatic void
18268c2ecf20Sopenharmony_cimwl8k_remove_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream)
18278c2ecf20Sopenharmony_ci{
18288c2ecf20Sopenharmony_ci	wiphy_debug(hw->wiphy, "Remove stream for %pM %d\n", stream->sta->addr,
18298c2ecf20Sopenharmony_ci		    stream->tid);
18308c2ecf20Sopenharmony_ci	memset(stream, 0, sizeof(*stream));
18318c2ecf20Sopenharmony_ci}
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_cistatic struct mwl8k_ampdu_stream *
18348c2ecf20Sopenharmony_cimwl8k_lookup_stream(struct ieee80211_hw *hw, u8 *addr, u8 tid)
18358c2ecf20Sopenharmony_ci{
18368c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
18378c2ecf20Sopenharmony_ci	int i;
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci	for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) {
18408c2ecf20Sopenharmony_ci		struct mwl8k_ampdu_stream *stream;
18418c2ecf20Sopenharmony_ci		stream = &priv->ampdu[i];
18428c2ecf20Sopenharmony_ci		if (stream->state == AMPDU_NO_STREAM)
18438c2ecf20Sopenharmony_ci			continue;
18448c2ecf20Sopenharmony_ci		if (!memcmp(stream->sta->addr, addr, ETH_ALEN) &&
18458c2ecf20Sopenharmony_ci		    stream->tid == tid)
18468c2ecf20Sopenharmony_ci			return stream;
18478c2ecf20Sopenharmony_ci	}
18488c2ecf20Sopenharmony_ci	return NULL;
18498c2ecf20Sopenharmony_ci}
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci#define MWL8K_AMPDU_PACKET_THRESHOLD 64
18528c2ecf20Sopenharmony_cistatic inline bool mwl8k_ampdu_allowed(struct ieee80211_sta *sta, u8 tid)
18538c2ecf20Sopenharmony_ci{
18548c2ecf20Sopenharmony_ci	struct mwl8k_sta *sta_info = MWL8K_STA(sta);
18558c2ecf20Sopenharmony_ci	struct tx_traffic_info *tx_stats;
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ci	BUG_ON(tid >= MWL8K_MAX_TID);
18588c2ecf20Sopenharmony_ci	tx_stats = &sta_info->tx_stats[tid];
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ci	return sta_info->is_ampdu_allowed &&
18618c2ecf20Sopenharmony_ci		tx_stats->pkts > MWL8K_AMPDU_PACKET_THRESHOLD;
18628c2ecf20Sopenharmony_ci}
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_cistatic inline void mwl8k_tx_count_packet(struct ieee80211_sta *sta, u8 tid)
18658c2ecf20Sopenharmony_ci{
18668c2ecf20Sopenharmony_ci	struct mwl8k_sta *sta_info = MWL8K_STA(sta);
18678c2ecf20Sopenharmony_ci	struct tx_traffic_info *tx_stats;
18688c2ecf20Sopenharmony_ci
18698c2ecf20Sopenharmony_ci	BUG_ON(tid >= MWL8K_MAX_TID);
18708c2ecf20Sopenharmony_ci	tx_stats = &sta_info->tx_stats[tid];
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci	if (tx_stats->start_time == 0)
18738c2ecf20Sopenharmony_ci		tx_stats->start_time = jiffies;
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci	/* reset the packet count after each second elapses.  If the number of
18768c2ecf20Sopenharmony_ci	 * packets ever exceeds the ampdu_min_traffic threshold, we will allow
18778c2ecf20Sopenharmony_ci	 * an ampdu stream to be started.
18788c2ecf20Sopenharmony_ci	 */
18798c2ecf20Sopenharmony_ci	if (jiffies - tx_stats->start_time > HZ) {
18808c2ecf20Sopenharmony_ci		tx_stats->pkts = 0;
18818c2ecf20Sopenharmony_ci		tx_stats->start_time = 0;
18828c2ecf20Sopenharmony_ci	} else
18838c2ecf20Sopenharmony_ci		tx_stats->pkts++;
18848c2ecf20Sopenharmony_ci}
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci/* The hardware ampdu queues start from 5.
18878c2ecf20Sopenharmony_ci * txpriorities for ampdu queues are
18888c2ecf20Sopenharmony_ci * 5 6 7 0 1 2 3 4 ie., queue 5 is highest
18898c2ecf20Sopenharmony_ci * and queue 3 is lowest (queue 4 is reserved)
18908c2ecf20Sopenharmony_ci */
18918c2ecf20Sopenharmony_ci#define BA_QUEUE		5
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_cistatic void
18948c2ecf20Sopenharmony_cimwl8k_txq_xmit(struct ieee80211_hw *hw,
18958c2ecf20Sopenharmony_ci	       int index,
18968c2ecf20Sopenharmony_ci	       struct ieee80211_sta *sta,
18978c2ecf20Sopenharmony_ci	       struct sk_buff *skb)
18988c2ecf20Sopenharmony_ci{
18998c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
19008c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *tx_info;
19018c2ecf20Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif;
19028c2ecf20Sopenharmony_ci	struct ieee80211_hdr *wh;
19038c2ecf20Sopenharmony_ci	struct mwl8k_tx_queue *txq;
19048c2ecf20Sopenharmony_ci	struct mwl8k_tx_desc *tx;
19058c2ecf20Sopenharmony_ci	dma_addr_t dma;
19068c2ecf20Sopenharmony_ci	u32 txstatus;
19078c2ecf20Sopenharmony_ci	u8 txdatarate;
19088c2ecf20Sopenharmony_ci	u16 qos;
19098c2ecf20Sopenharmony_ci	int txpriority;
19108c2ecf20Sopenharmony_ci	u8 tid = 0;
19118c2ecf20Sopenharmony_ci	struct mwl8k_ampdu_stream *stream = NULL;
19128c2ecf20Sopenharmony_ci	bool start_ba_session = false;
19138c2ecf20Sopenharmony_ci	bool mgmtframe = false;
19148c2ecf20Sopenharmony_ci	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
19158c2ecf20Sopenharmony_ci	bool eapol_frame = false;
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci	wh = (struct ieee80211_hdr *)skb->data;
19188c2ecf20Sopenharmony_ci	if (ieee80211_is_data_qos(wh->frame_control))
19198c2ecf20Sopenharmony_ci		qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh)));
19208c2ecf20Sopenharmony_ci	else
19218c2ecf20Sopenharmony_ci		qos = 0;
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_ci	if (skb->protocol == cpu_to_be16(ETH_P_PAE))
19248c2ecf20Sopenharmony_ci		eapol_frame = true;
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci	if (ieee80211_is_mgmt(wh->frame_control))
19278c2ecf20Sopenharmony_ci		mgmtframe = true;
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_ci	if (priv->ap_fw)
19308c2ecf20Sopenharmony_ci		mwl8k_encapsulate_tx_frame(priv, skb);
19318c2ecf20Sopenharmony_ci	else
19328c2ecf20Sopenharmony_ci		mwl8k_add_dma_header(priv, skb, 0, 0);
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci	wh = &((struct mwl8k_dma_data *)skb->data)->wh;
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci	tx_info = IEEE80211_SKB_CB(skb);
19378c2ecf20Sopenharmony_ci	mwl8k_vif = MWL8K_VIF(tx_info->control.vif);
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ci	if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
19408c2ecf20Sopenharmony_ci		wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
19418c2ecf20Sopenharmony_ci		wh->seq_ctrl |= cpu_to_le16(mwl8k_vif->seqno);
19428c2ecf20Sopenharmony_ci		mwl8k_vif->seqno += 0x10;
19438c2ecf20Sopenharmony_ci	}
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci	/* Setup firmware control bit fields for each frame type.  */
19468c2ecf20Sopenharmony_ci	txstatus = 0;
19478c2ecf20Sopenharmony_ci	txdatarate = 0;
19488c2ecf20Sopenharmony_ci	if (ieee80211_is_mgmt(wh->frame_control) ||
19498c2ecf20Sopenharmony_ci	    ieee80211_is_ctl(wh->frame_control)) {
19508c2ecf20Sopenharmony_ci		txdatarate = 0;
19518c2ecf20Sopenharmony_ci		qos |= MWL8K_QOS_QLEN_UNSPEC | MWL8K_QOS_EOSP;
19528c2ecf20Sopenharmony_ci	} else if (ieee80211_is_data(wh->frame_control)) {
19538c2ecf20Sopenharmony_ci		txdatarate = 1;
19548c2ecf20Sopenharmony_ci		if (is_multicast_ether_addr(wh->addr1))
19558c2ecf20Sopenharmony_ci			txstatus |= MWL8K_TXD_STATUS_MULTICAST_TX;
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci		qos &= ~MWL8K_QOS_ACK_POLICY_MASK;
19588c2ecf20Sopenharmony_ci		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
19598c2ecf20Sopenharmony_ci			qos |= MWL8K_QOS_ACK_POLICY_BLOCKACK;
19608c2ecf20Sopenharmony_ci		else
19618c2ecf20Sopenharmony_ci			qos |= MWL8K_QOS_ACK_POLICY_NORMAL;
19628c2ecf20Sopenharmony_ci	}
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci	/* Queue ADDBA request in the respective data queue.  While setting up
19658c2ecf20Sopenharmony_ci	 * the ampdu stream, mac80211 queues further packets for that
19668c2ecf20Sopenharmony_ci	 * particular ra/tid pair.  However, packets piled up in the hardware
19678c2ecf20Sopenharmony_ci	 * for that ra/tid pair will still go out. ADDBA request and the
19688c2ecf20Sopenharmony_ci	 * related data packets going out from different queues asynchronously
19698c2ecf20Sopenharmony_ci	 * will cause a shift in the receiver window which might result in
19708c2ecf20Sopenharmony_ci	 * ampdu packets getting dropped at the receiver after the stream has
19718c2ecf20Sopenharmony_ci	 * been setup.
19728c2ecf20Sopenharmony_ci	 */
19738c2ecf20Sopenharmony_ci	if (unlikely(ieee80211_is_action(wh->frame_control) &&
19748c2ecf20Sopenharmony_ci	    mgmt->u.action.category == WLAN_CATEGORY_BACK &&
19758c2ecf20Sopenharmony_ci	    mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ &&
19768c2ecf20Sopenharmony_ci	    priv->ap_fw)) {
19778c2ecf20Sopenharmony_ci		u16 capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
19788c2ecf20Sopenharmony_ci		tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
19798c2ecf20Sopenharmony_ci		index = mwl8k_tid_queue_mapping(tid);
19808c2ecf20Sopenharmony_ci	}
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci	txpriority = index;
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_ci	if (priv->ap_fw && sta && sta->ht_cap.ht_supported && !eapol_frame &&
19858c2ecf20Sopenharmony_ci	    ieee80211_is_data_qos(wh->frame_control)) {
19868c2ecf20Sopenharmony_ci		tid = qos & 0xf;
19878c2ecf20Sopenharmony_ci		mwl8k_tx_count_packet(sta, tid);
19888c2ecf20Sopenharmony_ci		spin_lock(&priv->stream_lock);
19898c2ecf20Sopenharmony_ci		stream = mwl8k_lookup_stream(hw, sta->addr, tid);
19908c2ecf20Sopenharmony_ci		if (stream != NULL) {
19918c2ecf20Sopenharmony_ci			if (stream->state == AMPDU_STREAM_ACTIVE) {
19928c2ecf20Sopenharmony_ci				WARN_ON(!(qos & MWL8K_QOS_ACK_POLICY_BLOCKACK));
19938c2ecf20Sopenharmony_ci				txpriority = (BA_QUEUE + stream->idx) %
19948c2ecf20Sopenharmony_ci					     TOTAL_HW_TX_QUEUES;
19958c2ecf20Sopenharmony_ci				if (stream->idx <= 1)
19968c2ecf20Sopenharmony_ci					index = stream->idx +
19978c2ecf20Sopenharmony_ci						MWL8K_TX_WMM_QUEUES;
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci			} else if (stream->state == AMPDU_STREAM_NEW) {
20008c2ecf20Sopenharmony_ci				/* We get here if the driver sends us packets
20018c2ecf20Sopenharmony_ci				 * after we've initiated a stream, but before
20028c2ecf20Sopenharmony_ci				 * our ampdu_action routine has been called
20038c2ecf20Sopenharmony_ci				 * with IEEE80211_AMPDU_TX_START to get the SSN
20048c2ecf20Sopenharmony_ci				 * for the ADDBA request.  So this packet can
20058c2ecf20Sopenharmony_ci				 * go out with no risk of sequence number
20068c2ecf20Sopenharmony_ci				 * mismatch.  No special handling is required.
20078c2ecf20Sopenharmony_ci				 */
20088c2ecf20Sopenharmony_ci			} else {
20098c2ecf20Sopenharmony_ci				/* Drop packets that would go out after the
20108c2ecf20Sopenharmony_ci				 * ADDBA request was sent but before the ADDBA
20118c2ecf20Sopenharmony_ci				 * response is received.  If we don't do this,
20128c2ecf20Sopenharmony_ci				 * the recipient would probably receive it
20138c2ecf20Sopenharmony_ci				 * after the ADDBA request with SSN 0.  This
20148c2ecf20Sopenharmony_ci				 * will cause the recipient's BA receive window
20158c2ecf20Sopenharmony_ci				 * to shift, which would cause the subsequent
20168c2ecf20Sopenharmony_ci				 * packets in the BA stream to be discarded.
20178c2ecf20Sopenharmony_ci				 * mac80211 queues our packets for us in this
20188c2ecf20Sopenharmony_ci				 * case, so this is really just a safety check.
20198c2ecf20Sopenharmony_ci				 */
20208c2ecf20Sopenharmony_ci				wiphy_warn(hw->wiphy,
20218c2ecf20Sopenharmony_ci					   "Cannot send packet while ADDBA "
20228c2ecf20Sopenharmony_ci					   "dialog is underway.\n");
20238c2ecf20Sopenharmony_ci				spin_unlock(&priv->stream_lock);
20248c2ecf20Sopenharmony_ci				dev_kfree_skb(skb);
20258c2ecf20Sopenharmony_ci				return;
20268c2ecf20Sopenharmony_ci			}
20278c2ecf20Sopenharmony_ci		} else {
20288c2ecf20Sopenharmony_ci			/* Defer calling mwl8k_start_stream so that the current
20298c2ecf20Sopenharmony_ci			 * skb can go out before the ADDBA request.  This
20308c2ecf20Sopenharmony_ci			 * prevents sequence number mismatch at the recepient
20318c2ecf20Sopenharmony_ci			 * as described above.
20328c2ecf20Sopenharmony_ci			 */
20338c2ecf20Sopenharmony_ci			if (mwl8k_ampdu_allowed(sta, tid)) {
20348c2ecf20Sopenharmony_ci				stream = mwl8k_add_stream(hw, sta, tid);
20358c2ecf20Sopenharmony_ci				if (stream != NULL)
20368c2ecf20Sopenharmony_ci					start_ba_session = true;
20378c2ecf20Sopenharmony_ci			}
20388c2ecf20Sopenharmony_ci		}
20398c2ecf20Sopenharmony_ci		spin_unlock(&priv->stream_lock);
20408c2ecf20Sopenharmony_ci	} else {
20418c2ecf20Sopenharmony_ci		qos &= ~MWL8K_QOS_ACK_POLICY_MASK;
20428c2ecf20Sopenharmony_ci		qos |= MWL8K_QOS_ACK_POLICY_NORMAL;
20438c2ecf20Sopenharmony_ci	}
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ci	dma = pci_map_single(priv->pdev, skb->data,
20468c2ecf20Sopenharmony_ci				skb->len, PCI_DMA_TODEVICE);
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_ci	if (pci_dma_mapping_error(priv->pdev, dma)) {
20498c2ecf20Sopenharmony_ci		wiphy_debug(hw->wiphy,
20508c2ecf20Sopenharmony_ci			    "failed to dma map skb, dropping TX frame.\n");
20518c2ecf20Sopenharmony_ci		if (start_ba_session) {
20528c2ecf20Sopenharmony_ci			spin_lock(&priv->stream_lock);
20538c2ecf20Sopenharmony_ci			mwl8k_remove_stream(hw, stream);
20548c2ecf20Sopenharmony_ci			spin_unlock(&priv->stream_lock);
20558c2ecf20Sopenharmony_ci		}
20568c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
20578c2ecf20Sopenharmony_ci		return;
20588c2ecf20Sopenharmony_ci	}
20598c2ecf20Sopenharmony_ci
20608c2ecf20Sopenharmony_ci	spin_lock_bh(&priv->tx_lock);
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci	txq = priv->txq + index;
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_ci	/* Mgmt frames that go out frequently are probe
20658c2ecf20Sopenharmony_ci	 * responses. Other mgmt frames got out relatively
20668c2ecf20Sopenharmony_ci	 * infrequently. Hence reserve 2 buffers so that
20678c2ecf20Sopenharmony_ci	 * other mgmt frames do not get dropped due to an
20688c2ecf20Sopenharmony_ci	 * already queued probe response in one of the
20698c2ecf20Sopenharmony_ci	 * reserved buffers.
20708c2ecf20Sopenharmony_ci	 */
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	if (txq->len >= MWL8K_TX_DESCS - 2) {
20738c2ecf20Sopenharmony_ci		if (!mgmtframe || txq->len == MWL8K_TX_DESCS) {
20748c2ecf20Sopenharmony_ci			if (start_ba_session) {
20758c2ecf20Sopenharmony_ci				spin_lock(&priv->stream_lock);
20768c2ecf20Sopenharmony_ci				mwl8k_remove_stream(hw, stream);
20778c2ecf20Sopenharmony_ci				spin_unlock(&priv->stream_lock);
20788c2ecf20Sopenharmony_ci			}
20798c2ecf20Sopenharmony_ci			mwl8k_tx_start(priv);
20808c2ecf20Sopenharmony_ci			spin_unlock_bh(&priv->tx_lock);
20818c2ecf20Sopenharmony_ci			pci_unmap_single(priv->pdev, dma, skb->len,
20828c2ecf20Sopenharmony_ci					 PCI_DMA_TODEVICE);
20838c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
20848c2ecf20Sopenharmony_ci			return;
20858c2ecf20Sopenharmony_ci		}
20868c2ecf20Sopenharmony_ci	}
20878c2ecf20Sopenharmony_ci
20888c2ecf20Sopenharmony_ci	BUG_ON(txq->skb[txq->tail] != NULL);
20898c2ecf20Sopenharmony_ci	txq->skb[txq->tail] = skb;
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci	tx = txq->txd + txq->tail;
20928c2ecf20Sopenharmony_ci	tx->data_rate = txdatarate;
20938c2ecf20Sopenharmony_ci	tx->tx_priority = txpriority;
20948c2ecf20Sopenharmony_ci	tx->qos_control = cpu_to_le16(qos);
20958c2ecf20Sopenharmony_ci	tx->pkt_phys_addr = cpu_to_le32(dma);
20968c2ecf20Sopenharmony_ci	tx->pkt_len = cpu_to_le16(skb->len);
20978c2ecf20Sopenharmony_ci	tx->rate_info = 0;
20988c2ecf20Sopenharmony_ci	if (!priv->ap_fw && sta != NULL)
20998c2ecf20Sopenharmony_ci		tx->peer_id = MWL8K_STA(sta)->peer_id;
21008c2ecf20Sopenharmony_ci	else
21018c2ecf20Sopenharmony_ci		tx->peer_id = 0;
21028c2ecf20Sopenharmony_ci
21038c2ecf20Sopenharmony_ci	if (priv->ap_fw && ieee80211_is_data(wh->frame_control) && !eapol_frame)
21048c2ecf20Sopenharmony_ci		tx->timestamp = cpu_to_le32(ioread32(priv->regs +
21058c2ecf20Sopenharmony_ci						MWL8K_HW_TIMER_REGISTER));
21068c2ecf20Sopenharmony_ci	else
21078c2ecf20Sopenharmony_ci		tx->timestamp = 0;
21088c2ecf20Sopenharmony_ci
21098c2ecf20Sopenharmony_ci	wmb();
21108c2ecf20Sopenharmony_ci	tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus);
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_ci	txq->len++;
21138c2ecf20Sopenharmony_ci	priv->pending_tx_pkts++;
21148c2ecf20Sopenharmony_ci
21158c2ecf20Sopenharmony_ci	txq->tail++;
21168c2ecf20Sopenharmony_ci	if (txq->tail == MWL8K_TX_DESCS)
21178c2ecf20Sopenharmony_ci		txq->tail = 0;
21188c2ecf20Sopenharmony_ci
21198c2ecf20Sopenharmony_ci	mwl8k_tx_start(priv);
21208c2ecf20Sopenharmony_ci
21218c2ecf20Sopenharmony_ci	spin_unlock_bh(&priv->tx_lock);
21228c2ecf20Sopenharmony_ci
21238c2ecf20Sopenharmony_ci	/* Initiate the ampdu session here */
21248c2ecf20Sopenharmony_ci	if (start_ba_session) {
21258c2ecf20Sopenharmony_ci		spin_lock(&priv->stream_lock);
21268c2ecf20Sopenharmony_ci		if (mwl8k_start_stream(hw, stream))
21278c2ecf20Sopenharmony_ci			mwl8k_remove_stream(hw, stream);
21288c2ecf20Sopenharmony_ci		spin_unlock(&priv->stream_lock);
21298c2ecf20Sopenharmony_ci	}
21308c2ecf20Sopenharmony_ci}
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_ci/*
21348c2ecf20Sopenharmony_ci * Firmware access.
21358c2ecf20Sopenharmony_ci *
21368c2ecf20Sopenharmony_ci * We have the following requirements for issuing firmware commands:
21378c2ecf20Sopenharmony_ci * - Some commands require that the packet transmit path is idle when
21388c2ecf20Sopenharmony_ci *   the command is issued.  (For simplicity, we'll just quiesce the
21398c2ecf20Sopenharmony_ci *   transmit path for every command.)
21408c2ecf20Sopenharmony_ci * - There are certain sequences of commands that need to be issued to
21418c2ecf20Sopenharmony_ci *   the hardware sequentially, with no other intervening commands.
21428c2ecf20Sopenharmony_ci *
21438c2ecf20Sopenharmony_ci * This leads to an implementation of a "firmware lock" as a mutex that
21448c2ecf20Sopenharmony_ci * can be taken recursively, and which is taken by both the low-level
21458c2ecf20Sopenharmony_ci * command submission function (mwl8k_post_cmd) as well as any users of
21468c2ecf20Sopenharmony_ci * that function that require issuing of an atomic sequence of commands,
21478c2ecf20Sopenharmony_ci * and quiesces the transmit path whenever it's taken.
21488c2ecf20Sopenharmony_ci */
21498c2ecf20Sopenharmony_cistatic int mwl8k_fw_lock(struct ieee80211_hw *hw)
21508c2ecf20Sopenharmony_ci{
21518c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ci	if (priv->fw_mutex_owner != current) {
21548c2ecf20Sopenharmony_ci		int rc;
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci		mutex_lock(&priv->fw_mutex);
21578c2ecf20Sopenharmony_ci		ieee80211_stop_queues(hw);
21588c2ecf20Sopenharmony_ci
21598c2ecf20Sopenharmony_ci		rc = mwl8k_tx_wait_empty(hw);
21608c2ecf20Sopenharmony_ci		if (rc) {
21618c2ecf20Sopenharmony_ci			if (!priv->hw_restart_in_progress)
21628c2ecf20Sopenharmony_ci				ieee80211_wake_queues(hw);
21638c2ecf20Sopenharmony_ci
21648c2ecf20Sopenharmony_ci			mutex_unlock(&priv->fw_mutex);
21658c2ecf20Sopenharmony_ci
21668c2ecf20Sopenharmony_ci			return rc;
21678c2ecf20Sopenharmony_ci		}
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_ci		priv->fw_mutex_owner = current;
21708c2ecf20Sopenharmony_ci	}
21718c2ecf20Sopenharmony_ci
21728c2ecf20Sopenharmony_ci	priv->fw_mutex_depth++;
21738c2ecf20Sopenharmony_ci
21748c2ecf20Sopenharmony_ci	return 0;
21758c2ecf20Sopenharmony_ci}
21768c2ecf20Sopenharmony_ci
21778c2ecf20Sopenharmony_cistatic void mwl8k_fw_unlock(struct ieee80211_hw *hw)
21788c2ecf20Sopenharmony_ci{
21798c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
21808c2ecf20Sopenharmony_ci
21818c2ecf20Sopenharmony_ci	if (!--priv->fw_mutex_depth) {
21828c2ecf20Sopenharmony_ci		if (!priv->hw_restart_in_progress)
21838c2ecf20Sopenharmony_ci			ieee80211_wake_queues(hw);
21848c2ecf20Sopenharmony_ci
21858c2ecf20Sopenharmony_ci		priv->fw_mutex_owner = NULL;
21868c2ecf20Sopenharmony_ci		mutex_unlock(&priv->fw_mutex);
21878c2ecf20Sopenharmony_ci	}
21888c2ecf20Sopenharmony_ci}
21898c2ecf20Sopenharmony_ci
21908c2ecf20Sopenharmony_cistatic void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable,
21918c2ecf20Sopenharmony_ci			       u32 bitmap);
21928c2ecf20Sopenharmony_ci
21938c2ecf20Sopenharmony_ci/*
21948c2ecf20Sopenharmony_ci * Command processing.
21958c2ecf20Sopenharmony_ci */
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci/* Timeout firmware commands after 10s */
21988c2ecf20Sopenharmony_ci#define MWL8K_CMD_TIMEOUT_MS	10000
21998c2ecf20Sopenharmony_ci
22008c2ecf20Sopenharmony_cistatic int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
22018c2ecf20Sopenharmony_ci{
22028c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(cmd_wait);
22038c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
22048c2ecf20Sopenharmony_ci	void __iomem *regs = priv->regs;
22058c2ecf20Sopenharmony_ci	dma_addr_t dma_addr;
22068c2ecf20Sopenharmony_ci	unsigned int dma_size;
22078c2ecf20Sopenharmony_ci	int rc;
22088c2ecf20Sopenharmony_ci	unsigned long timeout = 0;
22098c2ecf20Sopenharmony_ci	u8 buf[32];
22108c2ecf20Sopenharmony_ci	u32 bitmap = 0;
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	wiphy_dbg(hw->wiphy, "Posting %s [%d]\n",
22138c2ecf20Sopenharmony_ci		  mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), cmd->macid);
22148c2ecf20Sopenharmony_ci
22158c2ecf20Sopenharmony_ci	/* Before posting firmware commands that could change the hardware
22168c2ecf20Sopenharmony_ci	 * characteristics, make sure that all BSSes are stopped temporary.
22178c2ecf20Sopenharmony_ci	 * Enable these stopped BSSes after completion of the commands
22188c2ecf20Sopenharmony_ci	 */
22198c2ecf20Sopenharmony_ci
22208c2ecf20Sopenharmony_ci	rc = mwl8k_fw_lock(hw);
22218c2ecf20Sopenharmony_ci	if (rc)
22228c2ecf20Sopenharmony_ci		return rc;
22238c2ecf20Sopenharmony_ci
22248c2ecf20Sopenharmony_ci	if (priv->ap_fw && priv->running_bsses) {
22258c2ecf20Sopenharmony_ci		switch (le16_to_cpu(cmd->code)) {
22268c2ecf20Sopenharmony_ci		case MWL8K_CMD_SET_RF_CHANNEL:
22278c2ecf20Sopenharmony_ci		case MWL8K_CMD_RADIO_CONTROL:
22288c2ecf20Sopenharmony_ci		case MWL8K_CMD_RF_TX_POWER:
22298c2ecf20Sopenharmony_ci		case MWL8K_CMD_TX_POWER:
22308c2ecf20Sopenharmony_ci		case MWL8K_CMD_RF_ANTENNA:
22318c2ecf20Sopenharmony_ci		case MWL8K_CMD_RTS_THRESHOLD:
22328c2ecf20Sopenharmony_ci		case MWL8K_CMD_MIMO_CONFIG:
22338c2ecf20Sopenharmony_ci			bitmap = priv->running_bsses;
22348c2ecf20Sopenharmony_ci			mwl8k_enable_bsses(hw, false, bitmap);
22358c2ecf20Sopenharmony_ci			break;
22368c2ecf20Sopenharmony_ci		}
22378c2ecf20Sopenharmony_ci	}
22388c2ecf20Sopenharmony_ci
22398c2ecf20Sopenharmony_ci	cmd->result = (__force __le16) 0xffff;
22408c2ecf20Sopenharmony_ci	dma_size = le16_to_cpu(cmd->length);
22418c2ecf20Sopenharmony_ci	dma_addr = pci_map_single(priv->pdev, cmd, dma_size,
22428c2ecf20Sopenharmony_ci				  PCI_DMA_BIDIRECTIONAL);
22438c2ecf20Sopenharmony_ci	if (pci_dma_mapping_error(priv->pdev, dma_addr)) {
22448c2ecf20Sopenharmony_ci		rc = -ENOMEM;
22458c2ecf20Sopenharmony_ci		goto exit;
22468c2ecf20Sopenharmony_ci	}
22478c2ecf20Sopenharmony_ci
22488c2ecf20Sopenharmony_ci	priv->hostcmd_wait = &cmd_wait;
22498c2ecf20Sopenharmony_ci	iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR);
22508c2ecf20Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_DOORBELL,
22518c2ecf20Sopenharmony_ci		regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
22528c2ecf20Sopenharmony_ci	iowrite32(MWL8K_H2A_INT_DUMMY,
22538c2ecf20Sopenharmony_ci		regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS);
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_ci	timeout = wait_for_completion_timeout(&cmd_wait,
22568c2ecf20Sopenharmony_ci				msecs_to_jiffies(MWL8K_CMD_TIMEOUT_MS));
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci	priv->hostcmd_wait = NULL;
22598c2ecf20Sopenharmony_ci
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_ci	pci_unmap_single(priv->pdev, dma_addr, dma_size,
22628c2ecf20Sopenharmony_ci					PCI_DMA_BIDIRECTIONAL);
22638c2ecf20Sopenharmony_ci
22648c2ecf20Sopenharmony_ci	if (!timeout) {
22658c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy, "Command %s timeout after %u ms\n",
22668c2ecf20Sopenharmony_ci			  mwl8k_cmd_name(cmd->code, buf, sizeof(buf)),
22678c2ecf20Sopenharmony_ci			  MWL8K_CMD_TIMEOUT_MS);
22688c2ecf20Sopenharmony_ci		rc = -ETIMEDOUT;
22698c2ecf20Sopenharmony_ci	} else {
22708c2ecf20Sopenharmony_ci		int ms;
22718c2ecf20Sopenharmony_ci
22728c2ecf20Sopenharmony_ci		ms = MWL8K_CMD_TIMEOUT_MS - jiffies_to_msecs(timeout);
22738c2ecf20Sopenharmony_ci
22748c2ecf20Sopenharmony_ci		rc = cmd->result ? -EINVAL : 0;
22758c2ecf20Sopenharmony_ci		if (rc)
22768c2ecf20Sopenharmony_ci			wiphy_err(hw->wiphy, "Command %s error 0x%x\n",
22778c2ecf20Sopenharmony_ci				  mwl8k_cmd_name(cmd->code, buf, sizeof(buf)),
22788c2ecf20Sopenharmony_ci				  le16_to_cpu(cmd->result));
22798c2ecf20Sopenharmony_ci		else if (ms > 2000)
22808c2ecf20Sopenharmony_ci			wiphy_notice(hw->wiphy, "Command %s took %d ms\n",
22818c2ecf20Sopenharmony_ci				     mwl8k_cmd_name(cmd->code,
22828c2ecf20Sopenharmony_ci						    buf, sizeof(buf)),
22838c2ecf20Sopenharmony_ci				     ms);
22848c2ecf20Sopenharmony_ci	}
22858c2ecf20Sopenharmony_ci
22868c2ecf20Sopenharmony_ciexit:
22878c2ecf20Sopenharmony_ci	if (bitmap)
22888c2ecf20Sopenharmony_ci		mwl8k_enable_bsses(hw, true, bitmap);
22898c2ecf20Sopenharmony_ci
22908c2ecf20Sopenharmony_ci	mwl8k_fw_unlock(hw);
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci	return rc;
22938c2ecf20Sopenharmony_ci}
22948c2ecf20Sopenharmony_ci
22958c2ecf20Sopenharmony_cistatic int mwl8k_post_pervif_cmd(struct ieee80211_hw *hw,
22968c2ecf20Sopenharmony_ci				 struct ieee80211_vif *vif,
22978c2ecf20Sopenharmony_ci				 struct mwl8k_cmd_pkt *cmd)
22988c2ecf20Sopenharmony_ci{
22998c2ecf20Sopenharmony_ci	if (vif != NULL)
23008c2ecf20Sopenharmony_ci		cmd->macid = MWL8K_VIF(vif)->macid;
23018c2ecf20Sopenharmony_ci	return mwl8k_post_cmd(hw, cmd);
23028c2ecf20Sopenharmony_ci}
23038c2ecf20Sopenharmony_ci
23048c2ecf20Sopenharmony_ci/*
23058c2ecf20Sopenharmony_ci * Setup code shared between STA and AP firmware images.
23068c2ecf20Sopenharmony_ci */
23078c2ecf20Sopenharmony_cistatic void mwl8k_setup_2ghz_band(struct ieee80211_hw *hw)
23088c2ecf20Sopenharmony_ci{
23098c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
23108c2ecf20Sopenharmony_ci
23118c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(priv->channels_24) != sizeof(mwl8k_channels_24));
23128c2ecf20Sopenharmony_ci	memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24));
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24));
23158c2ecf20Sopenharmony_ci	memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24));
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci	priv->band_24.band = NL80211_BAND_2GHZ;
23188c2ecf20Sopenharmony_ci	priv->band_24.channels = priv->channels_24;
23198c2ecf20Sopenharmony_ci	priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24);
23208c2ecf20Sopenharmony_ci	priv->band_24.bitrates = priv->rates_24;
23218c2ecf20Sopenharmony_ci	priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24);
23228c2ecf20Sopenharmony_ci
23238c2ecf20Sopenharmony_ci	hw->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band_24;
23248c2ecf20Sopenharmony_ci}
23258c2ecf20Sopenharmony_ci
23268c2ecf20Sopenharmony_cistatic void mwl8k_setup_5ghz_band(struct ieee80211_hw *hw)
23278c2ecf20Sopenharmony_ci{
23288c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
23298c2ecf20Sopenharmony_ci
23308c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(priv->channels_50) != sizeof(mwl8k_channels_50));
23318c2ecf20Sopenharmony_ci	memcpy(priv->channels_50, mwl8k_channels_50, sizeof(mwl8k_channels_50));
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl8k_rates_50));
23348c2ecf20Sopenharmony_ci	memcpy(priv->rates_50, mwl8k_rates_50, sizeof(mwl8k_rates_50));
23358c2ecf20Sopenharmony_ci
23368c2ecf20Sopenharmony_ci	priv->band_50.band = NL80211_BAND_5GHZ;
23378c2ecf20Sopenharmony_ci	priv->band_50.channels = priv->channels_50;
23388c2ecf20Sopenharmony_ci	priv->band_50.n_channels = ARRAY_SIZE(mwl8k_channels_50);
23398c2ecf20Sopenharmony_ci	priv->band_50.bitrates = priv->rates_50;
23408c2ecf20Sopenharmony_ci	priv->band_50.n_bitrates = ARRAY_SIZE(mwl8k_rates_50);
23418c2ecf20Sopenharmony_ci
23428c2ecf20Sopenharmony_ci	hw->wiphy->bands[NL80211_BAND_5GHZ] = &priv->band_50;
23438c2ecf20Sopenharmony_ci}
23448c2ecf20Sopenharmony_ci
23458c2ecf20Sopenharmony_ci/*
23468c2ecf20Sopenharmony_ci * CMD_GET_HW_SPEC (STA version).
23478c2ecf20Sopenharmony_ci */
23488c2ecf20Sopenharmony_cistruct mwl8k_cmd_get_hw_spec_sta {
23498c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
23508c2ecf20Sopenharmony_ci	__u8 hw_rev;
23518c2ecf20Sopenharmony_ci	__u8 host_interface;
23528c2ecf20Sopenharmony_ci	__le16 num_mcaddrs;
23538c2ecf20Sopenharmony_ci	__u8 perm_addr[ETH_ALEN];
23548c2ecf20Sopenharmony_ci	__le16 region_code;
23558c2ecf20Sopenharmony_ci	__le32 fw_rev;
23568c2ecf20Sopenharmony_ci	__le32 ps_cookie;
23578c2ecf20Sopenharmony_ci	__le32 caps;
23588c2ecf20Sopenharmony_ci	__u8 mcs_bitmap[16];
23598c2ecf20Sopenharmony_ci	__le32 rx_queue_ptr;
23608c2ecf20Sopenharmony_ci	__le32 num_tx_queues;
23618c2ecf20Sopenharmony_ci	__le32 tx_queue_ptrs[MWL8K_TX_WMM_QUEUES];
23628c2ecf20Sopenharmony_ci	__le32 caps2;
23638c2ecf20Sopenharmony_ci	__le32 num_tx_desc_per_queue;
23648c2ecf20Sopenharmony_ci	__le32 total_rxd;
23658c2ecf20Sopenharmony_ci} __packed;
23668c2ecf20Sopenharmony_ci
23678c2ecf20Sopenharmony_ci#define MWL8K_CAP_MAX_AMSDU		0x20000000
23688c2ecf20Sopenharmony_ci#define MWL8K_CAP_GREENFIELD		0x08000000
23698c2ecf20Sopenharmony_ci#define MWL8K_CAP_AMPDU			0x04000000
23708c2ecf20Sopenharmony_ci#define MWL8K_CAP_RX_STBC		0x01000000
23718c2ecf20Sopenharmony_ci#define MWL8K_CAP_TX_STBC		0x00800000
23728c2ecf20Sopenharmony_ci#define MWL8K_CAP_SHORTGI_40MHZ		0x00400000
23738c2ecf20Sopenharmony_ci#define MWL8K_CAP_SHORTGI_20MHZ		0x00200000
23748c2ecf20Sopenharmony_ci#define MWL8K_CAP_RX_ANTENNA_MASK	0x000e0000
23758c2ecf20Sopenharmony_ci#define MWL8K_CAP_TX_ANTENNA_MASK	0x0001c000
23768c2ecf20Sopenharmony_ci#define MWL8K_CAP_DELAY_BA		0x00003000
23778c2ecf20Sopenharmony_ci#define MWL8K_CAP_MIMO			0x00000200
23788c2ecf20Sopenharmony_ci#define MWL8K_CAP_40MHZ			0x00000100
23798c2ecf20Sopenharmony_ci#define MWL8K_CAP_BAND_MASK		0x00000007
23808c2ecf20Sopenharmony_ci#define MWL8K_CAP_5GHZ			0x00000004
23818c2ecf20Sopenharmony_ci#define MWL8K_CAP_2GHZ4			0x00000001
23828c2ecf20Sopenharmony_ci
23838c2ecf20Sopenharmony_cistatic void
23848c2ecf20Sopenharmony_cimwl8k_set_ht_caps(struct ieee80211_hw *hw,
23858c2ecf20Sopenharmony_ci		  struct ieee80211_supported_band *band, u32 cap)
23868c2ecf20Sopenharmony_ci{
23878c2ecf20Sopenharmony_ci	int rx_streams;
23888c2ecf20Sopenharmony_ci	int tx_streams;
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_ci	band->ht_cap.ht_supported = 1;
23918c2ecf20Sopenharmony_ci
23928c2ecf20Sopenharmony_ci	if (cap & MWL8K_CAP_MAX_AMSDU)
23938c2ecf20Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
23948c2ecf20Sopenharmony_ci	if (cap & MWL8K_CAP_GREENFIELD)
23958c2ecf20Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD;
23968c2ecf20Sopenharmony_ci	if (cap & MWL8K_CAP_AMPDU) {
23978c2ecf20Sopenharmony_ci		ieee80211_hw_set(hw, AMPDU_AGGREGATION);
23988c2ecf20Sopenharmony_ci		band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
23998c2ecf20Sopenharmony_ci		band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
24008c2ecf20Sopenharmony_ci	}
24018c2ecf20Sopenharmony_ci	if (cap & MWL8K_CAP_RX_STBC)
24028c2ecf20Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC;
24038c2ecf20Sopenharmony_ci	if (cap & MWL8K_CAP_TX_STBC)
24048c2ecf20Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
24058c2ecf20Sopenharmony_ci	if (cap & MWL8K_CAP_SHORTGI_40MHZ)
24068c2ecf20Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
24078c2ecf20Sopenharmony_ci	if (cap & MWL8K_CAP_SHORTGI_20MHZ)
24088c2ecf20Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
24098c2ecf20Sopenharmony_ci	if (cap & MWL8K_CAP_DELAY_BA)
24108c2ecf20Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA;
24118c2ecf20Sopenharmony_ci	if (cap & MWL8K_CAP_40MHZ)
24128c2ecf20Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
24138c2ecf20Sopenharmony_ci
24148c2ecf20Sopenharmony_ci	rx_streams = hweight32(cap & MWL8K_CAP_RX_ANTENNA_MASK);
24158c2ecf20Sopenharmony_ci	tx_streams = hweight32(cap & MWL8K_CAP_TX_ANTENNA_MASK);
24168c2ecf20Sopenharmony_ci
24178c2ecf20Sopenharmony_ci	band->ht_cap.mcs.rx_mask[0] = 0xff;
24188c2ecf20Sopenharmony_ci	if (rx_streams >= 2)
24198c2ecf20Sopenharmony_ci		band->ht_cap.mcs.rx_mask[1] = 0xff;
24208c2ecf20Sopenharmony_ci	if (rx_streams >= 3)
24218c2ecf20Sopenharmony_ci		band->ht_cap.mcs.rx_mask[2] = 0xff;
24228c2ecf20Sopenharmony_ci	band->ht_cap.mcs.rx_mask[4] = 0x01;
24238c2ecf20Sopenharmony_ci	band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
24248c2ecf20Sopenharmony_ci
24258c2ecf20Sopenharmony_ci	if (rx_streams != tx_streams) {
24268c2ecf20Sopenharmony_ci		band->ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
24278c2ecf20Sopenharmony_ci		band->ht_cap.mcs.tx_params |= (tx_streams - 1) <<
24288c2ecf20Sopenharmony_ci				IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
24298c2ecf20Sopenharmony_ci	}
24308c2ecf20Sopenharmony_ci}
24318c2ecf20Sopenharmony_ci
24328c2ecf20Sopenharmony_cistatic void
24338c2ecf20Sopenharmony_cimwl8k_set_caps(struct ieee80211_hw *hw, u32 caps)
24348c2ecf20Sopenharmony_ci{
24358c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
24368c2ecf20Sopenharmony_ci
24378c2ecf20Sopenharmony_ci	if (priv->caps)
24388c2ecf20Sopenharmony_ci		return;
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_ci	if ((caps & MWL8K_CAP_2GHZ4) || !(caps & MWL8K_CAP_BAND_MASK)) {
24418c2ecf20Sopenharmony_ci		mwl8k_setup_2ghz_band(hw);
24428c2ecf20Sopenharmony_ci		if (caps & MWL8K_CAP_MIMO)
24438c2ecf20Sopenharmony_ci			mwl8k_set_ht_caps(hw, &priv->band_24, caps);
24448c2ecf20Sopenharmony_ci	}
24458c2ecf20Sopenharmony_ci
24468c2ecf20Sopenharmony_ci	if (caps & MWL8K_CAP_5GHZ) {
24478c2ecf20Sopenharmony_ci		mwl8k_setup_5ghz_band(hw);
24488c2ecf20Sopenharmony_ci		if (caps & MWL8K_CAP_MIMO)
24498c2ecf20Sopenharmony_ci			mwl8k_set_ht_caps(hw, &priv->band_50, caps);
24508c2ecf20Sopenharmony_ci	}
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_ci	priv->caps = caps;
24538c2ecf20Sopenharmony_ci}
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_cistatic int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw)
24568c2ecf20Sopenharmony_ci{
24578c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
24588c2ecf20Sopenharmony_ci	struct mwl8k_cmd_get_hw_spec_sta *cmd;
24598c2ecf20Sopenharmony_ci	int rc;
24608c2ecf20Sopenharmony_ci	int i;
24618c2ecf20Sopenharmony_ci
24628c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
24638c2ecf20Sopenharmony_ci	if (cmd == NULL)
24648c2ecf20Sopenharmony_ci		return -ENOMEM;
24658c2ecf20Sopenharmony_ci
24668c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC);
24678c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
24688c2ecf20Sopenharmony_ci
24698c2ecf20Sopenharmony_ci	memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr));
24708c2ecf20Sopenharmony_ci	cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
24718c2ecf20Sopenharmony_ci	cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma);
24728c2ecf20Sopenharmony_ci	cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv));
24738c2ecf20Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
24748c2ecf20Sopenharmony_ci		cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma);
24758c2ecf20Sopenharmony_ci	cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);
24768c2ecf20Sopenharmony_ci	cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS);
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
24798c2ecf20Sopenharmony_ci
24808c2ecf20Sopenharmony_ci	if (!rc) {
24818c2ecf20Sopenharmony_ci		SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr);
24828c2ecf20Sopenharmony_ci		priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
24838c2ecf20Sopenharmony_ci		priv->fw_rev = le32_to_cpu(cmd->fw_rev);
24848c2ecf20Sopenharmony_ci		priv->hw_rev = cmd->hw_rev;
24858c2ecf20Sopenharmony_ci		mwl8k_set_caps(hw, le32_to_cpu(cmd->caps));
24868c2ecf20Sopenharmony_ci		priv->ap_macids_supported = 0x00000000;
24878c2ecf20Sopenharmony_ci		priv->sta_macids_supported = 0x00000001;
24888c2ecf20Sopenharmony_ci	}
24898c2ecf20Sopenharmony_ci
24908c2ecf20Sopenharmony_ci	kfree(cmd);
24918c2ecf20Sopenharmony_ci	return rc;
24928c2ecf20Sopenharmony_ci}
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci/*
24958c2ecf20Sopenharmony_ci * CMD_GET_HW_SPEC (AP version).
24968c2ecf20Sopenharmony_ci */
24978c2ecf20Sopenharmony_cistruct mwl8k_cmd_get_hw_spec_ap {
24988c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
24998c2ecf20Sopenharmony_ci	__u8 hw_rev;
25008c2ecf20Sopenharmony_ci	__u8 host_interface;
25018c2ecf20Sopenharmony_ci	__le16 num_wcb;
25028c2ecf20Sopenharmony_ci	__le16 num_mcaddrs;
25038c2ecf20Sopenharmony_ci	__u8 perm_addr[ETH_ALEN];
25048c2ecf20Sopenharmony_ci	__le16 region_code;
25058c2ecf20Sopenharmony_ci	__le16 num_antenna;
25068c2ecf20Sopenharmony_ci	__le32 fw_rev;
25078c2ecf20Sopenharmony_ci	__le32 wcbbase0;
25088c2ecf20Sopenharmony_ci	__le32 rxwrptr;
25098c2ecf20Sopenharmony_ci	__le32 rxrdptr;
25108c2ecf20Sopenharmony_ci	__le32 ps_cookie;
25118c2ecf20Sopenharmony_ci	__le32 wcbbase1;
25128c2ecf20Sopenharmony_ci	__le32 wcbbase2;
25138c2ecf20Sopenharmony_ci	__le32 wcbbase3;
25148c2ecf20Sopenharmony_ci	__le32 fw_api_version;
25158c2ecf20Sopenharmony_ci	__le32 caps;
25168c2ecf20Sopenharmony_ci	__le32 num_of_ampdu_queues;
25178c2ecf20Sopenharmony_ci	__le32 wcbbase_ampdu[MWL8K_MAX_AMPDU_QUEUES];
25188c2ecf20Sopenharmony_ci} __packed;
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_cistatic int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw)
25218c2ecf20Sopenharmony_ci{
25228c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
25238c2ecf20Sopenharmony_ci	struct mwl8k_cmd_get_hw_spec_ap *cmd;
25248c2ecf20Sopenharmony_ci	int rc, i;
25258c2ecf20Sopenharmony_ci	u32 api_version;
25268c2ecf20Sopenharmony_ci
25278c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
25288c2ecf20Sopenharmony_ci	if (cmd == NULL)
25298c2ecf20Sopenharmony_ci		return -ENOMEM;
25308c2ecf20Sopenharmony_ci
25318c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC);
25328c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
25338c2ecf20Sopenharmony_ci
25348c2ecf20Sopenharmony_ci	memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr));
25358c2ecf20Sopenharmony_ci	cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
25368c2ecf20Sopenharmony_ci
25378c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
25388c2ecf20Sopenharmony_ci
25398c2ecf20Sopenharmony_ci	if (!rc) {
25408c2ecf20Sopenharmony_ci		int off;
25418c2ecf20Sopenharmony_ci
25428c2ecf20Sopenharmony_ci		api_version = le32_to_cpu(cmd->fw_api_version);
25438c2ecf20Sopenharmony_ci		if (priv->device_info->fw_api_ap != api_version) {
25448c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: Unsupported fw API version for %s."
25458c2ecf20Sopenharmony_ci			       "  Expected %d got %d.\n", MWL8K_NAME,
25468c2ecf20Sopenharmony_ci			       priv->device_info->part_name,
25478c2ecf20Sopenharmony_ci			       priv->device_info->fw_api_ap,
25488c2ecf20Sopenharmony_ci			       api_version);
25498c2ecf20Sopenharmony_ci			rc = -EINVAL;
25508c2ecf20Sopenharmony_ci			goto done;
25518c2ecf20Sopenharmony_ci		}
25528c2ecf20Sopenharmony_ci		SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr);
25538c2ecf20Sopenharmony_ci		priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
25548c2ecf20Sopenharmony_ci		priv->fw_rev = le32_to_cpu(cmd->fw_rev);
25558c2ecf20Sopenharmony_ci		priv->hw_rev = cmd->hw_rev;
25568c2ecf20Sopenharmony_ci		mwl8k_set_caps(hw, le32_to_cpu(cmd->caps));
25578c2ecf20Sopenharmony_ci		priv->ap_macids_supported = 0x000000ff;
25588c2ecf20Sopenharmony_ci		priv->sta_macids_supported = 0x00000100;
25598c2ecf20Sopenharmony_ci		priv->num_ampdu_queues = le32_to_cpu(cmd->num_of_ampdu_queues);
25608c2ecf20Sopenharmony_ci		if (priv->num_ampdu_queues > MWL8K_MAX_AMPDU_QUEUES) {
25618c2ecf20Sopenharmony_ci			wiphy_warn(hw->wiphy, "fw reported %d ampdu queues"
25628c2ecf20Sopenharmony_ci				   " but we only support %d.\n",
25638c2ecf20Sopenharmony_ci				   priv->num_ampdu_queues,
25648c2ecf20Sopenharmony_ci				   MWL8K_MAX_AMPDU_QUEUES);
25658c2ecf20Sopenharmony_ci			priv->num_ampdu_queues = MWL8K_MAX_AMPDU_QUEUES;
25668c2ecf20Sopenharmony_ci		}
25678c2ecf20Sopenharmony_ci		off = le32_to_cpu(cmd->rxwrptr) & 0xffff;
25688c2ecf20Sopenharmony_ci		iowrite32(priv->rxq[0].rxd_dma, priv->sram + off);
25698c2ecf20Sopenharmony_ci
25708c2ecf20Sopenharmony_ci		off = le32_to_cpu(cmd->rxrdptr) & 0xffff;
25718c2ecf20Sopenharmony_ci		iowrite32(priv->rxq[0].rxd_dma, priv->sram + off);
25728c2ecf20Sopenharmony_ci
25738c2ecf20Sopenharmony_ci		priv->txq_offset[0] = le32_to_cpu(cmd->wcbbase0) & 0xffff;
25748c2ecf20Sopenharmony_ci		priv->txq_offset[1] = le32_to_cpu(cmd->wcbbase1) & 0xffff;
25758c2ecf20Sopenharmony_ci		priv->txq_offset[2] = le32_to_cpu(cmd->wcbbase2) & 0xffff;
25768c2ecf20Sopenharmony_ci		priv->txq_offset[3] = le32_to_cpu(cmd->wcbbase3) & 0xffff;
25778c2ecf20Sopenharmony_ci
25788c2ecf20Sopenharmony_ci		for (i = 0; i < priv->num_ampdu_queues; i++)
25798c2ecf20Sopenharmony_ci			priv->txq_offset[i + MWL8K_TX_WMM_QUEUES] =
25808c2ecf20Sopenharmony_ci				le32_to_cpu(cmd->wcbbase_ampdu[i]) & 0xffff;
25818c2ecf20Sopenharmony_ci	}
25828c2ecf20Sopenharmony_ci
25838c2ecf20Sopenharmony_cidone:
25848c2ecf20Sopenharmony_ci	kfree(cmd);
25858c2ecf20Sopenharmony_ci	return rc;
25868c2ecf20Sopenharmony_ci}
25878c2ecf20Sopenharmony_ci
25888c2ecf20Sopenharmony_ci/*
25898c2ecf20Sopenharmony_ci * CMD_SET_HW_SPEC.
25908c2ecf20Sopenharmony_ci */
25918c2ecf20Sopenharmony_cistruct mwl8k_cmd_set_hw_spec {
25928c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
25938c2ecf20Sopenharmony_ci	__u8 hw_rev;
25948c2ecf20Sopenharmony_ci	__u8 host_interface;
25958c2ecf20Sopenharmony_ci	__le16 num_mcaddrs;
25968c2ecf20Sopenharmony_ci	__u8 perm_addr[ETH_ALEN];
25978c2ecf20Sopenharmony_ci	__le16 region_code;
25988c2ecf20Sopenharmony_ci	__le32 fw_rev;
25998c2ecf20Sopenharmony_ci	__le32 ps_cookie;
26008c2ecf20Sopenharmony_ci	__le32 caps;
26018c2ecf20Sopenharmony_ci	__le32 rx_queue_ptr;
26028c2ecf20Sopenharmony_ci	__le32 num_tx_queues;
26038c2ecf20Sopenharmony_ci	__le32 tx_queue_ptrs[MWL8K_MAX_TX_QUEUES];
26048c2ecf20Sopenharmony_ci	__le32 flags;
26058c2ecf20Sopenharmony_ci	__le32 num_tx_desc_per_queue;
26068c2ecf20Sopenharmony_ci	__le32 total_rxd;
26078c2ecf20Sopenharmony_ci} __packed;
26088c2ecf20Sopenharmony_ci
26098c2ecf20Sopenharmony_ci/* If enabled, MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY will cause
26108c2ecf20Sopenharmony_ci * packets to expire 500 ms after the timestamp in the tx descriptor.  That is,
26118c2ecf20Sopenharmony_ci * the packets that are queued for more than 500ms, will be dropped in the
26128c2ecf20Sopenharmony_ci * hardware. This helps minimizing the issues caused due to head-of-line
26138c2ecf20Sopenharmony_ci * blocking where a slow client can hog the bandwidth and affect traffic to a
26148c2ecf20Sopenharmony_ci * faster client.
26158c2ecf20Sopenharmony_ci */
26168c2ecf20Sopenharmony_ci#define MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY	0x00000400
26178c2ecf20Sopenharmony_ci#define MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR	0x00000200
26188c2ecf20Sopenharmony_ci#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT		0x00000080
26198c2ecf20Sopenharmony_ci#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP	0x00000020
26208c2ecf20Sopenharmony_ci#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON		0x00000010
26218c2ecf20Sopenharmony_ci
26228c2ecf20Sopenharmony_cistatic int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw)
26238c2ecf20Sopenharmony_ci{
26248c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
26258c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_hw_spec *cmd;
26268c2ecf20Sopenharmony_ci	int rc;
26278c2ecf20Sopenharmony_ci	int i;
26288c2ecf20Sopenharmony_ci
26298c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
26308c2ecf20Sopenharmony_ci	if (cmd == NULL)
26318c2ecf20Sopenharmony_ci		return -ENOMEM;
26328c2ecf20Sopenharmony_ci
26338c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_HW_SPEC);
26348c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
26358c2ecf20Sopenharmony_ci
26368c2ecf20Sopenharmony_ci	cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
26378c2ecf20Sopenharmony_ci	cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma);
26388c2ecf20Sopenharmony_ci	cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv));
26398c2ecf20Sopenharmony_ci
26408c2ecf20Sopenharmony_ci	/*
26418c2ecf20Sopenharmony_ci	 * Mac80211 stack has Q0 as highest priority and Q3 as lowest in
26428c2ecf20Sopenharmony_ci	 * that order. Firmware has Q3 as highest priority and Q0 as lowest
26438c2ecf20Sopenharmony_ci	 * in that order. Map Q3 of mac80211 to Q0 of firmware so that the
26448c2ecf20Sopenharmony_ci	 * priority is interpreted the right way in firmware.
26458c2ecf20Sopenharmony_ci	 */
26468c2ecf20Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++) {
26478c2ecf20Sopenharmony_ci		int j = mwl8k_tx_queues(priv) - 1 - i;
26488c2ecf20Sopenharmony_ci		cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[j].txd_dma);
26498c2ecf20Sopenharmony_ci	}
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_ci	cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT |
26528c2ecf20Sopenharmony_ci				 MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP |
26538c2ecf20Sopenharmony_ci				 MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON |
26548c2ecf20Sopenharmony_ci				 MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY |
26558c2ecf20Sopenharmony_ci				 MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR);
26568c2ecf20Sopenharmony_ci	cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);
26578c2ecf20Sopenharmony_ci	cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS);
26588c2ecf20Sopenharmony_ci
26598c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
26608c2ecf20Sopenharmony_ci	kfree(cmd);
26618c2ecf20Sopenharmony_ci
26628c2ecf20Sopenharmony_ci	return rc;
26638c2ecf20Sopenharmony_ci}
26648c2ecf20Sopenharmony_ci
26658c2ecf20Sopenharmony_ci/*
26668c2ecf20Sopenharmony_ci * CMD_MAC_MULTICAST_ADR.
26678c2ecf20Sopenharmony_ci */
26688c2ecf20Sopenharmony_cistruct mwl8k_cmd_mac_multicast_adr {
26698c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
26708c2ecf20Sopenharmony_ci	__le16 action;
26718c2ecf20Sopenharmony_ci	__le16 numaddr;
26728c2ecf20Sopenharmony_ci	__u8 addr[][ETH_ALEN];
26738c2ecf20Sopenharmony_ci};
26748c2ecf20Sopenharmony_ci
26758c2ecf20Sopenharmony_ci#define MWL8K_ENABLE_RX_DIRECTED	0x0001
26768c2ecf20Sopenharmony_ci#define MWL8K_ENABLE_RX_MULTICAST	0x0002
26778c2ecf20Sopenharmony_ci#define MWL8K_ENABLE_RX_ALL_MULTICAST	0x0004
26788c2ecf20Sopenharmony_ci#define MWL8K_ENABLE_RX_BROADCAST	0x0008
26798c2ecf20Sopenharmony_ci
26808c2ecf20Sopenharmony_cistatic struct mwl8k_cmd_pkt *
26818c2ecf20Sopenharmony_ci__mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti,
26828c2ecf20Sopenharmony_ci			      struct netdev_hw_addr_list *mc_list)
26838c2ecf20Sopenharmony_ci{
26848c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
26858c2ecf20Sopenharmony_ci	struct mwl8k_cmd_mac_multicast_adr *cmd;
26868c2ecf20Sopenharmony_ci	int size;
26878c2ecf20Sopenharmony_ci	int mc_count = 0;
26888c2ecf20Sopenharmony_ci
26898c2ecf20Sopenharmony_ci	if (mc_list)
26908c2ecf20Sopenharmony_ci		mc_count = netdev_hw_addr_list_count(mc_list);
26918c2ecf20Sopenharmony_ci
26928c2ecf20Sopenharmony_ci	if (allmulti || mc_count > priv->num_mcaddrs) {
26938c2ecf20Sopenharmony_ci		allmulti = 1;
26948c2ecf20Sopenharmony_ci		mc_count = 0;
26958c2ecf20Sopenharmony_ci	}
26968c2ecf20Sopenharmony_ci
26978c2ecf20Sopenharmony_ci	size = sizeof(*cmd) + mc_count * ETH_ALEN;
26988c2ecf20Sopenharmony_ci
26998c2ecf20Sopenharmony_ci	cmd = kzalloc(size, GFP_ATOMIC);
27008c2ecf20Sopenharmony_ci	if (cmd == NULL)
27018c2ecf20Sopenharmony_ci		return NULL;
27028c2ecf20Sopenharmony_ci
27038c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_MAC_MULTICAST_ADR);
27048c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(size);
27058c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_ENABLE_RX_DIRECTED |
27068c2ecf20Sopenharmony_ci				  MWL8K_ENABLE_RX_BROADCAST);
27078c2ecf20Sopenharmony_ci
27088c2ecf20Sopenharmony_ci	if (allmulti) {
27098c2ecf20Sopenharmony_ci		cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_ALL_MULTICAST);
27108c2ecf20Sopenharmony_ci	} else if (mc_count) {
27118c2ecf20Sopenharmony_ci		struct netdev_hw_addr *ha;
27128c2ecf20Sopenharmony_ci		int i = 0;
27138c2ecf20Sopenharmony_ci
27148c2ecf20Sopenharmony_ci		cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST);
27158c2ecf20Sopenharmony_ci		cmd->numaddr = cpu_to_le16(mc_count);
27168c2ecf20Sopenharmony_ci		netdev_hw_addr_list_for_each(ha, mc_list) {
27178c2ecf20Sopenharmony_ci			memcpy(cmd->addr[i], ha->addr, ETH_ALEN);
27188c2ecf20Sopenharmony_ci		}
27198c2ecf20Sopenharmony_ci	}
27208c2ecf20Sopenharmony_ci
27218c2ecf20Sopenharmony_ci	return &cmd->header;
27228c2ecf20Sopenharmony_ci}
27238c2ecf20Sopenharmony_ci
27248c2ecf20Sopenharmony_ci/*
27258c2ecf20Sopenharmony_ci * CMD_GET_STAT.
27268c2ecf20Sopenharmony_ci */
27278c2ecf20Sopenharmony_cistruct mwl8k_cmd_get_stat {
27288c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
27298c2ecf20Sopenharmony_ci	__le32 stats[64];
27308c2ecf20Sopenharmony_ci} __packed;
27318c2ecf20Sopenharmony_ci
27328c2ecf20Sopenharmony_ci#define MWL8K_STAT_ACK_FAILURE	9
27338c2ecf20Sopenharmony_ci#define MWL8K_STAT_RTS_FAILURE	12
27348c2ecf20Sopenharmony_ci#define MWL8K_STAT_FCS_ERROR	24
27358c2ecf20Sopenharmony_ci#define MWL8K_STAT_RTS_SUCCESS	11
27368c2ecf20Sopenharmony_ci
27378c2ecf20Sopenharmony_cistatic int mwl8k_cmd_get_stat(struct ieee80211_hw *hw,
27388c2ecf20Sopenharmony_ci			      struct ieee80211_low_level_stats *stats)
27398c2ecf20Sopenharmony_ci{
27408c2ecf20Sopenharmony_ci	struct mwl8k_cmd_get_stat *cmd;
27418c2ecf20Sopenharmony_ci	int rc;
27428c2ecf20Sopenharmony_ci
27438c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
27448c2ecf20Sopenharmony_ci	if (cmd == NULL)
27458c2ecf20Sopenharmony_ci		return -ENOMEM;
27468c2ecf20Sopenharmony_ci
27478c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_STAT);
27488c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
27498c2ecf20Sopenharmony_ci
27508c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
27518c2ecf20Sopenharmony_ci	if (!rc) {
27528c2ecf20Sopenharmony_ci		stats->dot11ACKFailureCount =
27538c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->stats[MWL8K_STAT_ACK_FAILURE]);
27548c2ecf20Sopenharmony_ci		stats->dot11RTSFailureCount =
27558c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_FAILURE]);
27568c2ecf20Sopenharmony_ci		stats->dot11FCSErrorCount =
27578c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->stats[MWL8K_STAT_FCS_ERROR]);
27588c2ecf20Sopenharmony_ci		stats->dot11RTSSuccessCount =
27598c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_SUCCESS]);
27608c2ecf20Sopenharmony_ci	}
27618c2ecf20Sopenharmony_ci	kfree(cmd);
27628c2ecf20Sopenharmony_ci
27638c2ecf20Sopenharmony_ci	return rc;
27648c2ecf20Sopenharmony_ci}
27658c2ecf20Sopenharmony_ci
27668c2ecf20Sopenharmony_ci/*
27678c2ecf20Sopenharmony_ci * CMD_RADIO_CONTROL.
27688c2ecf20Sopenharmony_ci */
27698c2ecf20Sopenharmony_cistruct mwl8k_cmd_radio_control {
27708c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
27718c2ecf20Sopenharmony_ci	__le16 action;
27728c2ecf20Sopenharmony_ci	__le16 control;
27738c2ecf20Sopenharmony_ci	__le16 radio_on;
27748c2ecf20Sopenharmony_ci} __packed;
27758c2ecf20Sopenharmony_ci
27768c2ecf20Sopenharmony_cistatic int
27778c2ecf20Sopenharmony_cimwl8k_cmd_radio_control(struct ieee80211_hw *hw, bool enable, bool force)
27788c2ecf20Sopenharmony_ci{
27798c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
27808c2ecf20Sopenharmony_ci	struct mwl8k_cmd_radio_control *cmd;
27818c2ecf20Sopenharmony_ci	int rc;
27828c2ecf20Sopenharmony_ci
27838c2ecf20Sopenharmony_ci	if (enable == priv->radio_on && !force)
27848c2ecf20Sopenharmony_ci		return 0;
27858c2ecf20Sopenharmony_ci
27868c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
27878c2ecf20Sopenharmony_ci	if (cmd == NULL)
27888c2ecf20Sopenharmony_ci		return -ENOMEM;
27898c2ecf20Sopenharmony_ci
27908c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_RADIO_CONTROL);
27918c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
27928c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
27938c2ecf20Sopenharmony_ci	cmd->control = cpu_to_le16(priv->radio_short_preamble ? 3 : 1);
27948c2ecf20Sopenharmony_ci	cmd->radio_on = cpu_to_le16(enable ? 0x0001 : 0x0000);
27958c2ecf20Sopenharmony_ci
27968c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
27978c2ecf20Sopenharmony_ci	kfree(cmd);
27988c2ecf20Sopenharmony_ci
27998c2ecf20Sopenharmony_ci	if (!rc)
28008c2ecf20Sopenharmony_ci		priv->radio_on = enable;
28018c2ecf20Sopenharmony_ci
28028c2ecf20Sopenharmony_ci	return rc;
28038c2ecf20Sopenharmony_ci}
28048c2ecf20Sopenharmony_ci
28058c2ecf20Sopenharmony_cistatic int mwl8k_cmd_radio_disable(struct ieee80211_hw *hw)
28068c2ecf20Sopenharmony_ci{
28078c2ecf20Sopenharmony_ci	return mwl8k_cmd_radio_control(hw, 0, 0);
28088c2ecf20Sopenharmony_ci}
28098c2ecf20Sopenharmony_ci
28108c2ecf20Sopenharmony_cistatic int mwl8k_cmd_radio_enable(struct ieee80211_hw *hw)
28118c2ecf20Sopenharmony_ci{
28128c2ecf20Sopenharmony_ci	return mwl8k_cmd_radio_control(hw, 1, 0);
28138c2ecf20Sopenharmony_ci}
28148c2ecf20Sopenharmony_ci
28158c2ecf20Sopenharmony_cistatic int
28168c2ecf20Sopenharmony_cimwl8k_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble)
28178c2ecf20Sopenharmony_ci{
28188c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
28198c2ecf20Sopenharmony_ci
28208c2ecf20Sopenharmony_ci	priv->radio_short_preamble = short_preamble;
28218c2ecf20Sopenharmony_ci
28228c2ecf20Sopenharmony_ci	return mwl8k_cmd_radio_control(hw, 1, 1);
28238c2ecf20Sopenharmony_ci}
28248c2ecf20Sopenharmony_ci
28258c2ecf20Sopenharmony_ci/*
28268c2ecf20Sopenharmony_ci * CMD_RF_TX_POWER.
28278c2ecf20Sopenharmony_ci */
28288c2ecf20Sopenharmony_ci#define MWL8K_RF_TX_POWER_LEVEL_TOTAL	8
28298c2ecf20Sopenharmony_ci
28308c2ecf20Sopenharmony_cistruct mwl8k_cmd_rf_tx_power {
28318c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
28328c2ecf20Sopenharmony_ci	__le16 action;
28338c2ecf20Sopenharmony_ci	__le16 support_level;
28348c2ecf20Sopenharmony_ci	__le16 current_level;
28358c2ecf20Sopenharmony_ci	__le16 reserved;
28368c2ecf20Sopenharmony_ci	__le16 power_level_list[MWL8K_RF_TX_POWER_LEVEL_TOTAL];
28378c2ecf20Sopenharmony_ci} __packed;
28388c2ecf20Sopenharmony_ci
28398c2ecf20Sopenharmony_cistatic int mwl8k_cmd_rf_tx_power(struct ieee80211_hw *hw, int dBm)
28408c2ecf20Sopenharmony_ci{
28418c2ecf20Sopenharmony_ci	struct mwl8k_cmd_rf_tx_power *cmd;
28428c2ecf20Sopenharmony_ci	int rc;
28438c2ecf20Sopenharmony_ci
28448c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
28458c2ecf20Sopenharmony_ci	if (cmd == NULL)
28468c2ecf20Sopenharmony_ci		return -ENOMEM;
28478c2ecf20Sopenharmony_ci
28488c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_TX_POWER);
28498c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
28508c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
28518c2ecf20Sopenharmony_ci	cmd->support_level = cpu_to_le16(dBm);
28528c2ecf20Sopenharmony_ci
28538c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
28548c2ecf20Sopenharmony_ci	kfree(cmd);
28558c2ecf20Sopenharmony_ci
28568c2ecf20Sopenharmony_ci	return rc;
28578c2ecf20Sopenharmony_ci}
28588c2ecf20Sopenharmony_ci
28598c2ecf20Sopenharmony_ci/*
28608c2ecf20Sopenharmony_ci * CMD_TX_POWER.
28618c2ecf20Sopenharmony_ci */
28628c2ecf20Sopenharmony_ci#define MWL8K_TX_POWER_LEVEL_TOTAL      12
28638c2ecf20Sopenharmony_ci
28648c2ecf20Sopenharmony_cistruct mwl8k_cmd_tx_power {
28658c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
28668c2ecf20Sopenharmony_ci	__le16 action;
28678c2ecf20Sopenharmony_ci	__le16 band;
28688c2ecf20Sopenharmony_ci	__le16 channel;
28698c2ecf20Sopenharmony_ci	__le16 bw;
28708c2ecf20Sopenharmony_ci	__le16 sub_ch;
28718c2ecf20Sopenharmony_ci	__le16 power_level_list[MWL8K_TX_POWER_LEVEL_TOTAL];
28728c2ecf20Sopenharmony_ci} __packed;
28738c2ecf20Sopenharmony_ci
28748c2ecf20Sopenharmony_cistatic int mwl8k_cmd_tx_power(struct ieee80211_hw *hw,
28758c2ecf20Sopenharmony_ci				     struct ieee80211_conf *conf,
28768c2ecf20Sopenharmony_ci				     unsigned short pwr)
28778c2ecf20Sopenharmony_ci{
28788c2ecf20Sopenharmony_ci	struct ieee80211_channel *channel = conf->chandef.chan;
28798c2ecf20Sopenharmony_ci	enum nl80211_channel_type channel_type =
28808c2ecf20Sopenharmony_ci		cfg80211_get_chandef_type(&conf->chandef);
28818c2ecf20Sopenharmony_ci	struct mwl8k_cmd_tx_power *cmd;
28828c2ecf20Sopenharmony_ci	int rc;
28838c2ecf20Sopenharmony_ci	int i;
28848c2ecf20Sopenharmony_ci
28858c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
28868c2ecf20Sopenharmony_ci	if (cmd == NULL)
28878c2ecf20Sopenharmony_ci		return -ENOMEM;
28888c2ecf20Sopenharmony_ci
28898c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_TX_POWER);
28908c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
28918c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_CMD_SET_LIST);
28928c2ecf20Sopenharmony_ci
28938c2ecf20Sopenharmony_ci	if (channel->band == NL80211_BAND_2GHZ)
28948c2ecf20Sopenharmony_ci		cmd->band = cpu_to_le16(0x1);
28958c2ecf20Sopenharmony_ci	else if (channel->band == NL80211_BAND_5GHZ)
28968c2ecf20Sopenharmony_ci		cmd->band = cpu_to_le16(0x4);
28978c2ecf20Sopenharmony_ci
28988c2ecf20Sopenharmony_ci	cmd->channel = cpu_to_le16(channel->hw_value);
28998c2ecf20Sopenharmony_ci
29008c2ecf20Sopenharmony_ci	if (channel_type == NL80211_CHAN_NO_HT ||
29018c2ecf20Sopenharmony_ci	    channel_type == NL80211_CHAN_HT20) {
29028c2ecf20Sopenharmony_ci		cmd->bw = cpu_to_le16(0x2);
29038c2ecf20Sopenharmony_ci	} else {
29048c2ecf20Sopenharmony_ci		cmd->bw = cpu_to_le16(0x4);
29058c2ecf20Sopenharmony_ci		if (channel_type == NL80211_CHAN_HT40MINUS)
29068c2ecf20Sopenharmony_ci			cmd->sub_ch = cpu_to_le16(0x3);
29078c2ecf20Sopenharmony_ci		else if (channel_type == NL80211_CHAN_HT40PLUS)
29088c2ecf20Sopenharmony_ci			cmd->sub_ch = cpu_to_le16(0x1);
29098c2ecf20Sopenharmony_ci	}
29108c2ecf20Sopenharmony_ci
29118c2ecf20Sopenharmony_ci	for (i = 0; i < MWL8K_TX_POWER_LEVEL_TOTAL; i++)
29128c2ecf20Sopenharmony_ci		cmd->power_level_list[i] = cpu_to_le16(pwr);
29138c2ecf20Sopenharmony_ci
29148c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
29158c2ecf20Sopenharmony_ci	kfree(cmd);
29168c2ecf20Sopenharmony_ci
29178c2ecf20Sopenharmony_ci	return rc;
29188c2ecf20Sopenharmony_ci}
29198c2ecf20Sopenharmony_ci
29208c2ecf20Sopenharmony_ci/*
29218c2ecf20Sopenharmony_ci * CMD_RF_ANTENNA.
29228c2ecf20Sopenharmony_ci */
29238c2ecf20Sopenharmony_cistruct mwl8k_cmd_rf_antenna {
29248c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
29258c2ecf20Sopenharmony_ci	__le16 antenna;
29268c2ecf20Sopenharmony_ci	__le16 mode;
29278c2ecf20Sopenharmony_ci} __packed;
29288c2ecf20Sopenharmony_ci
29298c2ecf20Sopenharmony_ci#define MWL8K_RF_ANTENNA_RX		1
29308c2ecf20Sopenharmony_ci#define MWL8K_RF_ANTENNA_TX		2
29318c2ecf20Sopenharmony_ci
29328c2ecf20Sopenharmony_cistatic int
29338c2ecf20Sopenharmony_cimwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask)
29348c2ecf20Sopenharmony_ci{
29358c2ecf20Sopenharmony_ci	struct mwl8k_cmd_rf_antenna *cmd;
29368c2ecf20Sopenharmony_ci	int rc;
29378c2ecf20Sopenharmony_ci
29388c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
29398c2ecf20Sopenharmony_ci	if (cmd == NULL)
29408c2ecf20Sopenharmony_ci		return -ENOMEM;
29418c2ecf20Sopenharmony_ci
29428c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_ANTENNA);
29438c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
29448c2ecf20Sopenharmony_ci	cmd->antenna = cpu_to_le16(antenna);
29458c2ecf20Sopenharmony_ci	cmd->mode = cpu_to_le16(mask);
29468c2ecf20Sopenharmony_ci
29478c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
29488c2ecf20Sopenharmony_ci	kfree(cmd);
29498c2ecf20Sopenharmony_ci
29508c2ecf20Sopenharmony_ci	return rc;
29518c2ecf20Sopenharmony_ci}
29528c2ecf20Sopenharmony_ci
29538c2ecf20Sopenharmony_ci/*
29548c2ecf20Sopenharmony_ci * CMD_SET_BEACON.
29558c2ecf20Sopenharmony_ci */
29568c2ecf20Sopenharmony_cistruct mwl8k_cmd_set_beacon {
29578c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
29588c2ecf20Sopenharmony_ci	__le16 beacon_len;
29598c2ecf20Sopenharmony_ci	__u8 beacon[];
29608c2ecf20Sopenharmony_ci};
29618c2ecf20Sopenharmony_ci
29628c2ecf20Sopenharmony_cistatic int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw,
29638c2ecf20Sopenharmony_ci				struct ieee80211_vif *vif, u8 *beacon, int len)
29648c2ecf20Sopenharmony_ci{
29658c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_beacon *cmd;
29668c2ecf20Sopenharmony_ci	int rc;
29678c2ecf20Sopenharmony_ci
29688c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd) + len, GFP_KERNEL);
29698c2ecf20Sopenharmony_ci	if (cmd == NULL)
29708c2ecf20Sopenharmony_ci		return -ENOMEM;
29718c2ecf20Sopenharmony_ci
29728c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_BEACON);
29738c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd) + len);
29748c2ecf20Sopenharmony_ci	cmd->beacon_len = cpu_to_le16(len);
29758c2ecf20Sopenharmony_ci	memcpy(cmd->beacon, beacon, len);
29768c2ecf20Sopenharmony_ci
29778c2ecf20Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
29788c2ecf20Sopenharmony_ci	kfree(cmd);
29798c2ecf20Sopenharmony_ci
29808c2ecf20Sopenharmony_ci	return rc;
29818c2ecf20Sopenharmony_ci}
29828c2ecf20Sopenharmony_ci
29838c2ecf20Sopenharmony_ci/*
29848c2ecf20Sopenharmony_ci * CMD_SET_PRE_SCAN.
29858c2ecf20Sopenharmony_ci */
29868c2ecf20Sopenharmony_cistruct mwl8k_cmd_set_pre_scan {
29878c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
29888c2ecf20Sopenharmony_ci} __packed;
29898c2ecf20Sopenharmony_ci
29908c2ecf20Sopenharmony_cistatic int mwl8k_cmd_set_pre_scan(struct ieee80211_hw *hw)
29918c2ecf20Sopenharmony_ci{
29928c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_pre_scan *cmd;
29938c2ecf20Sopenharmony_ci	int rc;
29948c2ecf20Sopenharmony_ci
29958c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
29968c2ecf20Sopenharmony_ci	if (cmd == NULL)
29978c2ecf20Sopenharmony_ci		return -ENOMEM;
29988c2ecf20Sopenharmony_ci
29998c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_PRE_SCAN);
30008c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
30018c2ecf20Sopenharmony_ci
30028c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
30038c2ecf20Sopenharmony_ci	kfree(cmd);
30048c2ecf20Sopenharmony_ci
30058c2ecf20Sopenharmony_ci	return rc;
30068c2ecf20Sopenharmony_ci}
30078c2ecf20Sopenharmony_ci
30088c2ecf20Sopenharmony_ci/*
30098c2ecf20Sopenharmony_ci * CMD_BBP_REG_ACCESS.
30108c2ecf20Sopenharmony_ci */
30118c2ecf20Sopenharmony_cistruct mwl8k_cmd_bbp_reg_access {
30128c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
30138c2ecf20Sopenharmony_ci	__le16 action;
30148c2ecf20Sopenharmony_ci	__le16 offset;
30158c2ecf20Sopenharmony_ci	u8 value;
30168c2ecf20Sopenharmony_ci	u8 rsrv[3];
30178c2ecf20Sopenharmony_ci} __packed;
30188c2ecf20Sopenharmony_ci
30198c2ecf20Sopenharmony_cistatic int
30208c2ecf20Sopenharmony_cimwl8k_cmd_bbp_reg_access(struct ieee80211_hw *hw,
30218c2ecf20Sopenharmony_ci			 u16 action,
30228c2ecf20Sopenharmony_ci			 u16 offset,
30238c2ecf20Sopenharmony_ci			 u8 *value)
30248c2ecf20Sopenharmony_ci{
30258c2ecf20Sopenharmony_ci	struct mwl8k_cmd_bbp_reg_access *cmd;
30268c2ecf20Sopenharmony_ci	int rc;
30278c2ecf20Sopenharmony_ci
30288c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
30298c2ecf20Sopenharmony_ci	if (cmd == NULL)
30308c2ecf20Sopenharmony_ci		return -ENOMEM;
30318c2ecf20Sopenharmony_ci
30328c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_BBP_REG_ACCESS);
30338c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
30348c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le16(action);
30358c2ecf20Sopenharmony_ci	cmd->offset = cpu_to_le16(offset);
30368c2ecf20Sopenharmony_ci
30378c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
30388c2ecf20Sopenharmony_ci
30398c2ecf20Sopenharmony_ci	if (!rc)
30408c2ecf20Sopenharmony_ci		*value = cmd->value;
30418c2ecf20Sopenharmony_ci	else
30428c2ecf20Sopenharmony_ci		*value = 0;
30438c2ecf20Sopenharmony_ci
30448c2ecf20Sopenharmony_ci	kfree(cmd);
30458c2ecf20Sopenharmony_ci
30468c2ecf20Sopenharmony_ci	return rc;
30478c2ecf20Sopenharmony_ci}
30488c2ecf20Sopenharmony_ci
30498c2ecf20Sopenharmony_ci/*
30508c2ecf20Sopenharmony_ci * CMD_SET_POST_SCAN.
30518c2ecf20Sopenharmony_ci */
30528c2ecf20Sopenharmony_cistruct mwl8k_cmd_set_post_scan {
30538c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
30548c2ecf20Sopenharmony_ci	__le32 isibss;
30558c2ecf20Sopenharmony_ci	__u8 bssid[ETH_ALEN];
30568c2ecf20Sopenharmony_ci} __packed;
30578c2ecf20Sopenharmony_ci
30588c2ecf20Sopenharmony_cistatic int
30598c2ecf20Sopenharmony_cimwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac)
30608c2ecf20Sopenharmony_ci{
30618c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_post_scan *cmd;
30628c2ecf20Sopenharmony_ci	int rc;
30638c2ecf20Sopenharmony_ci
30648c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
30658c2ecf20Sopenharmony_ci	if (cmd == NULL)
30668c2ecf20Sopenharmony_ci		return -ENOMEM;
30678c2ecf20Sopenharmony_ci
30688c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_POST_SCAN);
30698c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
30708c2ecf20Sopenharmony_ci	cmd->isibss = 0;
30718c2ecf20Sopenharmony_ci	memcpy(cmd->bssid, mac, ETH_ALEN);
30728c2ecf20Sopenharmony_ci
30738c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
30748c2ecf20Sopenharmony_ci	kfree(cmd);
30758c2ecf20Sopenharmony_ci
30768c2ecf20Sopenharmony_ci	return rc;
30778c2ecf20Sopenharmony_ci}
30788c2ecf20Sopenharmony_ci
30798c2ecf20Sopenharmony_cistatic int freq_to_idx(struct mwl8k_priv *priv, int freq)
30808c2ecf20Sopenharmony_ci{
30818c2ecf20Sopenharmony_ci	struct ieee80211_supported_band *sband;
30828c2ecf20Sopenharmony_ci	int band, ch, idx = 0;
30838c2ecf20Sopenharmony_ci
30848c2ecf20Sopenharmony_ci	for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
30858c2ecf20Sopenharmony_ci		sband = priv->hw->wiphy->bands[band];
30868c2ecf20Sopenharmony_ci		if (!sband)
30878c2ecf20Sopenharmony_ci			continue;
30888c2ecf20Sopenharmony_ci
30898c2ecf20Sopenharmony_ci		for (ch = 0; ch < sband->n_channels; ch++, idx++)
30908c2ecf20Sopenharmony_ci			if (sband->channels[ch].center_freq == freq)
30918c2ecf20Sopenharmony_ci				goto exit;
30928c2ecf20Sopenharmony_ci	}
30938c2ecf20Sopenharmony_ci
30948c2ecf20Sopenharmony_ciexit:
30958c2ecf20Sopenharmony_ci	return idx;
30968c2ecf20Sopenharmony_ci}
30978c2ecf20Sopenharmony_ci
30988c2ecf20Sopenharmony_cistatic void mwl8k_update_survey(struct mwl8k_priv *priv,
30998c2ecf20Sopenharmony_ci				struct ieee80211_channel *channel)
31008c2ecf20Sopenharmony_ci{
31018c2ecf20Sopenharmony_ci	u32 cca_cnt, rx_rdy;
31028c2ecf20Sopenharmony_ci	s8 nf = 0, idx;
31038c2ecf20Sopenharmony_ci	struct survey_info *survey;
31048c2ecf20Sopenharmony_ci
31058c2ecf20Sopenharmony_ci	idx = freq_to_idx(priv, priv->acs_chan->center_freq);
31068c2ecf20Sopenharmony_ci	if (idx >= MWL8K_NUM_CHANS) {
31078c2ecf20Sopenharmony_ci		wiphy_err(priv->hw->wiphy, "Failed to update survey\n");
31088c2ecf20Sopenharmony_ci		return;
31098c2ecf20Sopenharmony_ci	}
31108c2ecf20Sopenharmony_ci
31118c2ecf20Sopenharmony_ci	survey = &priv->survey[idx];
31128c2ecf20Sopenharmony_ci
31138c2ecf20Sopenharmony_ci	cca_cnt = ioread32(priv->regs + NOK_CCA_CNT_REG);
31148c2ecf20Sopenharmony_ci	cca_cnt /= 1000; /* uSecs to mSecs */
31158c2ecf20Sopenharmony_ci	survey->time_busy = (u64) cca_cnt;
31168c2ecf20Sopenharmony_ci
31178c2ecf20Sopenharmony_ci	rx_rdy = ioread32(priv->regs + BBU_RXRDY_CNT_REG);
31188c2ecf20Sopenharmony_ci	rx_rdy /= 1000; /* uSecs to mSecs */
31198c2ecf20Sopenharmony_ci	survey->time_rx = (u64) rx_rdy;
31208c2ecf20Sopenharmony_ci
31218c2ecf20Sopenharmony_ci	priv->channel_time = jiffies - priv->channel_time;
31228c2ecf20Sopenharmony_ci	survey->time = jiffies_to_msecs(priv->channel_time);
31238c2ecf20Sopenharmony_ci
31248c2ecf20Sopenharmony_ci	survey->channel = channel;
31258c2ecf20Sopenharmony_ci
31268c2ecf20Sopenharmony_ci	mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &nf);
31278c2ecf20Sopenharmony_ci
31288c2ecf20Sopenharmony_ci	/* Make sure sign is negative else ACS  at hostapd fails */
31298c2ecf20Sopenharmony_ci	survey->noise = nf * -1;
31308c2ecf20Sopenharmony_ci
31318c2ecf20Sopenharmony_ci	survey->filled = SURVEY_INFO_NOISE_DBM |
31328c2ecf20Sopenharmony_ci			 SURVEY_INFO_TIME |
31338c2ecf20Sopenharmony_ci			 SURVEY_INFO_TIME_BUSY |
31348c2ecf20Sopenharmony_ci			 SURVEY_INFO_TIME_RX;
31358c2ecf20Sopenharmony_ci}
31368c2ecf20Sopenharmony_ci
31378c2ecf20Sopenharmony_ci/*
31388c2ecf20Sopenharmony_ci * CMD_SET_RF_CHANNEL.
31398c2ecf20Sopenharmony_ci */
31408c2ecf20Sopenharmony_cistruct mwl8k_cmd_set_rf_channel {
31418c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
31428c2ecf20Sopenharmony_ci	__le16 action;
31438c2ecf20Sopenharmony_ci	__u8 current_channel;
31448c2ecf20Sopenharmony_ci	__le32 channel_flags;
31458c2ecf20Sopenharmony_ci} __packed;
31468c2ecf20Sopenharmony_ci
31478c2ecf20Sopenharmony_cistatic int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw,
31488c2ecf20Sopenharmony_ci				    struct ieee80211_conf *conf)
31498c2ecf20Sopenharmony_ci{
31508c2ecf20Sopenharmony_ci	struct ieee80211_channel *channel = conf->chandef.chan;
31518c2ecf20Sopenharmony_ci	enum nl80211_channel_type channel_type =
31528c2ecf20Sopenharmony_ci		cfg80211_get_chandef_type(&conf->chandef);
31538c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_rf_channel *cmd;
31548c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
31558c2ecf20Sopenharmony_ci	int rc;
31568c2ecf20Sopenharmony_ci
31578c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
31588c2ecf20Sopenharmony_ci	if (cmd == NULL)
31598c2ecf20Sopenharmony_ci		return -ENOMEM;
31608c2ecf20Sopenharmony_ci
31618c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RF_CHANNEL);
31628c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
31638c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
31648c2ecf20Sopenharmony_ci	cmd->current_channel = channel->hw_value;
31658c2ecf20Sopenharmony_ci
31668c2ecf20Sopenharmony_ci	if (channel->band == NL80211_BAND_2GHZ)
31678c2ecf20Sopenharmony_ci		cmd->channel_flags |= cpu_to_le32(0x00000001);
31688c2ecf20Sopenharmony_ci	else if (channel->band == NL80211_BAND_5GHZ)
31698c2ecf20Sopenharmony_ci		cmd->channel_flags |= cpu_to_le32(0x00000004);
31708c2ecf20Sopenharmony_ci
31718c2ecf20Sopenharmony_ci	if (!priv->sw_scan_start) {
31728c2ecf20Sopenharmony_ci		if (channel_type == NL80211_CHAN_NO_HT ||
31738c2ecf20Sopenharmony_ci		    channel_type == NL80211_CHAN_HT20)
31748c2ecf20Sopenharmony_ci			cmd->channel_flags |= cpu_to_le32(0x00000080);
31758c2ecf20Sopenharmony_ci		else if (channel_type == NL80211_CHAN_HT40MINUS)
31768c2ecf20Sopenharmony_ci			cmd->channel_flags |= cpu_to_le32(0x000001900);
31778c2ecf20Sopenharmony_ci		else if (channel_type == NL80211_CHAN_HT40PLUS)
31788c2ecf20Sopenharmony_ci			cmd->channel_flags |= cpu_to_le32(0x000000900);
31798c2ecf20Sopenharmony_ci	} else {
31808c2ecf20Sopenharmony_ci		cmd->channel_flags |= cpu_to_le32(0x00000080);
31818c2ecf20Sopenharmony_ci	}
31828c2ecf20Sopenharmony_ci
31838c2ecf20Sopenharmony_ci	if (priv->sw_scan_start) {
31848c2ecf20Sopenharmony_ci		/* Store current channel stats
31858c2ecf20Sopenharmony_ci		 * before switching to newer one.
31868c2ecf20Sopenharmony_ci		 * This will be processed only for AP fw.
31878c2ecf20Sopenharmony_ci		 */
31888c2ecf20Sopenharmony_ci		if (priv->channel_time != 0)
31898c2ecf20Sopenharmony_ci			mwl8k_update_survey(priv, priv->acs_chan);
31908c2ecf20Sopenharmony_ci
31918c2ecf20Sopenharmony_ci		priv->channel_time = jiffies;
31928c2ecf20Sopenharmony_ci		priv->acs_chan =  channel;
31938c2ecf20Sopenharmony_ci	}
31948c2ecf20Sopenharmony_ci
31958c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
31968c2ecf20Sopenharmony_ci	kfree(cmd);
31978c2ecf20Sopenharmony_ci
31988c2ecf20Sopenharmony_ci	return rc;
31998c2ecf20Sopenharmony_ci}
32008c2ecf20Sopenharmony_ci
32018c2ecf20Sopenharmony_ci/*
32028c2ecf20Sopenharmony_ci * CMD_SET_AID.
32038c2ecf20Sopenharmony_ci */
32048c2ecf20Sopenharmony_ci#define MWL8K_FRAME_PROT_DISABLED			0x00
32058c2ecf20Sopenharmony_ci#define MWL8K_FRAME_PROT_11G				0x07
32068c2ecf20Sopenharmony_ci#define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY		0x02
32078c2ecf20Sopenharmony_ci#define MWL8K_FRAME_PROT_11N_HT_ALL			0x06
32088c2ecf20Sopenharmony_ci
32098c2ecf20Sopenharmony_cistruct mwl8k_cmd_update_set_aid {
32108c2ecf20Sopenharmony_ci	struct	mwl8k_cmd_pkt header;
32118c2ecf20Sopenharmony_ci	__le16	aid;
32128c2ecf20Sopenharmony_ci
32138c2ecf20Sopenharmony_ci	 /* AP's MAC address (BSSID) */
32148c2ecf20Sopenharmony_ci	__u8	bssid[ETH_ALEN];
32158c2ecf20Sopenharmony_ci	__le16	protection_mode;
32168c2ecf20Sopenharmony_ci	__u8	supp_rates[14];
32178c2ecf20Sopenharmony_ci} __packed;
32188c2ecf20Sopenharmony_ci
32198c2ecf20Sopenharmony_cistatic void legacy_rate_mask_to_array(u8 *rates, u32 mask)
32208c2ecf20Sopenharmony_ci{
32218c2ecf20Sopenharmony_ci	int i;
32228c2ecf20Sopenharmony_ci	int j;
32238c2ecf20Sopenharmony_ci
32248c2ecf20Sopenharmony_ci	/*
32258c2ecf20Sopenharmony_ci	 * Clear nonstandard rate 4.
32268c2ecf20Sopenharmony_ci	 */
32278c2ecf20Sopenharmony_ci	mask &= 0x1fef;
32288c2ecf20Sopenharmony_ci
32298c2ecf20Sopenharmony_ci	for (i = 0, j = 0; i < 13; i++) {
32308c2ecf20Sopenharmony_ci		if (mask & (1 << i))
32318c2ecf20Sopenharmony_ci			rates[j++] = mwl8k_rates_24[i].hw_value;
32328c2ecf20Sopenharmony_ci	}
32338c2ecf20Sopenharmony_ci}
32348c2ecf20Sopenharmony_ci
32358c2ecf20Sopenharmony_cistatic int
32368c2ecf20Sopenharmony_cimwl8k_cmd_set_aid(struct ieee80211_hw *hw,
32378c2ecf20Sopenharmony_ci		  struct ieee80211_vif *vif, u32 legacy_rate_mask)
32388c2ecf20Sopenharmony_ci{
32398c2ecf20Sopenharmony_ci	struct mwl8k_cmd_update_set_aid *cmd;
32408c2ecf20Sopenharmony_ci	u16 prot_mode;
32418c2ecf20Sopenharmony_ci	int rc;
32428c2ecf20Sopenharmony_ci
32438c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
32448c2ecf20Sopenharmony_ci	if (cmd == NULL)
32458c2ecf20Sopenharmony_ci		return -ENOMEM;
32468c2ecf20Sopenharmony_ci
32478c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID);
32488c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
32498c2ecf20Sopenharmony_ci	cmd->aid = cpu_to_le16(vif->bss_conf.aid);
32508c2ecf20Sopenharmony_ci	memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN);
32518c2ecf20Sopenharmony_ci
32528c2ecf20Sopenharmony_ci	if (vif->bss_conf.use_cts_prot) {
32538c2ecf20Sopenharmony_ci		prot_mode = MWL8K_FRAME_PROT_11G;
32548c2ecf20Sopenharmony_ci	} else {
32558c2ecf20Sopenharmony_ci		switch (vif->bss_conf.ht_operation_mode &
32568c2ecf20Sopenharmony_ci			IEEE80211_HT_OP_MODE_PROTECTION) {
32578c2ecf20Sopenharmony_ci		case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
32588c2ecf20Sopenharmony_ci			prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY;
32598c2ecf20Sopenharmony_ci			break;
32608c2ecf20Sopenharmony_ci		case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
32618c2ecf20Sopenharmony_ci			prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL;
32628c2ecf20Sopenharmony_ci			break;
32638c2ecf20Sopenharmony_ci		default:
32648c2ecf20Sopenharmony_ci			prot_mode = MWL8K_FRAME_PROT_DISABLED;
32658c2ecf20Sopenharmony_ci			break;
32668c2ecf20Sopenharmony_ci		}
32678c2ecf20Sopenharmony_ci	}
32688c2ecf20Sopenharmony_ci	cmd->protection_mode = cpu_to_le16(prot_mode);
32698c2ecf20Sopenharmony_ci
32708c2ecf20Sopenharmony_ci	legacy_rate_mask_to_array(cmd->supp_rates, legacy_rate_mask);
32718c2ecf20Sopenharmony_ci
32728c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
32738c2ecf20Sopenharmony_ci	kfree(cmd);
32748c2ecf20Sopenharmony_ci
32758c2ecf20Sopenharmony_ci	return rc;
32768c2ecf20Sopenharmony_ci}
32778c2ecf20Sopenharmony_ci
32788c2ecf20Sopenharmony_ci/*
32798c2ecf20Sopenharmony_ci * CMD_SET_RATE.
32808c2ecf20Sopenharmony_ci */
32818c2ecf20Sopenharmony_cistruct mwl8k_cmd_set_rate {
32828c2ecf20Sopenharmony_ci	struct	mwl8k_cmd_pkt header;
32838c2ecf20Sopenharmony_ci	__u8	legacy_rates[14];
32848c2ecf20Sopenharmony_ci
32858c2ecf20Sopenharmony_ci	/* Bitmap for supported MCS codes.  */
32868c2ecf20Sopenharmony_ci	__u8	mcs_set[16];
32878c2ecf20Sopenharmony_ci	__u8	reserved[16];
32888c2ecf20Sopenharmony_ci} __packed;
32898c2ecf20Sopenharmony_ci
32908c2ecf20Sopenharmony_cistatic int
32918c2ecf20Sopenharmony_cimwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
32928c2ecf20Sopenharmony_ci		   u32 legacy_rate_mask, u8 *mcs_rates)
32938c2ecf20Sopenharmony_ci{
32948c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_rate *cmd;
32958c2ecf20Sopenharmony_ci	int rc;
32968c2ecf20Sopenharmony_ci
32978c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
32988c2ecf20Sopenharmony_ci	if (cmd == NULL)
32998c2ecf20Sopenharmony_ci		return -ENOMEM;
33008c2ecf20Sopenharmony_ci
33018c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE);
33028c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
33038c2ecf20Sopenharmony_ci	legacy_rate_mask_to_array(cmd->legacy_rates, legacy_rate_mask);
33048c2ecf20Sopenharmony_ci	memcpy(cmd->mcs_set, mcs_rates, 16);
33058c2ecf20Sopenharmony_ci
33068c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
33078c2ecf20Sopenharmony_ci	kfree(cmd);
33088c2ecf20Sopenharmony_ci
33098c2ecf20Sopenharmony_ci	return rc;
33108c2ecf20Sopenharmony_ci}
33118c2ecf20Sopenharmony_ci
33128c2ecf20Sopenharmony_ci/*
33138c2ecf20Sopenharmony_ci * CMD_FINALIZE_JOIN.
33148c2ecf20Sopenharmony_ci */
33158c2ecf20Sopenharmony_ci#define MWL8K_FJ_BEACON_MAXLEN	128
33168c2ecf20Sopenharmony_ci
33178c2ecf20Sopenharmony_cistruct mwl8k_cmd_finalize_join {
33188c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
33198c2ecf20Sopenharmony_ci	__le32 sleep_interval;	/* Number of beacon periods to sleep */
33208c2ecf20Sopenharmony_ci	__u8 beacon_data[MWL8K_FJ_BEACON_MAXLEN];
33218c2ecf20Sopenharmony_ci} __packed;
33228c2ecf20Sopenharmony_ci
33238c2ecf20Sopenharmony_cistatic int mwl8k_cmd_finalize_join(struct ieee80211_hw *hw, void *frame,
33248c2ecf20Sopenharmony_ci				   int framelen, int dtim)
33258c2ecf20Sopenharmony_ci{
33268c2ecf20Sopenharmony_ci	struct mwl8k_cmd_finalize_join *cmd;
33278c2ecf20Sopenharmony_ci	struct ieee80211_mgmt *payload = frame;
33288c2ecf20Sopenharmony_ci	int payload_len;
33298c2ecf20Sopenharmony_ci	int rc;
33308c2ecf20Sopenharmony_ci
33318c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
33328c2ecf20Sopenharmony_ci	if (cmd == NULL)
33338c2ecf20Sopenharmony_ci		return -ENOMEM;
33348c2ecf20Sopenharmony_ci
33358c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN);
33368c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
33378c2ecf20Sopenharmony_ci	cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1);
33388c2ecf20Sopenharmony_ci
33398c2ecf20Sopenharmony_ci	payload_len = framelen - ieee80211_hdrlen(payload->frame_control);
33408c2ecf20Sopenharmony_ci	if (payload_len < 0)
33418c2ecf20Sopenharmony_ci		payload_len = 0;
33428c2ecf20Sopenharmony_ci	else if (payload_len > MWL8K_FJ_BEACON_MAXLEN)
33438c2ecf20Sopenharmony_ci		payload_len = MWL8K_FJ_BEACON_MAXLEN;
33448c2ecf20Sopenharmony_ci
33458c2ecf20Sopenharmony_ci	memcpy(cmd->beacon_data, &payload->u.beacon, payload_len);
33468c2ecf20Sopenharmony_ci
33478c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
33488c2ecf20Sopenharmony_ci	kfree(cmd);
33498c2ecf20Sopenharmony_ci
33508c2ecf20Sopenharmony_ci	return rc;
33518c2ecf20Sopenharmony_ci}
33528c2ecf20Sopenharmony_ci
33538c2ecf20Sopenharmony_ci/*
33548c2ecf20Sopenharmony_ci * CMD_SET_RTS_THRESHOLD.
33558c2ecf20Sopenharmony_ci */
33568c2ecf20Sopenharmony_cistruct mwl8k_cmd_set_rts_threshold {
33578c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
33588c2ecf20Sopenharmony_ci	__le16 action;
33598c2ecf20Sopenharmony_ci	__le16 threshold;
33608c2ecf20Sopenharmony_ci} __packed;
33618c2ecf20Sopenharmony_ci
33628c2ecf20Sopenharmony_cistatic int
33638c2ecf20Sopenharmony_cimwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, int rts_thresh)
33648c2ecf20Sopenharmony_ci{
33658c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_rts_threshold *cmd;
33668c2ecf20Sopenharmony_ci	int rc;
33678c2ecf20Sopenharmony_ci
33688c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
33698c2ecf20Sopenharmony_ci	if (cmd == NULL)
33708c2ecf20Sopenharmony_ci		return -ENOMEM;
33718c2ecf20Sopenharmony_ci
33728c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD);
33738c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
33748c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
33758c2ecf20Sopenharmony_ci	cmd->threshold = cpu_to_le16(rts_thresh);
33768c2ecf20Sopenharmony_ci
33778c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
33788c2ecf20Sopenharmony_ci	kfree(cmd);
33798c2ecf20Sopenharmony_ci
33808c2ecf20Sopenharmony_ci	return rc;
33818c2ecf20Sopenharmony_ci}
33828c2ecf20Sopenharmony_ci
33838c2ecf20Sopenharmony_ci/*
33848c2ecf20Sopenharmony_ci * CMD_SET_SLOT.
33858c2ecf20Sopenharmony_ci */
33868c2ecf20Sopenharmony_cistruct mwl8k_cmd_set_slot {
33878c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
33888c2ecf20Sopenharmony_ci	__le16 action;
33898c2ecf20Sopenharmony_ci	__u8 short_slot;
33908c2ecf20Sopenharmony_ci} __packed;
33918c2ecf20Sopenharmony_ci
33928c2ecf20Sopenharmony_cistatic int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time)
33938c2ecf20Sopenharmony_ci{
33948c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_slot *cmd;
33958c2ecf20Sopenharmony_ci	int rc;
33968c2ecf20Sopenharmony_ci
33978c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
33988c2ecf20Sopenharmony_ci	if (cmd == NULL)
33998c2ecf20Sopenharmony_ci		return -ENOMEM;
34008c2ecf20Sopenharmony_ci
34018c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT);
34028c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
34038c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
34048c2ecf20Sopenharmony_ci	cmd->short_slot = short_slot_time;
34058c2ecf20Sopenharmony_ci
34068c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
34078c2ecf20Sopenharmony_ci	kfree(cmd);
34088c2ecf20Sopenharmony_ci
34098c2ecf20Sopenharmony_ci	return rc;
34108c2ecf20Sopenharmony_ci}
34118c2ecf20Sopenharmony_ci
34128c2ecf20Sopenharmony_ci/*
34138c2ecf20Sopenharmony_ci * CMD_SET_EDCA_PARAMS.
34148c2ecf20Sopenharmony_ci */
34158c2ecf20Sopenharmony_cistruct mwl8k_cmd_set_edca_params {
34168c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
34178c2ecf20Sopenharmony_ci
34188c2ecf20Sopenharmony_ci	/* See MWL8K_SET_EDCA_XXX below */
34198c2ecf20Sopenharmony_ci	__le16 action;
34208c2ecf20Sopenharmony_ci
34218c2ecf20Sopenharmony_ci	/* TX opportunity in units of 32 us */
34228c2ecf20Sopenharmony_ci	__le16 txop;
34238c2ecf20Sopenharmony_ci
34248c2ecf20Sopenharmony_ci	union {
34258c2ecf20Sopenharmony_ci		struct {
34268c2ecf20Sopenharmony_ci			/* Log exponent of max contention period: 0...15 */
34278c2ecf20Sopenharmony_ci			__le32 log_cw_max;
34288c2ecf20Sopenharmony_ci
34298c2ecf20Sopenharmony_ci			/* Log exponent of min contention period: 0...15 */
34308c2ecf20Sopenharmony_ci			__le32 log_cw_min;
34318c2ecf20Sopenharmony_ci
34328c2ecf20Sopenharmony_ci			/* Adaptive interframe spacing in units of 32us */
34338c2ecf20Sopenharmony_ci			__u8 aifs;
34348c2ecf20Sopenharmony_ci
34358c2ecf20Sopenharmony_ci			/* TX queue to configure */
34368c2ecf20Sopenharmony_ci			__u8 txq;
34378c2ecf20Sopenharmony_ci		} ap;
34388c2ecf20Sopenharmony_ci		struct {
34398c2ecf20Sopenharmony_ci			/* Log exponent of max contention period: 0...15 */
34408c2ecf20Sopenharmony_ci			__u8 log_cw_max;
34418c2ecf20Sopenharmony_ci
34428c2ecf20Sopenharmony_ci			/* Log exponent of min contention period: 0...15 */
34438c2ecf20Sopenharmony_ci			__u8 log_cw_min;
34448c2ecf20Sopenharmony_ci
34458c2ecf20Sopenharmony_ci			/* Adaptive interframe spacing in units of 32us */
34468c2ecf20Sopenharmony_ci			__u8 aifs;
34478c2ecf20Sopenharmony_ci
34488c2ecf20Sopenharmony_ci			/* TX queue to configure */
34498c2ecf20Sopenharmony_ci			__u8 txq;
34508c2ecf20Sopenharmony_ci		} sta;
34518c2ecf20Sopenharmony_ci	};
34528c2ecf20Sopenharmony_ci} __packed;
34538c2ecf20Sopenharmony_ci
34548c2ecf20Sopenharmony_ci#define MWL8K_SET_EDCA_CW	0x01
34558c2ecf20Sopenharmony_ci#define MWL8K_SET_EDCA_TXOP	0x02
34568c2ecf20Sopenharmony_ci#define MWL8K_SET_EDCA_AIFS	0x04
34578c2ecf20Sopenharmony_ci
34588c2ecf20Sopenharmony_ci#define MWL8K_SET_EDCA_ALL	(MWL8K_SET_EDCA_CW | \
34598c2ecf20Sopenharmony_ci				 MWL8K_SET_EDCA_TXOP | \
34608c2ecf20Sopenharmony_ci				 MWL8K_SET_EDCA_AIFS)
34618c2ecf20Sopenharmony_ci
34628c2ecf20Sopenharmony_cistatic int
34638c2ecf20Sopenharmony_cimwl8k_cmd_set_edca_params(struct ieee80211_hw *hw, __u8 qnum,
34648c2ecf20Sopenharmony_ci			  __u16 cw_min, __u16 cw_max,
34658c2ecf20Sopenharmony_ci			  __u8 aifs, __u16 txop)
34668c2ecf20Sopenharmony_ci{
34678c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
34688c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_edca_params *cmd;
34698c2ecf20Sopenharmony_ci	int rc;
34708c2ecf20Sopenharmony_ci
34718c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
34728c2ecf20Sopenharmony_ci	if (cmd == NULL)
34738c2ecf20Sopenharmony_ci		return -ENOMEM;
34748c2ecf20Sopenharmony_ci
34758c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS);
34768c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
34778c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL);
34788c2ecf20Sopenharmony_ci	cmd->txop = cpu_to_le16(txop);
34798c2ecf20Sopenharmony_ci	if (priv->ap_fw) {
34808c2ecf20Sopenharmony_ci		cmd->ap.log_cw_max = cpu_to_le32(ilog2(cw_max + 1));
34818c2ecf20Sopenharmony_ci		cmd->ap.log_cw_min = cpu_to_le32(ilog2(cw_min + 1));
34828c2ecf20Sopenharmony_ci		cmd->ap.aifs = aifs;
34838c2ecf20Sopenharmony_ci		cmd->ap.txq = qnum;
34848c2ecf20Sopenharmony_ci	} else {
34858c2ecf20Sopenharmony_ci		cmd->sta.log_cw_max = (u8)ilog2(cw_max + 1);
34868c2ecf20Sopenharmony_ci		cmd->sta.log_cw_min = (u8)ilog2(cw_min + 1);
34878c2ecf20Sopenharmony_ci		cmd->sta.aifs = aifs;
34888c2ecf20Sopenharmony_ci		cmd->sta.txq = qnum;
34898c2ecf20Sopenharmony_ci	}
34908c2ecf20Sopenharmony_ci
34918c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
34928c2ecf20Sopenharmony_ci	kfree(cmd);
34938c2ecf20Sopenharmony_ci
34948c2ecf20Sopenharmony_ci	return rc;
34958c2ecf20Sopenharmony_ci}
34968c2ecf20Sopenharmony_ci
34978c2ecf20Sopenharmony_ci/*
34988c2ecf20Sopenharmony_ci * CMD_SET_WMM_MODE.
34998c2ecf20Sopenharmony_ci */
35008c2ecf20Sopenharmony_cistruct mwl8k_cmd_set_wmm_mode {
35018c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
35028c2ecf20Sopenharmony_ci	__le16 action;
35038c2ecf20Sopenharmony_ci} __packed;
35048c2ecf20Sopenharmony_ci
35058c2ecf20Sopenharmony_cistatic int mwl8k_cmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable)
35068c2ecf20Sopenharmony_ci{
35078c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
35088c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_wmm_mode *cmd;
35098c2ecf20Sopenharmony_ci	int rc;
35108c2ecf20Sopenharmony_ci
35118c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
35128c2ecf20Sopenharmony_ci	if (cmd == NULL)
35138c2ecf20Sopenharmony_ci		return -ENOMEM;
35148c2ecf20Sopenharmony_ci
35158c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_WMM_MODE);
35168c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
35178c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le16(!!enable);
35188c2ecf20Sopenharmony_ci
35198c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
35208c2ecf20Sopenharmony_ci	kfree(cmd);
35218c2ecf20Sopenharmony_ci
35228c2ecf20Sopenharmony_ci	if (!rc)
35238c2ecf20Sopenharmony_ci		priv->wmm_enabled = enable;
35248c2ecf20Sopenharmony_ci
35258c2ecf20Sopenharmony_ci	return rc;
35268c2ecf20Sopenharmony_ci}
35278c2ecf20Sopenharmony_ci
35288c2ecf20Sopenharmony_ci/*
35298c2ecf20Sopenharmony_ci * CMD_MIMO_CONFIG.
35308c2ecf20Sopenharmony_ci */
35318c2ecf20Sopenharmony_cistruct mwl8k_cmd_mimo_config {
35328c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
35338c2ecf20Sopenharmony_ci	__le32 action;
35348c2ecf20Sopenharmony_ci	__u8 rx_antenna_map;
35358c2ecf20Sopenharmony_ci	__u8 tx_antenna_map;
35368c2ecf20Sopenharmony_ci} __packed;
35378c2ecf20Sopenharmony_ci
35388c2ecf20Sopenharmony_cistatic int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx)
35398c2ecf20Sopenharmony_ci{
35408c2ecf20Sopenharmony_ci	struct mwl8k_cmd_mimo_config *cmd;
35418c2ecf20Sopenharmony_ci	int rc;
35428c2ecf20Sopenharmony_ci
35438c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
35448c2ecf20Sopenharmony_ci	if (cmd == NULL)
35458c2ecf20Sopenharmony_ci		return -ENOMEM;
35468c2ecf20Sopenharmony_ci
35478c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_MIMO_CONFIG);
35488c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
35498c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le32((u32)MWL8K_CMD_SET);
35508c2ecf20Sopenharmony_ci	cmd->rx_antenna_map = rx;
35518c2ecf20Sopenharmony_ci	cmd->tx_antenna_map = tx;
35528c2ecf20Sopenharmony_ci
35538c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
35548c2ecf20Sopenharmony_ci	kfree(cmd);
35558c2ecf20Sopenharmony_ci
35568c2ecf20Sopenharmony_ci	return rc;
35578c2ecf20Sopenharmony_ci}
35588c2ecf20Sopenharmony_ci
35598c2ecf20Sopenharmony_ci/*
35608c2ecf20Sopenharmony_ci * CMD_USE_FIXED_RATE (STA version).
35618c2ecf20Sopenharmony_ci */
35628c2ecf20Sopenharmony_cistruct mwl8k_cmd_use_fixed_rate_sta {
35638c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
35648c2ecf20Sopenharmony_ci	__le32 action;
35658c2ecf20Sopenharmony_ci	__le32 allow_rate_drop;
35668c2ecf20Sopenharmony_ci	__le32 num_rates;
35678c2ecf20Sopenharmony_ci	struct {
35688c2ecf20Sopenharmony_ci		__le32 is_ht_rate;
35698c2ecf20Sopenharmony_ci		__le32 enable_retry;
35708c2ecf20Sopenharmony_ci		__le32 rate;
35718c2ecf20Sopenharmony_ci		__le32 retry_count;
35728c2ecf20Sopenharmony_ci	} rate_entry[8];
35738c2ecf20Sopenharmony_ci	__le32 rate_type;
35748c2ecf20Sopenharmony_ci	__le32 reserved1;
35758c2ecf20Sopenharmony_ci	__le32 reserved2;
35768c2ecf20Sopenharmony_ci} __packed;
35778c2ecf20Sopenharmony_ci
35788c2ecf20Sopenharmony_ci#define MWL8K_USE_AUTO_RATE	0x0002
35798c2ecf20Sopenharmony_ci#define MWL8K_UCAST_RATE	0
35808c2ecf20Sopenharmony_ci
35818c2ecf20Sopenharmony_cistatic int mwl8k_cmd_use_fixed_rate_sta(struct ieee80211_hw *hw)
35828c2ecf20Sopenharmony_ci{
35838c2ecf20Sopenharmony_ci	struct mwl8k_cmd_use_fixed_rate_sta *cmd;
35848c2ecf20Sopenharmony_ci	int rc;
35858c2ecf20Sopenharmony_ci
35868c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
35878c2ecf20Sopenharmony_ci	if (cmd == NULL)
35888c2ecf20Sopenharmony_ci		return -ENOMEM;
35898c2ecf20Sopenharmony_ci
35908c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE);
35918c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
35928c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE);
35938c2ecf20Sopenharmony_ci	cmd->rate_type = cpu_to_le32(MWL8K_UCAST_RATE);
35948c2ecf20Sopenharmony_ci
35958c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
35968c2ecf20Sopenharmony_ci	kfree(cmd);
35978c2ecf20Sopenharmony_ci
35988c2ecf20Sopenharmony_ci	return rc;
35998c2ecf20Sopenharmony_ci}
36008c2ecf20Sopenharmony_ci
36018c2ecf20Sopenharmony_ci/*
36028c2ecf20Sopenharmony_ci * CMD_USE_FIXED_RATE (AP version).
36038c2ecf20Sopenharmony_ci */
36048c2ecf20Sopenharmony_cistruct mwl8k_cmd_use_fixed_rate_ap {
36058c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
36068c2ecf20Sopenharmony_ci	__le32 action;
36078c2ecf20Sopenharmony_ci	__le32 allow_rate_drop;
36088c2ecf20Sopenharmony_ci	__le32 num_rates;
36098c2ecf20Sopenharmony_ci	struct mwl8k_rate_entry_ap {
36108c2ecf20Sopenharmony_ci		__le32 is_ht_rate;
36118c2ecf20Sopenharmony_ci		__le32 enable_retry;
36128c2ecf20Sopenharmony_ci		__le32 rate;
36138c2ecf20Sopenharmony_ci		__le32 retry_count;
36148c2ecf20Sopenharmony_ci	} rate_entry[4];
36158c2ecf20Sopenharmony_ci	u8 multicast_rate;
36168c2ecf20Sopenharmony_ci	u8 multicast_rate_type;
36178c2ecf20Sopenharmony_ci	u8 management_rate;
36188c2ecf20Sopenharmony_ci} __packed;
36198c2ecf20Sopenharmony_ci
36208c2ecf20Sopenharmony_cistatic int
36218c2ecf20Sopenharmony_cimwl8k_cmd_use_fixed_rate_ap(struct ieee80211_hw *hw, int mcast, int mgmt)
36228c2ecf20Sopenharmony_ci{
36238c2ecf20Sopenharmony_ci	struct mwl8k_cmd_use_fixed_rate_ap *cmd;
36248c2ecf20Sopenharmony_ci	int rc;
36258c2ecf20Sopenharmony_ci
36268c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
36278c2ecf20Sopenharmony_ci	if (cmd == NULL)
36288c2ecf20Sopenharmony_ci		return -ENOMEM;
36298c2ecf20Sopenharmony_ci
36308c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE);
36318c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
36328c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE);
36338c2ecf20Sopenharmony_ci	cmd->multicast_rate = mcast;
36348c2ecf20Sopenharmony_ci	cmd->management_rate = mgmt;
36358c2ecf20Sopenharmony_ci
36368c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
36378c2ecf20Sopenharmony_ci	kfree(cmd);
36388c2ecf20Sopenharmony_ci
36398c2ecf20Sopenharmony_ci	return rc;
36408c2ecf20Sopenharmony_ci}
36418c2ecf20Sopenharmony_ci
36428c2ecf20Sopenharmony_ci/*
36438c2ecf20Sopenharmony_ci * CMD_ENABLE_SNIFFER.
36448c2ecf20Sopenharmony_ci */
36458c2ecf20Sopenharmony_cistruct mwl8k_cmd_enable_sniffer {
36468c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
36478c2ecf20Sopenharmony_ci	__le32 action;
36488c2ecf20Sopenharmony_ci} __packed;
36498c2ecf20Sopenharmony_ci
36508c2ecf20Sopenharmony_cistatic int mwl8k_cmd_enable_sniffer(struct ieee80211_hw *hw, bool enable)
36518c2ecf20Sopenharmony_ci{
36528c2ecf20Sopenharmony_ci	struct mwl8k_cmd_enable_sniffer *cmd;
36538c2ecf20Sopenharmony_ci	int rc;
36548c2ecf20Sopenharmony_ci
36558c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
36568c2ecf20Sopenharmony_ci	if (cmd == NULL)
36578c2ecf20Sopenharmony_ci		return -ENOMEM;
36588c2ecf20Sopenharmony_ci
36598c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER);
36608c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
36618c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le32(!!enable);
36628c2ecf20Sopenharmony_ci
36638c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
36648c2ecf20Sopenharmony_ci	kfree(cmd);
36658c2ecf20Sopenharmony_ci
36668c2ecf20Sopenharmony_ci	return rc;
36678c2ecf20Sopenharmony_ci}
36688c2ecf20Sopenharmony_ci
36698c2ecf20Sopenharmony_cistruct mwl8k_cmd_update_mac_addr {
36708c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
36718c2ecf20Sopenharmony_ci	union {
36728c2ecf20Sopenharmony_ci		struct {
36738c2ecf20Sopenharmony_ci			__le16 mac_type;
36748c2ecf20Sopenharmony_ci			__u8 mac_addr[ETH_ALEN];
36758c2ecf20Sopenharmony_ci		} mbss;
36768c2ecf20Sopenharmony_ci		__u8 mac_addr[ETH_ALEN];
36778c2ecf20Sopenharmony_ci	};
36788c2ecf20Sopenharmony_ci} __packed;
36798c2ecf20Sopenharmony_ci
36808c2ecf20Sopenharmony_ci#define MWL8K_MAC_TYPE_PRIMARY_CLIENT		0
36818c2ecf20Sopenharmony_ci#define MWL8K_MAC_TYPE_SECONDARY_CLIENT		1
36828c2ecf20Sopenharmony_ci#define MWL8K_MAC_TYPE_PRIMARY_AP		2
36838c2ecf20Sopenharmony_ci#define MWL8K_MAC_TYPE_SECONDARY_AP		3
36848c2ecf20Sopenharmony_ci
36858c2ecf20Sopenharmony_cistatic int mwl8k_cmd_update_mac_addr(struct ieee80211_hw *hw,
36868c2ecf20Sopenharmony_ci				  struct ieee80211_vif *vif, u8 *mac, bool set)
36878c2ecf20Sopenharmony_ci{
36888c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
36898c2ecf20Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
36908c2ecf20Sopenharmony_ci	struct mwl8k_cmd_update_mac_addr *cmd;
36918c2ecf20Sopenharmony_ci	int mac_type;
36928c2ecf20Sopenharmony_ci	int rc;
36938c2ecf20Sopenharmony_ci
36948c2ecf20Sopenharmony_ci	mac_type = MWL8K_MAC_TYPE_PRIMARY_AP;
36958c2ecf20Sopenharmony_ci	if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) {
36968c2ecf20Sopenharmony_ci		if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported))
36978c2ecf20Sopenharmony_ci			if (priv->ap_fw)
36988c2ecf20Sopenharmony_ci				mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT;
36998c2ecf20Sopenharmony_ci			else
37008c2ecf20Sopenharmony_ci				mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT;
37018c2ecf20Sopenharmony_ci		else
37028c2ecf20Sopenharmony_ci			mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT;
37038c2ecf20Sopenharmony_ci	} else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) {
37048c2ecf20Sopenharmony_ci		if (mwl8k_vif->macid + 1 == ffs(priv->ap_macids_supported))
37058c2ecf20Sopenharmony_ci			mac_type = MWL8K_MAC_TYPE_PRIMARY_AP;
37068c2ecf20Sopenharmony_ci		else
37078c2ecf20Sopenharmony_ci			mac_type = MWL8K_MAC_TYPE_SECONDARY_AP;
37088c2ecf20Sopenharmony_ci	}
37098c2ecf20Sopenharmony_ci
37108c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
37118c2ecf20Sopenharmony_ci	if (cmd == NULL)
37128c2ecf20Sopenharmony_ci		return -ENOMEM;
37138c2ecf20Sopenharmony_ci
37148c2ecf20Sopenharmony_ci	if (set)
37158c2ecf20Sopenharmony_ci		cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR);
37168c2ecf20Sopenharmony_ci	else
37178c2ecf20Sopenharmony_ci		cmd->header.code = cpu_to_le16(MWL8K_CMD_DEL_MAC_ADDR);
37188c2ecf20Sopenharmony_ci
37198c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
37208c2ecf20Sopenharmony_ci	if (priv->ap_fw) {
37218c2ecf20Sopenharmony_ci		cmd->mbss.mac_type = cpu_to_le16(mac_type);
37228c2ecf20Sopenharmony_ci		memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN);
37238c2ecf20Sopenharmony_ci	} else {
37248c2ecf20Sopenharmony_ci		memcpy(cmd->mac_addr, mac, ETH_ALEN);
37258c2ecf20Sopenharmony_ci	}
37268c2ecf20Sopenharmony_ci
37278c2ecf20Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
37288c2ecf20Sopenharmony_ci	kfree(cmd);
37298c2ecf20Sopenharmony_ci
37308c2ecf20Sopenharmony_ci	return rc;
37318c2ecf20Sopenharmony_ci}
37328c2ecf20Sopenharmony_ci
37338c2ecf20Sopenharmony_ci/*
37348c2ecf20Sopenharmony_ci * MWL8K_CMD_SET_MAC_ADDR.
37358c2ecf20Sopenharmony_ci */
37368c2ecf20Sopenharmony_cistatic inline int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw,
37378c2ecf20Sopenharmony_ci				  struct ieee80211_vif *vif, u8 *mac)
37388c2ecf20Sopenharmony_ci{
37398c2ecf20Sopenharmony_ci	return mwl8k_cmd_update_mac_addr(hw, vif, mac, true);
37408c2ecf20Sopenharmony_ci}
37418c2ecf20Sopenharmony_ci
37428c2ecf20Sopenharmony_ci/*
37438c2ecf20Sopenharmony_ci * MWL8K_CMD_DEL_MAC_ADDR.
37448c2ecf20Sopenharmony_ci */
37458c2ecf20Sopenharmony_cistatic inline int mwl8k_cmd_del_mac_addr(struct ieee80211_hw *hw,
37468c2ecf20Sopenharmony_ci				  struct ieee80211_vif *vif, u8 *mac)
37478c2ecf20Sopenharmony_ci{
37488c2ecf20Sopenharmony_ci	return mwl8k_cmd_update_mac_addr(hw, vif, mac, false);
37498c2ecf20Sopenharmony_ci}
37508c2ecf20Sopenharmony_ci
37518c2ecf20Sopenharmony_ci/*
37528c2ecf20Sopenharmony_ci * CMD_SET_RATEADAPT_MODE.
37538c2ecf20Sopenharmony_ci */
37548c2ecf20Sopenharmony_cistruct mwl8k_cmd_set_rate_adapt_mode {
37558c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
37568c2ecf20Sopenharmony_ci	__le16 action;
37578c2ecf20Sopenharmony_ci	__le16 mode;
37588c2ecf20Sopenharmony_ci} __packed;
37598c2ecf20Sopenharmony_ci
37608c2ecf20Sopenharmony_cistatic int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode)
37618c2ecf20Sopenharmony_ci{
37628c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_rate_adapt_mode *cmd;
37638c2ecf20Sopenharmony_ci	int rc;
37648c2ecf20Sopenharmony_ci
37658c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
37668c2ecf20Sopenharmony_ci	if (cmd == NULL)
37678c2ecf20Sopenharmony_ci		return -ENOMEM;
37688c2ecf20Sopenharmony_ci
37698c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATEADAPT_MODE);
37708c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
37718c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_CMD_SET);
37728c2ecf20Sopenharmony_ci	cmd->mode = cpu_to_le16(mode);
37738c2ecf20Sopenharmony_ci
37748c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
37758c2ecf20Sopenharmony_ci	kfree(cmd);
37768c2ecf20Sopenharmony_ci
37778c2ecf20Sopenharmony_ci	return rc;
37788c2ecf20Sopenharmony_ci}
37798c2ecf20Sopenharmony_ci
37808c2ecf20Sopenharmony_ci/*
37818c2ecf20Sopenharmony_ci * CMD_GET_WATCHDOG_BITMAP.
37828c2ecf20Sopenharmony_ci */
37838c2ecf20Sopenharmony_cistruct mwl8k_cmd_get_watchdog_bitmap {
37848c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
37858c2ecf20Sopenharmony_ci	u8	bitmap;
37868c2ecf20Sopenharmony_ci} __packed;
37878c2ecf20Sopenharmony_ci
37888c2ecf20Sopenharmony_cistatic int mwl8k_cmd_get_watchdog_bitmap(struct ieee80211_hw *hw, u8 *bitmap)
37898c2ecf20Sopenharmony_ci{
37908c2ecf20Sopenharmony_ci	struct mwl8k_cmd_get_watchdog_bitmap *cmd;
37918c2ecf20Sopenharmony_ci	int rc;
37928c2ecf20Sopenharmony_ci
37938c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
37948c2ecf20Sopenharmony_ci	if (cmd == NULL)
37958c2ecf20Sopenharmony_ci		return -ENOMEM;
37968c2ecf20Sopenharmony_ci
37978c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_WATCHDOG_BITMAP);
37988c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
37998c2ecf20Sopenharmony_ci
38008c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
38018c2ecf20Sopenharmony_ci	if (!rc)
38028c2ecf20Sopenharmony_ci		*bitmap = cmd->bitmap;
38038c2ecf20Sopenharmony_ci
38048c2ecf20Sopenharmony_ci	kfree(cmd);
38058c2ecf20Sopenharmony_ci
38068c2ecf20Sopenharmony_ci	return rc;
38078c2ecf20Sopenharmony_ci}
38088c2ecf20Sopenharmony_ci
38098c2ecf20Sopenharmony_ci#define MWL8K_WMM_QUEUE_NUMBER	3
38108c2ecf20Sopenharmony_ci
38118c2ecf20Sopenharmony_cistatic void mwl8k_destroy_ba(struct ieee80211_hw *hw,
38128c2ecf20Sopenharmony_ci			     u8 idx);
38138c2ecf20Sopenharmony_ci
38148c2ecf20Sopenharmony_cistatic void mwl8k_watchdog_ba_events(struct work_struct *work)
38158c2ecf20Sopenharmony_ci{
38168c2ecf20Sopenharmony_ci	int rc;
38178c2ecf20Sopenharmony_ci	u8 bitmap = 0, stream_index;
38188c2ecf20Sopenharmony_ci	struct mwl8k_ampdu_stream *streams;
38198c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv =
38208c2ecf20Sopenharmony_ci		container_of(work, struct mwl8k_priv, watchdog_ba_handle);
38218c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = priv->hw;
38228c2ecf20Sopenharmony_ci	int i;
38238c2ecf20Sopenharmony_ci	u32 status = 0;
38248c2ecf20Sopenharmony_ci
38258c2ecf20Sopenharmony_ci	mwl8k_fw_lock(hw);
38268c2ecf20Sopenharmony_ci
38278c2ecf20Sopenharmony_ci	rc = mwl8k_cmd_get_watchdog_bitmap(priv->hw, &bitmap);
38288c2ecf20Sopenharmony_ci	if (rc)
38298c2ecf20Sopenharmony_ci		goto done;
38308c2ecf20Sopenharmony_ci
38318c2ecf20Sopenharmony_ci	spin_lock(&priv->stream_lock);
38328c2ecf20Sopenharmony_ci
38338c2ecf20Sopenharmony_ci	/* the bitmap is the hw queue number.  Map it to the ampdu queue. */
38348c2ecf20Sopenharmony_ci	for (i = 0; i < TOTAL_HW_TX_QUEUES; i++) {
38358c2ecf20Sopenharmony_ci		if (bitmap & (1 << i)) {
38368c2ecf20Sopenharmony_ci			stream_index = (i + MWL8K_WMM_QUEUE_NUMBER) %
38378c2ecf20Sopenharmony_ci				       TOTAL_HW_TX_QUEUES;
38388c2ecf20Sopenharmony_ci			streams = &priv->ampdu[stream_index];
38398c2ecf20Sopenharmony_ci			if (streams->state == AMPDU_STREAM_ACTIVE) {
38408c2ecf20Sopenharmony_ci				ieee80211_stop_tx_ba_session(streams->sta,
38418c2ecf20Sopenharmony_ci							     streams->tid);
38428c2ecf20Sopenharmony_ci				spin_unlock(&priv->stream_lock);
38438c2ecf20Sopenharmony_ci				mwl8k_destroy_ba(hw, stream_index);
38448c2ecf20Sopenharmony_ci				spin_lock(&priv->stream_lock);
38458c2ecf20Sopenharmony_ci			}
38468c2ecf20Sopenharmony_ci		}
38478c2ecf20Sopenharmony_ci	}
38488c2ecf20Sopenharmony_ci
38498c2ecf20Sopenharmony_ci	spin_unlock(&priv->stream_lock);
38508c2ecf20Sopenharmony_cidone:
38518c2ecf20Sopenharmony_ci	atomic_dec(&priv->watchdog_event_pending);
38528c2ecf20Sopenharmony_ci	status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
38538c2ecf20Sopenharmony_ci	iowrite32((status | MWL8K_A2H_INT_BA_WATCHDOG),
38548c2ecf20Sopenharmony_ci		  priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
38558c2ecf20Sopenharmony_ci	mwl8k_fw_unlock(hw);
38568c2ecf20Sopenharmony_ci	return;
38578c2ecf20Sopenharmony_ci}
38588c2ecf20Sopenharmony_ci
38598c2ecf20Sopenharmony_ci
38608c2ecf20Sopenharmony_ci/*
38618c2ecf20Sopenharmony_ci * CMD_BSS_START.
38628c2ecf20Sopenharmony_ci */
38638c2ecf20Sopenharmony_cistruct mwl8k_cmd_bss_start {
38648c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
38658c2ecf20Sopenharmony_ci	__le32 enable;
38668c2ecf20Sopenharmony_ci} __packed;
38678c2ecf20Sopenharmony_ci
38688c2ecf20Sopenharmony_cistatic int mwl8k_cmd_bss_start(struct ieee80211_hw *hw,
38698c2ecf20Sopenharmony_ci			       struct ieee80211_vif *vif, int enable)
38708c2ecf20Sopenharmony_ci{
38718c2ecf20Sopenharmony_ci	struct mwl8k_cmd_bss_start *cmd;
38728c2ecf20Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
38738c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
38748c2ecf20Sopenharmony_ci	int rc;
38758c2ecf20Sopenharmony_ci
38768c2ecf20Sopenharmony_ci	if (enable && (priv->running_bsses & (1 << mwl8k_vif->macid)))
38778c2ecf20Sopenharmony_ci		return 0;
38788c2ecf20Sopenharmony_ci
38798c2ecf20Sopenharmony_ci	if (!enable && !(priv->running_bsses & (1 << mwl8k_vif->macid)))
38808c2ecf20Sopenharmony_ci		return 0;
38818c2ecf20Sopenharmony_ci
38828c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
38838c2ecf20Sopenharmony_ci	if (cmd == NULL)
38848c2ecf20Sopenharmony_ci		return -ENOMEM;
38858c2ecf20Sopenharmony_ci
38868c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_BSS_START);
38878c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
38888c2ecf20Sopenharmony_ci	cmd->enable = cpu_to_le32(enable);
38898c2ecf20Sopenharmony_ci
38908c2ecf20Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
38918c2ecf20Sopenharmony_ci	kfree(cmd);
38928c2ecf20Sopenharmony_ci
38938c2ecf20Sopenharmony_ci	if (!rc) {
38948c2ecf20Sopenharmony_ci		if (enable)
38958c2ecf20Sopenharmony_ci			priv->running_bsses |= (1 << mwl8k_vif->macid);
38968c2ecf20Sopenharmony_ci		else
38978c2ecf20Sopenharmony_ci			priv->running_bsses &= ~(1 << mwl8k_vif->macid);
38988c2ecf20Sopenharmony_ci	}
38998c2ecf20Sopenharmony_ci	return rc;
39008c2ecf20Sopenharmony_ci}
39018c2ecf20Sopenharmony_ci
39028c2ecf20Sopenharmony_cistatic void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable, u32 bitmap)
39038c2ecf20Sopenharmony_ci{
39048c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
39058c2ecf20Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif, *tmp_vif;
39068c2ecf20Sopenharmony_ci	struct ieee80211_vif *vif;
39078c2ecf20Sopenharmony_ci
39088c2ecf20Sopenharmony_ci	list_for_each_entry_safe(mwl8k_vif, tmp_vif, &priv->vif_list, list) {
39098c2ecf20Sopenharmony_ci		vif = mwl8k_vif->vif;
39108c2ecf20Sopenharmony_ci
39118c2ecf20Sopenharmony_ci		if (!(bitmap & (1 << mwl8k_vif->macid)))
39128c2ecf20Sopenharmony_ci			continue;
39138c2ecf20Sopenharmony_ci
39148c2ecf20Sopenharmony_ci		if (vif->type == NL80211_IFTYPE_AP)
39158c2ecf20Sopenharmony_ci			mwl8k_cmd_bss_start(hw, vif, enable);
39168c2ecf20Sopenharmony_ci	}
39178c2ecf20Sopenharmony_ci}
39188c2ecf20Sopenharmony_ci/*
39198c2ecf20Sopenharmony_ci * CMD_BASTREAM.
39208c2ecf20Sopenharmony_ci */
39218c2ecf20Sopenharmony_ci
39228c2ecf20Sopenharmony_ci/*
39238c2ecf20Sopenharmony_ci * UPSTREAM is tx direction
39248c2ecf20Sopenharmony_ci */
39258c2ecf20Sopenharmony_ci#define BASTREAM_FLAG_DIRECTION_UPSTREAM	0x00
39268c2ecf20Sopenharmony_ci#define BASTREAM_FLAG_IMMEDIATE_TYPE		0x01
39278c2ecf20Sopenharmony_ci
39288c2ecf20Sopenharmony_cienum ba_stream_action_type {
39298c2ecf20Sopenharmony_ci	MWL8K_BA_CREATE,
39308c2ecf20Sopenharmony_ci	MWL8K_BA_UPDATE,
39318c2ecf20Sopenharmony_ci	MWL8K_BA_DESTROY,
39328c2ecf20Sopenharmony_ci	MWL8K_BA_FLUSH,
39338c2ecf20Sopenharmony_ci	MWL8K_BA_CHECK,
39348c2ecf20Sopenharmony_ci};
39358c2ecf20Sopenharmony_ci
39368c2ecf20Sopenharmony_ci
39378c2ecf20Sopenharmony_cistruct mwl8k_create_ba_stream {
39388c2ecf20Sopenharmony_ci	__le32	flags;
39398c2ecf20Sopenharmony_ci	__le32	idle_thrs;
39408c2ecf20Sopenharmony_ci	__le32	bar_thrs;
39418c2ecf20Sopenharmony_ci	__le32	window_size;
39428c2ecf20Sopenharmony_ci	u8	peer_mac_addr[6];
39438c2ecf20Sopenharmony_ci	u8	dialog_token;
39448c2ecf20Sopenharmony_ci	u8	tid;
39458c2ecf20Sopenharmony_ci	u8	queue_id;
39468c2ecf20Sopenharmony_ci	u8	param_info;
39478c2ecf20Sopenharmony_ci	__le32	ba_context;
39488c2ecf20Sopenharmony_ci	u8	reset_seq_no_flag;
39498c2ecf20Sopenharmony_ci	__le16	curr_seq_no;
39508c2ecf20Sopenharmony_ci	u8	sta_src_mac_addr[6];
39518c2ecf20Sopenharmony_ci} __packed;
39528c2ecf20Sopenharmony_ci
39538c2ecf20Sopenharmony_cistruct mwl8k_destroy_ba_stream {
39548c2ecf20Sopenharmony_ci	__le32	flags;
39558c2ecf20Sopenharmony_ci	__le32	ba_context;
39568c2ecf20Sopenharmony_ci} __packed;
39578c2ecf20Sopenharmony_ci
39588c2ecf20Sopenharmony_cistruct mwl8k_cmd_bastream {
39598c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt	header;
39608c2ecf20Sopenharmony_ci	__le32	action;
39618c2ecf20Sopenharmony_ci	union {
39628c2ecf20Sopenharmony_ci		struct mwl8k_create_ba_stream	create_params;
39638c2ecf20Sopenharmony_ci		struct mwl8k_destroy_ba_stream	destroy_params;
39648c2ecf20Sopenharmony_ci	};
39658c2ecf20Sopenharmony_ci} __packed;
39668c2ecf20Sopenharmony_ci
39678c2ecf20Sopenharmony_cistatic int
39688c2ecf20Sopenharmony_cimwl8k_check_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream,
39698c2ecf20Sopenharmony_ci	       struct ieee80211_vif *vif)
39708c2ecf20Sopenharmony_ci{
39718c2ecf20Sopenharmony_ci	struct mwl8k_cmd_bastream *cmd;
39728c2ecf20Sopenharmony_ci	int rc;
39738c2ecf20Sopenharmony_ci
39748c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
39758c2ecf20Sopenharmony_ci	if (cmd == NULL)
39768c2ecf20Sopenharmony_ci		return -ENOMEM;
39778c2ecf20Sopenharmony_ci
39788c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM);
39798c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
39808c2ecf20Sopenharmony_ci
39818c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_BA_CHECK);
39828c2ecf20Sopenharmony_ci
39838c2ecf20Sopenharmony_ci	cmd->create_params.queue_id = stream->idx;
39848c2ecf20Sopenharmony_ci	memcpy(&cmd->create_params.peer_mac_addr[0], stream->sta->addr,
39858c2ecf20Sopenharmony_ci	       ETH_ALEN);
39868c2ecf20Sopenharmony_ci	cmd->create_params.tid = stream->tid;
39878c2ecf20Sopenharmony_ci
39888c2ecf20Sopenharmony_ci	cmd->create_params.flags =
39898c2ecf20Sopenharmony_ci		cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE) |
39908c2ecf20Sopenharmony_ci		cpu_to_le32(BASTREAM_FLAG_DIRECTION_UPSTREAM);
39918c2ecf20Sopenharmony_ci
39928c2ecf20Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
39938c2ecf20Sopenharmony_ci
39948c2ecf20Sopenharmony_ci	kfree(cmd);
39958c2ecf20Sopenharmony_ci
39968c2ecf20Sopenharmony_ci	return rc;
39978c2ecf20Sopenharmony_ci}
39988c2ecf20Sopenharmony_ci
39998c2ecf20Sopenharmony_cistatic int
40008c2ecf20Sopenharmony_cimwl8k_create_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream,
40018c2ecf20Sopenharmony_ci		u8 buf_size, struct ieee80211_vif *vif)
40028c2ecf20Sopenharmony_ci{
40038c2ecf20Sopenharmony_ci	struct mwl8k_cmd_bastream *cmd;
40048c2ecf20Sopenharmony_ci	int rc;
40058c2ecf20Sopenharmony_ci
40068c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
40078c2ecf20Sopenharmony_ci	if (cmd == NULL)
40088c2ecf20Sopenharmony_ci		return -ENOMEM;
40098c2ecf20Sopenharmony_ci
40108c2ecf20Sopenharmony_ci
40118c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM);
40128c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
40138c2ecf20Sopenharmony_ci
40148c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_BA_CREATE);
40158c2ecf20Sopenharmony_ci
40168c2ecf20Sopenharmony_ci	cmd->create_params.bar_thrs = cpu_to_le32((u32)buf_size);
40178c2ecf20Sopenharmony_ci	cmd->create_params.window_size = cpu_to_le32((u32)buf_size);
40188c2ecf20Sopenharmony_ci	cmd->create_params.queue_id = stream->idx;
40198c2ecf20Sopenharmony_ci
40208c2ecf20Sopenharmony_ci	memcpy(cmd->create_params.peer_mac_addr, stream->sta->addr, ETH_ALEN);
40218c2ecf20Sopenharmony_ci	cmd->create_params.tid = stream->tid;
40228c2ecf20Sopenharmony_ci	cmd->create_params.curr_seq_no = cpu_to_le16(0);
40238c2ecf20Sopenharmony_ci	cmd->create_params.reset_seq_no_flag = 1;
40248c2ecf20Sopenharmony_ci
40258c2ecf20Sopenharmony_ci	cmd->create_params.param_info =
40268c2ecf20Sopenharmony_ci		(stream->sta->ht_cap.ampdu_factor &
40278c2ecf20Sopenharmony_ci		 IEEE80211_HT_AMPDU_PARM_FACTOR) |
40288c2ecf20Sopenharmony_ci		((stream->sta->ht_cap.ampdu_density << 2) &
40298c2ecf20Sopenharmony_ci		 IEEE80211_HT_AMPDU_PARM_DENSITY);
40308c2ecf20Sopenharmony_ci
40318c2ecf20Sopenharmony_ci	cmd->create_params.flags =
40328c2ecf20Sopenharmony_ci		cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE |
40338c2ecf20Sopenharmony_ci					BASTREAM_FLAG_DIRECTION_UPSTREAM);
40348c2ecf20Sopenharmony_ci
40358c2ecf20Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
40368c2ecf20Sopenharmony_ci
40378c2ecf20Sopenharmony_ci	wiphy_debug(hw->wiphy, "Created a BA stream for %pM : tid %d\n",
40388c2ecf20Sopenharmony_ci		stream->sta->addr, stream->tid);
40398c2ecf20Sopenharmony_ci	kfree(cmd);
40408c2ecf20Sopenharmony_ci
40418c2ecf20Sopenharmony_ci	return rc;
40428c2ecf20Sopenharmony_ci}
40438c2ecf20Sopenharmony_ci
40448c2ecf20Sopenharmony_cistatic void mwl8k_destroy_ba(struct ieee80211_hw *hw,
40458c2ecf20Sopenharmony_ci			     u8 idx)
40468c2ecf20Sopenharmony_ci{
40478c2ecf20Sopenharmony_ci	struct mwl8k_cmd_bastream *cmd;
40488c2ecf20Sopenharmony_ci
40498c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
40508c2ecf20Sopenharmony_ci	if (cmd == NULL)
40518c2ecf20Sopenharmony_ci		return;
40528c2ecf20Sopenharmony_ci
40538c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM);
40548c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
40558c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_BA_DESTROY);
40568c2ecf20Sopenharmony_ci
40578c2ecf20Sopenharmony_ci	cmd->destroy_params.ba_context = cpu_to_le32(idx);
40588c2ecf20Sopenharmony_ci	mwl8k_post_cmd(hw, &cmd->header);
40598c2ecf20Sopenharmony_ci
40608c2ecf20Sopenharmony_ci	wiphy_debug(hw->wiphy, "Deleted BA stream index %d\n", idx);
40618c2ecf20Sopenharmony_ci
40628c2ecf20Sopenharmony_ci	kfree(cmd);
40638c2ecf20Sopenharmony_ci}
40648c2ecf20Sopenharmony_ci
40658c2ecf20Sopenharmony_ci/*
40668c2ecf20Sopenharmony_ci * CMD_SET_NEW_STN.
40678c2ecf20Sopenharmony_ci */
40688c2ecf20Sopenharmony_cistruct mwl8k_cmd_set_new_stn {
40698c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
40708c2ecf20Sopenharmony_ci	__le16 aid;
40718c2ecf20Sopenharmony_ci	__u8 mac_addr[6];
40728c2ecf20Sopenharmony_ci	__le16 stn_id;
40738c2ecf20Sopenharmony_ci	__le16 action;
40748c2ecf20Sopenharmony_ci	__le16 rsvd;
40758c2ecf20Sopenharmony_ci	__le32 legacy_rates;
40768c2ecf20Sopenharmony_ci	__u8 ht_rates[4];
40778c2ecf20Sopenharmony_ci	__le16 cap_info;
40788c2ecf20Sopenharmony_ci	__le16 ht_capabilities_info;
40798c2ecf20Sopenharmony_ci	__u8 mac_ht_param_info;
40808c2ecf20Sopenharmony_ci	__u8 rev;
40818c2ecf20Sopenharmony_ci	__u8 control_channel;
40828c2ecf20Sopenharmony_ci	__u8 add_channel;
40838c2ecf20Sopenharmony_ci	__le16 op_mode;
40848c2ecf20Sopenharmony_ci	__le16 stbc;
40858c2ecf20Sopenharmony_ci	__u8 add_qos_info;
40868c2ecf20Sopenharmony_ci	__u8 is_qos_sta;
40878c2ecf20Sopenharmony_ci	__le32 fw_sta_ptr;
40888c2ecf20Sopenharmony_ci} __packed;
40898c2ecf20Sopenharmony_ci
40908c2ecf20Sopenharmony_ci#define MWL8K_STA_ACTION_ADD		0
40918c2ecf20Sopenharmony_ci#define MWL8K_STA_ACTION_REMOVE		2
40928c2ecf20Sopenharmony_ci
40938c2ecf20Sopenharmony_cistatic int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw,
40948c2ecf20Sopenharmony_ci				     struct ieee80211_vif *vif,
40958c2ecf20Sopenharmony_ci				     struct ieee80211_sta *sta)
40968c2ecf20Sopenharmony_ci{
40978c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_new_stn *cmd;
40988c2ecf20Sopenharmony_ci	u32 rates;
40998c2ecf20Sopenharmony_ci	int rc;
41008c2ecf20Sopenharmony_ci
41018c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
41028c2ecf20Sopenharmony_ci	if (cmd == NULL)
41038c2ecf20Sopenharmony_ci		return -ENOMEM;
41048c2ecf20Sopenharmony_ci
41058c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
41068c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
41078c2ecf20Sopenharmony_ci	cmd->aid = cpu_to_le16(sta->aid);
41088c2ecf20Sopenharmony_ci	memcpy(cmd->mac_addr, sta->addr, ETH_ALEN);
41098c2ecf20Sopenharmony_ci	cmd->stn_id = cpu_to_le16(sta->aid);
41108c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_STA_ACTION_ADD);
41118c2ecf20Sopenharmony_ci	if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ)
41128c2ecf20Sopenharmony_ci		rates = sta->supp_rates[NL80211_BAND_2GHZ];
41138c2ecf20Sopenharmony_ci	else
41148c2ecf20Sopenharmony_ci		rates = sta->supp_rates[NL80211_BAND_5GHZ] << 5;
41158c2ecf20Sopenharmony_ci	cmd->legacy_rates = cpu_to_le32(rates);
41168c2ecf20Sopenharmony_ci	if (sta->ht_cap.ht_supported) {
41178c2ecf20Sopenharmony_ci		cmd->ht_rates[0] = sta->ht_cap.mcs.rx_mask[0];
41188c2ecf20Sopenharmony_ci		cmd->ht_rates[1] = sta->ht_cap.mcs.rx_mask[1];
41198c2ecf20Sopenharmony_ci		cmd->ht_rates[2] = sta->ht_cap.mcs.rx_mask[2];
41208c2ecf20Sopenharmony_ci		cmd->ht_rates[3] = sta->ht_cap.mcs.rx_mask[3];
41218c2ecf20Sopenharmony_ci		cmd->ht_capabilities_info = cpu_to_le16(sta->ht_cap.cap);
41228c2ecf20Sopenharmony_ci		cmd->mac_ht_param_info = (sta->ht_cap.ampdu_factor & 3) |
41238c2ecf20Sopenharmony_ci			((sta->ht_cap.ampdu_density & 7) << 2);
41248c2ecf20Sopenharmony_ci		cmd->is_qos_sta = 1;
41258c2ecf20Sopenharmony_ci	}
41268c2ecf20Sopenharmony_ci
41278c2ecf20Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
41288c2ecf20Sopenharmony_ci	kfree(cmd);
41298c2ecf20Sopenharmony_ci
41308c2ecf20Sopenharmony_ci	return rc;
41318c2ecf20Sopenharmony_ci}
41328c2ecf20Sopenharmony_ci
41338c2ecf20Sopenharmony_cistatic int mwl8k_cmd_set_new_stn_add_self(struct ieee80211_hw *hw,
41348c2ecf20Sopenharmony_ci					  struct ieee80211_vif *vif)
41358c2ecf20Sopenharmony_ci{
41368c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_new_stn *cmd;
41378c2ecf20Sopenharmony_ci	int rc;
41388c2ecf20Sopenharmony_ci
41398c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
41408c2ecf20Sopenharmony_ci	if (cmd == NULL)
41418c2ecf20Sopenharmony_ci		return -ENOMEM;
41428c2ecf20Sopenharmony_ci
41438c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
41448c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
41458c2ecf20Sopenharmony_ci	memcpy(cmd->mac_addr, vif->addr, ETH_ALEN);
41468c2ecf20Sopenharmony_ci
41478c2ecf20Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
41488c2ecf20Sopenharmony_ci	kfree(cmd);
41498c2ecf20Sopenharmony_ci
41508c2ecf20Sopenharmony_ci	return rc;
41518c2ecf20Sopenharmony_ci}
41528c2ecf20Sopenharmony_ci
41538c2ecf20Sopenharmony_cistatic int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw,
41548c2ecf20Sopenharmony_ci				     struct ieee80211_vif *vif, u8 *addr)
41558c2ecf20Sopenharmony_ci{
41568c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_new_stn *cmd;
41578c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
41588c2ecf20Sopenharmony_ci	int rc, i;
41598c2ecf20Sopenharmony_ci	u8 idx;
41608c2ecf20Sopenharmony_ci
41618c2ecf20Sopenharmony_ci	spin_lock(&priv->stream_lock);
41628c2ecf20Sopenharmony_ci	/* Destroy any active ampdu streams for this sta */
41638c2ecf20Sopenharmony_ci	for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) {
41648c2ecf20Sopenharmony_ci		struct mwl8k_ampdu_stream *s;
41658c2ecf20Sopenharmony_ci		s = &priv->ampdu[i];
41668c2ecf20Sopenharmony_ci		if (s->state != AMPDU_NO_STREAM) {
41678c2ecf20Sopenharmony_ci			if (memcmp(s->sta->addr, addr, ETH_ALEN) == 0) {
41688c2ecf20Sopenharmony_ci				if (s->state == AMPDU_STREAM_ACTIVE) {
41698c2ecf20Sopenharmony_ci					idx = s->idx;
41708c2ecf20Sopenharmony_ci					spin_unlock(&priv->stream_lock);
41718c2ecf20Sopenharmony_ci					mwl8k_destroy_ba(hw, idx);
41728c2ecf20Sopenharmony_ci					spin_lock(&priv->stream_lock);
41738c2ecf20Sopenharmony_ci				} else if (s->state == AMPDU_STREAM_NEW) {
41748c2ecf20Sopenharmony_ci					mwl8k_remove_stream(hw, s);
41758c2ecf20Sopenharmony_ci				}
41768c2ecf20Sopenharmony_ci			}
41778c2ecf20Sopenharmony_ci		}
41788c2ecf20Sopenharmony_ci	}
41798c2ecf20Sopenharmony_ci
41808c2ecf20Sopenharmony_ci	spin_unlock(&priv->stream_lock);
41818c2ecf20Sopenharmony_ci
41828c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
41838c2ecf20Sopenharmony_ci	if (cmd == NULL)
41848c2ecf20Sopenharmony_ci		return -ENOMEM;
41858c2ecf20Sopenharmony_ci
41868c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN);
41878c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
41888c2ecf20Sopenharmony_ci	memcpy(cmd->mac_addr, addr, ETH_ALEN);
41898c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le16(MWL8K_STA_ACTION_REMOVE);
41908c2ecf20Sopenharmony_ci
41918c2ecf20Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
41928c2ecf20Sopenharmony_ci	kfree(cmd);
41938c2ecf20Sopenharmony_ci
41948c2ecf20Sopenharmony_ci	return rc;
41958c2ecf20Sopenharmony_ci}
41968c2ecf20Sopenharmony_ci
41978c2ecf20Sopenharmony_ci/*
41988c2ecf20Sopenharmony_ci * CMD_UPDATE_ENCRYPTION.
41998c2ecf20Sopenharmony_ci */
42008c2ecf20Sopenharmony_ci
42018c2ecf20Sopenharmony_ci#define MAX_ENCR_KEY_LENGTH	16
42028c2ecf20Sopenharmony_ci#define MIC_KEY_LENGTH		8
42038c2ecf20Sopenharmony_ci
42048c2ecf20Sopenharmony_cistruct mwl8k_cmd_update_encryption {
42058c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
42068c2ecf20Sopenharmony_ci
42078c2ecf20Sopenharmony_ci	__le32 action;
42088c2ecf20Sopenharmony_ci	__le32 reserved;
42098c2ecf20Sopenharmony_ci	__u8 mac_addr[6];
42108c2ecf20Sopenharmony_ci	__u8 encr_type;
42118c2ecf20Sopenharmony_ci
42128c2ecf20Sopenharmony_ci} __packed;
42138c2ecf20Sopenharmony_ci
42148c2ecf20Sopenharmony_cistruct mwl8k_cmd_set_key {
42158c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
42168c2ecf20Sopenharmony_ci
42178c2ecf20Sopenharmony_ci	__le32 action;
42188c2ecf20Sopenharmony_ci	__le32 reserved;
42198c2ecf20Sopenharmony_ci	__le16 length;
42208c2ecf20Sopenharmony_ci	__le16 key_type_id;
42218c2ecf20Sopenharmony_ci	__le32 key_info;
42228c2ecf20Sopenharmony_ci	__le32 key_id;
42238c2ecf20Sopenharmony_ci	__le16 key_len;
42248c2ecf20Sopenharmony_ci	__u8 key_material[MAX_ENCR_KEY_LENGTH];
42258c2ecf20Sopenharmony_ci	__u8 tkip_tx_mic_key[MIC_KEY_LENGTH];
42268c2ecf20Sopenharmony_ci	__u8 tkip_rx_mic_key[MIC_KEY_LENGTH];
42278c2ecf20Sopenharmony_ci	__le16 tkip_rsc_low;
42288c2ecf20Sopenharmony_ci	__le32 tkip_rsc_high;
42298c2ecf20Sopenharmony_ci	__le16 tkip_tsc_low;
42308c2ecf20Sopenharmony_ci	__le32 tkip_tsc_high;
42318c2ecf20Sopenharmony_ci	__u8 mac_addr[6];
42328c2ecf20Sopenharmony_ci} __packed;
42338c2ecf20Sopenharmony_ci
42348c2ecf20Sopenharmony_cienum {
42358c2ecf20Sopenharmony_ci	MWL8K_ENCR_ENABLE,
42368c2ecf20Sopenharmony_ci	MWL8K_ENCR_SET_KEY,
42378c2ecf20Sopenharmony_ci	MWL8K_ENCR_REMOVE_KEY,
42388c2ecf20Sopenharmony_ci	MWL8K_ENCR_SET_GROUP_KEY,
42398c2ecf20Sopenharmony_ci};
42408c2ecf20Sopenharmony_ci
42418c2ecf20Sopenharmony_ci#define MWL8K_UPDATE_ENCRYPTION_TYPE_WEP	0
42428c2ecf20Sopenharmony_ci#define MWL8K_UPDATE_ENCRYPTION_TYPE_DISABLE	1
42438c2ecf20Sopenharmony_ci#define MWL8K_UPDATE_ENCRYPTION_TYPE_TKIP	4
42448c2ecf20Sopenharmony_ci#define MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED	7
42458c2ecf20Sopenharmony_ci#define MWL8K_UPDATE_ENCRYPTION_TYPE_AES	8
42468c2ecf20Sopenharmony_ci
42478c2ecf20Sopenharmony_cienum {
42488c2ecf20Sopenharmony_ci	MWL8K_ALG_WEP,
42498c2ecf20Sopenharmony_ci	MWL8K_ALG_TKIP,
42508c2ecf20Sopenharmony_ci	MWL8K_ALG_CCMP,
42518c2ecf20Sopenharmony_ci};
42528c2ecf20Sopenharmony_ci
42538c2ecf20Sopenharmony_ci#define MWL8K_KEY_FLAG_TXGROUPKEY	0x00000004
42548c2ecf20Sopenharmony_ci#define MWL8K_KEY_FLAG_PAIRWISE		0x00000008
42558c2ecf20Sopenharmony_ci#define MWL8K_KEY_FLAG_TSC_VALID	0x00000040
42568c2ecf20Sopenharmony_ci#define MWL8K_KEY_FLAG_WEP_TXKEY	0x01000000
42578c2ecf20Sopenharmony_ci#define MWL8K_KEY_FLAG_MICKEY_VALID	0x02000000
42588c2ecf20Sopenharmony_ci
42598c2ecf20Sopenharmony_cistatic int mwl8k_cmd_update_encryption_enable(struct ieee80211_hw *hw,
42608c2ecf20Sopenharmony_ci					      struct ieee80211_vif *vif,
42618c2ecf20Sopenharmony_ci					      u8 *addr,
42628c2ecf20Sopenharmony_ci					      u8 encr_type)
42638c2ecf20Sopenharmony_ci{
42648c2ecf20Sopenharmony_ci	struct mwl8k_cmd_update_encryption *cmd;
42658c2ecf20Sopenharmony_ci	int rc;
42668c2ecf20Sopenharmony_ci
42678c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
42688c2ecf20Sopenharmony_ci	if (cmd == NULL)
42698c2ecf20Sopenharmony_ci		return -ENOMEM;
42708c2ecf20Sopenharmony_ci
42718c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION);
42728c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
42738c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_ENCR_ENABLE);
42748c2ecf20Sopenharmony_ci	memcpy(cmd->mac_addr, addr, ETH_ALEN);
42758c2ecf20Sopenharmony_ci	cmd->encr_type = encr_type;
42768c2ecf20Sopenharmony_ci
42778c2ecf20Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
42788c2ecf20Sopenharmony_ci	kfree(cmd);
42798c2ecf20Sopenharmony_ci
42808c2ecf20Sopenharmony_ci	return rc;
42818c2ecf20Sopenharmony_ci}
42828c2ecf20Sopenharmony_ci
42838c2ecf20Sopenharmony_cistatic int mwl8k_encryption_set_cmd_info(struct mwl8k_cmd_set_key *cmd,
42848c2ecf20Sopenharmony_ci						u8 *addr,
42858c2ecf20Sopenharmony_ci						struct ieee80211_key_conf *key)
42868c2ecf20Sopenharmony_ci{
42878c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION);
42888c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
42898c2ecf20Sopenharmony_ci	cmd->length = cpu_to_le16(sizeof(*cmd) -
42908c2ecf20Sopenharmony_ci				offsetof(struct mwl8k_cmd_set_key, length));
42918c2ecf20Sopenharmony_ci	cmd->key_id = cpu_to_le32(key->keyidx);
42928c2ecf20Sopenharmony_ci	cmd->key_len = cpu_to_le16(key->keylen);
42938c2ecf20Sopenharmony_ci	memcpy(cmd->mac_addr, addr, ETH_ALEN);
42948c2ecf20Sopenharmony_ci
42958c2ecf20Sopenharmony_ci	switch (key->cipher) {
42968c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP40:
42978c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP104:
42988c2ecf20Sopenharmony_ci		cmd->key_type_id = cpu_to_le16(MWL8K_ALG_WEP);
42998c2ecf20Sopenharmony_ci		if (key->keyidx == 0)
43008c2ecf20Sopenharmony_ci			cmd->key_info =	cpu_to_le32(MWL8K_KEY_FLAG_WEP_TXKEY);
43018c2ecf20Sopenharmony_ci
43028c2ecf20Sopenharmony_ci		break;
43038c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
43048c2ecf20Sopenharmony_ci		cmd->key_type_id = cpu_to_le16(MWL8K_ALG_TKIP);
43058c2ecf20Sopenharmony_ci		cmd->key_info =	(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
43068c2ecf20Sopenharmony_ci			? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE)
43078c2ecf20Sopenharmony_ci			: cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY);
43088c2ecf20Sopenharmony_ci		cmd->key_info |= cpu_to_le32(MWL8K_KEY_FLAG_MICKEY_VALID
43098c2ecf20Sopenharmony_ci						| MWL8K_KEY_FLAG_TSC_VALID);
43108c2ecf20Sopenharmony_ci		break;
43118c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
43128c2ecf20Sopenharmony_ci		cmd->key_type_id = cpu_to_le16(MWL8K_ALG_CCMP);
43138c2ecf20Sopenharmony_ci		cmd->key_info =	(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
43148c2ecf20Sopenharmony_ci			? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE)
43158c2ecf20Sopenharmony_ci			: cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY);
43168c2ecf20Sopenharmony_ci		break;
43178c2ecf20Sopenharmony_ci	default:
43188c2ecf20Sopenharmony_ci		return -ENOTSUPP;
43198c2ecf20Sopenharmony_ci	}
43208c2ecf20Sopenharmony_ci
43218c2ecf20Sopenharmony_ci	return 0;
43228c2ecf20Sopenharmony_ci}
43238c2ecf20Sopenharmony_ci
43248c2ecf20Sopenharmony_cistatic int mwl8k_cmd_encryption_set_key(struct ieee80211_hw *hw,
43258c2ecf20Sopenharmony_ci						struct ieee80211_vif *vif,
43268c2ecf20Sopenharmony_ci						u8 *addr,
43278c2ecf20Sopenharmony_ci						struct ieee80211_key_conf *key)
43288c2ecf20Sopenharmony_ci{
43298c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_key *cmd;
43308c2ecf20Sopenharmony_ci	int rc;
43318c2ecf20Sopenharmony_ci	int keymlen;
43328c2ecf20Sopenharmony_ci	u32 action;
43338c2ecf20Sopenharmony_ci	u8 idx;
43348c2ecf20Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
43358c2ecf20Sopenharmony_ci
43368c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
43378c2ecf20Sopenharmony_ci	if (cmd == NULL)
43388c2ecf20Sopenharmony_ci		return -ENOMEM;
43398c2ecf20Sopenharmony_ci
43408c2ecf20Sopenharmony_ci	rc = mwl8k_encryption_set_cmd_info(cmd, addr, key);
43418c2ecf20Sopenharmony_ci	if (rc < 0)
43428c2ecf20Sopenharmony_ci		goto done;
43438c2ecf20Sopenharmony_ci
43448c2ecf20Sopenharmony_ci	idx = key->keyidx;
43458c2ecf20Sopenharmony_ci
43468c2ecf20Sopenharmony_ci	if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
43478c2ecf20Sopenharmony_ci		action = MWL8K_ENCR_SET_KEY;
43488c2ecf20Sopenharmony_ci	else
43498c2ecf20Sopenharmony_ci		action = MWL8K_ENCR_SET_GROUP_KEY;
43508c2ecf20Sopenharmony_ci
43518c2ecf20Sopenharmony_ci	switch (key->cipher) {
43528c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP40:
43538c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP104:
43548c2ecf20Sopenharmony_ci		if (!mwl8k_vif->wep_key_conf[idx].enabled) {
43558c2ecf20Sopenharmony_ci			memcpy(mwl8k_vif->wep_key_conf[idx].key, key,
43568c2ecf20Sopenharmony_ci						sizeof(*key) + key->keylen);
43578c2ecf20Sopenharmony_ci			mwl8k_vif->wep_key_conf[idx].enabled = 1;
43588c2ecf20Sopenharmony_ci		}
43598c2ecf20Sopenharmony_ci
43608c2ecf20Sopenharmony_ci		keymlen = key->keylen;
43618c2ecf20Sopenharmony_ci		action = MWL8K_ENCR_SET_KEY;
43628c2ecf20Sopenharmony_ci		break;
43638c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
43648c2ecf20Sopenharmony_ci		keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH;
43658c2ecf20Sopenharmony_ci		break;
43668c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
43678c2ecf20Sopenharmony_ci		keymlen = key->keylen;
43688c2ecf20Sopenharmony_ci		break;
43698c2ecf20Sopenharmony_ci	default:
43708c2ecf20Sopenharmony_ci		rc = -ENOTSUPP;
43718c2ecf20Sopenharmony_ci		goto done;
43728c2ecf20Sopenharmony_ci	}
43738c2ecf20Sopenharmony_ci
43748c2ecf20Sopenharmony_ci	memcpy(cmd->key_material, key->key, keymlen);
43758c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le32(action);
43768c2ecf20Sopenharmony_ci
43778c2ecf20Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
43788c2ecf20Sopenharmony_cidone:
43798c2ecf20Sopenharmony_ci	kfree(cmd);
43808c2ecf20Sopenharmony_ci
43818c2ecf20Sopenharmony_ci	return rc;
43828c2ecf20Sopenharmony_ci}
43838c2ecf20Sopenharmony_ci
43848c2ecf20Sopenharmony_cistatic int mwl8k_cmd_encryption_remove_key(struct ieee80211_hw *hw,
43858c2ecf20Sopenharmony_ci						struct ieee80211_vif *vif,
43868c2ecf20Sopenharmony_ci						u8 *addr,
43878c2ecf20Sopenharmony_ci						struct ieee80211_key_conf *key)
43888c2ecf20Sopenharmony_ci{
43898c2ecf20Sopenharmony_ci	struct mwl8k_cmd_set_key *cmd;
43908c2ecf20Sopenharmony_ci	int rc;
43918c2ecf20Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
43928c2ecf20Sopenharmony_ci
43938c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
43948c2ecf20Sopenharmony_ci	if (cmd == NULL)
43958c2ecf20Sopenharmony_ci		return -ENOMEM;
43968c2ecf20Sopenharmony_ci
43978c2ecf20Sopenharmony_ci	rc = mwl8k_encryption_set_cmd_info(cmd, addr, key);
43988c2ecf20Sopenharmony_ci	if (rc < 0)
43998c2ecf20Sopenharmony_ci		goto done;
44008c2ecf20Sopenharmony_ci
44018c2ecf20Sopenharmony_ci	if (key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
44028c2ecf20Sopenharmony_ci			key->cipher == WLAN_CIPHER_SUITE_WEP104)
44038c2ecf20Sopenharmony_ci		mwl8k_vif->wep_key_conf[key->keyidx].enabled = 0;
44048c2ecf20Sopenharmony_ci
44058c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_ENCR_REMOVE_KEY);
44068c2ecf20Sopenharmony_ci
44078c2ecf20Sopenharmony_ci	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
44088c2ecf20Sopenharmony_cidone:
44098c2ecf20Sopenharmony_ci	kfree(cmd);
44108c2ecf20Sopenharmony_ci
44118c2ecf20Sopenharmony_ci	return rc;
44128c2ecf20Sopenharmony_ci}
44138c2ecf20Sopenharmony_ci
44148c2ecf20Sopenharmony_cistatic int mwl8k_set_key(struct ieee80211_hw *hw,
44158c2ecf20Sopenharmony_ci			 enum set_key_cmd cmd_param,
44168c2ecf20Sopenharmony_ci			 struct ieee80211_vif *vif,
44178c2ecf20Sopenharmony_ci			 struct ieee80211_sta *sta,
44188c2ecf20Sopenharmony_ci			 struct ieee80211_key_conf *key)
44198c2ecf20Sopenharmony_ci{
44208c2ecf20Sopenharmony_ci	int rc = 0;
44218c2ecf20Sopenharmony_ci	u8 encr_type;
44228c2ecf20Sopenharmony_ci	u8 *addr;
44238c2ecf20Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
44248c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
44258c2ecf20Sopenharmony_ci
44268c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_STATION && !priv->ap_fw)
44278c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
44288c2ecf20Sopenharmony_ci
44298c2ecf20Sopenharmony_ci	if (sta == NULL)
44308c2ecf20Sopenharmony_ci		addr = vif->addr;
44318c2ecf20Sopenharmony_ci	else
44328c2ecf20Sopenharmony_ci		addr = sta->addr;
44338c2ecf20Sopenharmony_ci
44348c2ecf20Sopenharmony_ci	if (cmd_param == SET_KEY) {
44358c2ecf20Sopenharmony_ci		rc = mwl8k_cmd_encryption_set_key(hw, vif, addr, key);
44368c2ecf20Sopenharmony_ci		if (rc)
44378c2ecf20Sopenharmony_ci			goto out;
44388c2ecf20Sopenharmony_ci
44398c2ecf20Sopenharmony_ci		if ((key->cipher == WLAN_CIPHER_SUITE_WEP40)
44408c2ecf20Sopenharmony_ci				|| (key->cipher == WLAN_CIPHER_SUITE_WEP104))
44418c2ecf20Sopenharmony_ci			encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_WEP;
44428c2ecf20Sopenharmony_ci		else
44438c2ecf20Sopenharmony_ci			encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED;
44448c2ecf20Sopenharmony_ci
44458c2ecf20Sopenharmony_ci		rc = mwl8k_cmd_update_encryption_enable(hw, vif, addr,
44468c2ecf20Sopenharmony_ci								encr_type);
44478c2ecf20Sopenharmony_ci		if (rc)
44488c2ecf20Sopenharmony_ci			goto out;
44498c2ecf20Sopenharmony_ci
44508c2ecf20Sopenharmony_ci		mwl8k_vif->is_hw_crypto_enabled = true;
44518c2ecf20Sopenharmony_ci
44528c2ecf20Sopenharmony_ci	} else {
44538c2ecf20Sopenharmony_ci		rc = mwl8k_cmd_encryption_remove_key(hw, vif, addr, key);
44548c2ecf20Sopenharmony_ci
44558c2ecf20Sopenharmony_ci		if (rc)
44568c2ecf20Sopenharmony_ci			goto out;
44578c2ecf20Sopenharmony_ci	}
44588c2ecf20Sopenharmony_ciout:
44598c2ecf20Sopenharmony_ci	return rc;
44608c2ecf20Sopenharmony_ci}
44618c2ecf20Sopenharmony_ci
44628c2ecf20Sopenharmony_ci/*
44638c2ecf20Sopenharmony_ci * CMD_UPDATE_STADB.
44648c2ecf20Sopenharmony_ci */
44658c2ecf20Sopenharmony_cistruct ewc_ht_info {
44668c2ecf20Sopenharmony_ci	__le16	control1;
44678c2ecf20Sopenharmony_ci	__le16	control2;
44688c2ecf20Sopenharmony_ci	__le16	control3;
44698c2ecf20Sopenharmony_ci} __packed;
44708c2ecf20Sopenharmony_ci
44718c2ecf20Sopenharmony_cistruct peer_capability_info {
44728c2ecf20Sopenharmony_ci	/* Peer type - AP vs. STA.  */
44738c2ecf20Sopenharmony_ci	__u8	peer_type;
44748c2ecf20Sopenharmony_ci
44758c2ecf20Sopenharmony_ci	/* Basic 802.11 capabilities from assoc resp.  */
44768c2ecf20Sopenharmony_ci	__le16	basic_caps;
44778c2ecf20Sopenharmony_ci
44788c2ecf20Sopenharmony_ci	/* Set if peer supports 802.11n high throughput (HT).  */
44798c2ecf20Sopenharmony_ci	__u8	ht_support;
44808c2ecf20Sopenharmony_ci
44818c2ecf20Sopenharmony_ci	/* Valid if HT is supported.  */
44828c2ecf20Sopenharmony_ci	__le16	ht_caps;
44838c2ecf20Sopenharmony_ci	__u8	extended_ht_caps;
44848c2ecf20Sopenharmony_ci	struct ewc_ht_info	ewc_info;
44858c2ecf20Sopenharmony_ci
44868c2ecf20Sopenharmony_ci	/* Legacy rate table. Intersection of our rates and peer rates.  */
44878c2ecf20Sopenharmony_ci	__u8	legacy_rates[12];
44888c2ecf20Sopenharmony_ci
44898c2ecf20Sopenharmony_ci	/* HT rate table. Intersection of our rates and peer rates.  */
44908c2ecf20Sopenharmony_ci	__u8	ht_rates[16];
44918c2ecf20Sopenharmony_ci	__u8	pad[16];
44928c2ecf20Sopenharmony_ci
44938c2ecf20Sopenharmony_ci	/* If set, interoperability mode, no proprietary extensions.  */
44948c2ecf20Sopenharmony_ci	__u8	interop;
44958c2ecf20Sopenharmony_ci	__u8	pad2;
44968c2ecf20Sopenharmony_ci	__u8	station_id;
44978c2ecf20Sopenharmony_ci	__le16	amsdu_enabled;
44988c2ecf20Sopenharmony_ci} __packed;
44998c2ecf20Sopenharmony_ci
45008c2ecf20Sopenharmony_cistruct mwl8k_cmd_update_stadb {
45018c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt header;
45028c2ecf20Sopenharmony_ci
45038c2ecf20Sopenharmony_ci	/* See STADB_ACTION_TYPE */
45048c2ecf20Sopenharmony_ci	__le32	action;
45058c2ecf20Sopenharmony_ci
45068c2ecf20Sopenharmony_ci	/* Peer MAC address */
45078c2ecf20Sopenharmony_ci	__u8	peer_addr[ETH_ALEN];
45088c2ecf20Sopenharmony_ci
45098c2ecf20Sopenharmony_ci	__le32	reserved;
45108c2ecf20Sopenharmony_ci
45118c2ecf20Sopenharmony_ci	/* Peer info - valid during add/update.  */
45128c2ecf20Sopenharmony_ci	struct peer_capability_info	peer_info;
45138c2ecf20Sopenharmony_ci} __packed;
45148c2ecf20Sopenharmony_ci
45158c2ecf20Sopenharmony_ci#define MWL8K_STA_DB_MODIFY_ENTRY	1
45168c2ecf20Sopenharmony_ci#define MWL8K_STA_DB_DEL_ENTRY		2
45178c2ecf20Sopenharmony_ci
45188c2ecf20Sopenharmony_ci/* Peer Entry flags - used to define the type of the peer node */
45198c2ecf20Sopenharmony_ci#define MWL8K_PEER_TYPE_ACCESSPOINT	2
45208c2ecf20Sopenharmony_ci
45218c2ecf20Sopenharmony_cistatic int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw,
45228c2ecf20Sopenharmony_ci				      struct ieee80211_vif *vif,
45238c2ecf20Sopenharmony_ci				      struct ieee80211_sta *sta)
45248c2ecf20Sopenharmony_ci{
45258c2ecf20Sopenharmony_ci	struct mwl8k_cmd_update_stadb *cmd;
45268c2ecf20Sopenharmony_ci	struct peer_capability_info *p;
45278c2ecf20Sopenharmony_ci	u32 rates;
45288c2ecf20Sopenharmony_ci	int rc;
45298c2ecf20Sopenharmony_ci
45308c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
45318c2ecf20Sopenharmony_ci	if (cmd == NULL)
45328c2ecf20Sopenharmony_ci		return -ENOMEM;
45338c2ecf20Sopenharmony_ci
45348c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB);
45358c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
45368c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_STA_DB_MODIFY_ENTRY);
45378c2ecf20Sopenharmony_ci	memcpy(cmd->peer_addr, sta->addr, ETH_ALEN);
45388c2ecf20Sopenharmony_ci
45398c2ecf20Sopenharmony_ci	p = &cmd->peer_info;
45408c2ecf20Sopenharmony_ci	p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT;
45418c2ecf20Sopenharmony_ci	p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability);
45428c2ecf20Sopenharmony_ci	p->ht_support = sta->ht_cap.ht_supported;
45438c2ecf20Sopenharmony_ci	p->ht_caps = cpu_to_le16(sta->ht_cap.cap);
45448c2ecf20Sopenharmony_ci	p->extended_ht_caps = (sta->ht_cap.ampdu_factor & 3) |
45458c2ecf20Sopenharmony_ci		((sta->ht_cap.ampdu_density & 7) << 2);
45468c2ecf20Sopenharmony_ci	if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ)
45478c2ecf20Sopenharmony_ci		rates = sta->supp_rates[NL80211_BAND_2GHZ];
45488c2ecf20Sopenharmony_ci	else
45498c2ecf20Sopenharmony_ci		rates = sta->supp_rates[NL80211_BAND_5GHZ] << 5;
45508c2ecf20Sopenharmony_ci	legacy_rate_mask_to_array(p->legacy_rates, rates);
45518c2ecf20Sopenharmony_ci	memcpy(p->ht_rates, sta->ht_cap.mcs.rx_mask, 16);
45528c2ecf20Sopenharmony_ci	p->interop = 1;
45538c2ecf20Sopenharmony_ci	p->amsdu_enabled = 0;
45548c2ecf20Sopenharmony_ci
45558c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
45568c2ecf20Sopenharmony_ci	if (!rc)
45578c2ecf20Sopenharmony_ci		rc = p->station_id;
45588c2ecf20Sopenharmony_ci	kfree(cmd);
45598c2ecf20Sopenharmony_ci
45608c2ecf20Sopenharmony_ci	return rc;
45618c2ecf20Sopenharmony_ci}
45628c2ecf20Sopenharmony_ci
45638c2ecf20Sopenharmony_cistatic int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw,
45648c2ecf20Sopenharmony_ci				      struct ieee80211_vif *vif, u8 *addr)
45658c2ecf20Sopenharmony_ci{
45668c2ecf20Sopenharmony_ci	struct mwl8k_cmd_update_stadb *cmd;
45678c2ecf20Sopenharmony_ci	int rc;
45688c2ecf20Sopenharmony_ci
45698c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
45708c2ecf20Sopenharmony_ci	if (cmd == NULL)
45718c2ecf20Sopenharmony_ci		return -ENOMEM;
45728c2ecf20Sopenharmony_ci
45738c2ecf20Sopenharmony_ci	cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB);
45748c2ecf20Sopenharmony_ci	cmd->header.length = cpu_to_le16(sizeof(*cmd));
45758c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le32(MWL8K_STA_DB_DEL_ENTRY);
45768c2ecf20Sopenharmony_ci	memcpy(cmd->peer_addr, addr, ETH_ALEN);
45778c2ecf20Sopenharmony_ci
45788c2ecf20Sopenharmony_ci	rc = mwl8k_post_cmd(hw, &cmd->header);
45798c2ecf20Sopenharmony_ci	kfree(cmd);
45808c2ecf20Sopenharmony_ci
45818c2ecf20Sopenharmony_ci	return rc;
45828c2ecf20Sopenharmony_ci}
45838c2ecf20Sopenharmony_ci
45848c2ecf20Sopenharmony_ci
45858c2ecf20Sopenharmony_ci/*
45868c2ecf20Sopenharmony_ci * Interrupt handling.
45878c2ecf20Sopenharmony_ci */
45888c2ecf20Sopenharmony_cistatic irqreturn_t mwl8k_interrupt(int irq, void *dev_id)
45898c2ecf20Sopenharmony_ci{
45908c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = dev_id;
45918c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
45928c2ecf20Sopenharmony_ci	u32 status;
45938c2ecf20Sopenharmony_ci
45948c2ecf20Sopenharmony_ci	status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
45958c2ecf20Sopenharmony_ci	if (!status)
45968c2ecf20Sopenharmony_ci		return IRQ_NONE;
45978c2ecf20Sopenharmony_ci
45988c2ecf20Sopenharmony_ci	if (status & MWL8K_A2H_INT_TX_DONE) {
45998c2ecf20Sopenharmony_ci		status &= ~MWL8K_A2H_INT_TX_DONE;
46008c2ecf20Sopenharmony_ci		tasklet_schedule(&priv->poll_tx_task);
46018c2ecf20Sopenharmony_ci	}
46028c2ecf20Sopenharmony_ci
46038c2ecf20Sopenharmony_ci	if (status & MWL8K_A2H_INT_RX_READY) {
46048c2ecf20Sopenharmony_ci		status &= ~MWL8K_A2H_INT_RX_READY;
46058c2ecf20Sopenharmony_ci		tasklet_schedule(&priv->poll_rx_task);
46068c2ecf20Sopenharmony_ci	}
46078c2ecf20Sopenharmony_ci
46088c2ecf20Sopenharmony_ci	if (status & MWL8K_A2H_INT_BA_WATCHDOG) {
46098c2ecf20Sopenharmony_ci		iowrite32(~MWL8K_A2H_INT_BA_WATCHDOG,
46108c2ecf20Sopenharmony_ci			  priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
46118c2ecf20Sopenharmony_ci
46128c2ecf20Sopenharmony_ci		atomic_inc(&priv->watchdog_event_pending);
46138c2ecf20Sopenharmony_ci		status &= ~MWL8K_A2H_INT_BA_WATCHDOG;
46148c2ecf20Sopenharmony_ci		ieee80211_queue_work(hw, &priv->watchdog_ba_handle);
46158c2ecf20Sopenharmony_ci	}
46168c2ecf20Sopenharmony_ci
46178c2ecf20Sopenharmony_ci	if (status)
46188c2ecf20Sopenharmony_ci		iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
46198c2ecf20Sopenharmony_ci
46208c2ecf20Sopenharmony_ci	if (status & MWL8K_A2H_INT_OPC_DONE) {
46218c2ecf20Sopenharmony_ci		if (priv->hostcmd_wait != NULL)
46228c2ecf20Sopenharmony_ci			complete(priv->hostcmd_wait);
46238c2ecf20Sopenharmony_ci	}
46248c2ecf20Sopenharmony_ci
46258c2ecf20Sopenharmony_ci	if (status & MWL8K_A2H_INT_QUEUE_EMPTY) {
46268c2ecf20Sopenharmony_ci		if (!mutex_is_locked(&priv->fw_mutex) &&
46278c2ecf20Sopenharmony_ci		    priv->radio_on && priv->pending_tx_pkts)
46288c2ecf20Sopenharmony_ci			mwl8k_tx_start(priv);
46298c2ecf20Sopenharmony_ci	}
46308c2ecf20Sopenharmony_ci
46318c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
46328c2ecf20Sopenharmony_ci}
46338c2ecf20Sopenharmony_ci
46348c2ecf20Sopenharmony_cistatic void mwl8k_tx_poll(struct tasklet_struct *t)
46358c2ecf20Sopenharmony_ci{
46368c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = from_tasklet(priv, t, poll_tx_task);
46378c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = pci_get_drvdata(priv->pdev);
46388c2ecf20Sopenharmony_ci	int limit;
46398c2ecf20Sopenharmony_ci	int i;
46408c2ecf20Sopenharmony_ci
46418c2ecf20Sopenharmony_ci	limit = 32;
46428c2ecf20Sopenharmony_ci
46438c2ecf20Sopenharmony_ci	spin_lock(&priv->tx_lock);
46448c2ecf20Sopenharmony_ci
46458c2ecf20Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
46468c2ecf20Sopenharmony_ci		limit -= mwl8k_txq_reclaim(hw, i, limit, 0);
46478c2ecf20Sopenharmony_ci
46488c2ecf20Sopenharmony_ci	if (!priv->pending_tx_pkts && priv->tx_wait != NULL) {
46498c2ecf20Sopenharmony_ci		complete(priv->tx_wait);
46508c2ecf20Sopenharmony_ci		priv->tx_wait = NULL;
46518c2ecf20Sopenharmony_ci	}
46528c2ecf20Sopenharmony_ci
46538c2ecf20Sopenharmony_ci	spin_unlock(&priv->tx_lock);
46548c2ecf20Sopenharmony_ci
46558c2ecf20Sopenharmony_ci	if (limit) {
46568c2ecf20Sopenharmony_ci		writel(~MWL8K_A2H_INT_TX_DONE,
46578c2ecf20Sopenharmony_ci		       priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
46588c2ecf20Sopenharmony_ci	} else {
46598c2ecf20Sopenharmony_ci		tasklet_schedule(&priv->poll_tx_task);
46608c2ecf20Sopenharmony_ci	}
46618c2ecf20Sopenharmony_ci}
46628c2ecf20Sopenharmony_ci
46638c2ecf20Sopenharmony_cistatic void mwl8k_rx_poll(struct tasklet_struct *t)
46648c2ecf20Sopenharmony_ci{
46658c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = from_tasklet(priv, t, poll_rx_task);
46668c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = pci_get_drvdata(priv->pdev);
46678c2ecf20Sopenharmony_ci	int limit;
46688c2ecf20Sopenharmony_ci
46698c2ecf20Sopenharmony_ci	limit = 32;
46708c2ecf20Sopenharmony_ci	limit -= rxq_process(hw, 0, limit);
46718c2ecf20Sopenharmony_ci	limit -= rxq_refill(hw, 0, limit);
46728c2ecf20Sopenharmony_ci
46738c2ecf20Sopenharmony_ci	if (limit) {
46748c2ecf20Sopenharmony_ci		writel(~MWL8K_A2H_INT_RX_READY,
46758c2ecf20Sopenharmony_ci		       priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
46768c2ecf20Sopenharmony_ci	} else {
46778c2ecf20Sopenharmony_ci		tasklet_schedule(&priv->poll_rx_task);
46788c2ecf20Sopenharmony_ci	}
46798c2ecf20Sopenharmony_ci}
46808c2ecf20Sopenharmony_ci
46818c2ecf20Sopenharmony_ci
46828c2ecf20Sopenharmony_ci/*
46838c2ecf20Sopenharmony_ci * Core driver operations.
46848c2ecf20Sopenharmony_ci */
46858c2ecf20Sopenharmony_cistatic void mwl8k_tx(struct ieee80211_hw *hw,
46868c2ecf20Sopenharmony_ci		     struct ieee80211_tx_control *control,
46878c2ecf20Sopenharmony_ci		     struct sk_buff *skb)
46888c2ecf20Sopenharmony_ci{
46898c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
46908c2ecf20Sopenharmony_ci	int index = skb_get_queue_mapping(skb);
46918c2ecf20Sopenharmony_ci
46928c2ecf20Sopenharmony_ci	if (!priv->radio_on) {
46938c2ecf20Sopenharmony_ci		wiphy_debug(hw->wiphy,
46948c2ecf20Sopenharmony_ci			    "dropped TX frame since radio disabled\n");
46958c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
46968c2ecf20Sopenharmony_ci		return;
46978c2ecf20Sopenharmony_ci	}
46988c2ecf20Sopenharmony_ci
46998c2ecf20Sopenharmony_ci	mwl8k_txq_xmit(hw, index, control->sta, skb);
47008c2ecf20Sopenharmony_ci}
47018c2ecf20Sopenharmony_ci
47028c2ecf20Sopenharmony_cistatic int mwl8k_start(struct ieee80211_hw *hw)
47038c2ecf20Sopenharmony_ci{
47048c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
47058c2ecf20Sopenharmony_ci	int rc;
47068c2ecf20Sopenharmony_ci
47078c2ecf20Sopenharmony_ci	rc = request_irq(priv->pdev->irq, mwl8k_interrupt,
47088c2ecf20Sopenharmony_ci			 IRQF_SHARED, MWL8K_NAME, hw);
47098c2ecf20Sopenharmony_ci	if (rc) {
47108c2ecf20Sopenharmony_ci		priv->irq = -1;
47118c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy, "failed to register IRQ handler\n");
47128c2ecf20Sopenharmony_ci		return -EIO;
47138c2ecf20Sopenharmony_ci	}
47148c2ecf20Sopenharmony_ci	priv->irq = priv->pdev->irq;
47158c2ecf20Sopenharmony_ci
47168c2ecf20Sopenharmony_ci	/* Enable TX reclaim and RX tasklets.  */
47178c2ecf20Sopenharmony_ci	tasklet_enable(&priv->poll_tx_task);
47188c2ecf20Sopenharmony_ci	tasklet_enable(&priv->poll_rx_task);
47198c2ecf20Sopenharmony_ci
47208c2ecf20Sopenharmony_ci	/* Enable interrupts */
47218c2ecf20Sopenharmony_ci	iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
47228c2ecf20Sopenharmony_ci	iowrite32(MWL8K_A2H_EVENTS,
47238c2ecf20Sopenharmony_ci		  priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
47248c2ecf20Sopenharmony_ci
47258c2ecf20Sopenharmony_ci	rc = mwl8k_fw_lock(hw);
47268c2ecf20Sopenharmony_ci	if (!rc) {
47278c2ecf20Sopenharmony_ci		rc = mwl8k_cmd_radio_enable(hw);
47288c2ecf20Sopenharmony_ci
47298c2ecf20Sopenharmony_ci		if (!priv->ap_fw) {
47308c2ecf20Sopenharmony_ci			if (!rc)
47318c2ecf20Sopenharmony_ci				rc = mwl8k_cmd_enable_sniffer(hw, 0);
47328c2ecf20Sopenharmony_ci
47338c2ecf20Sopenharmony_ci			if (!rc)
47348c2ecf20Sopenharmony_ci				rc = mwl8k_cmd_set_pre_scan(hw);
47358c2ecf20Sopenharmony_ci
47368c2ecf20Sopenharmony_ci			if (!rc)
47378c2ecf20Sopenharmony_ci				rc = mwl8k_cmd_set_post_scan(hw,
47388c2ecf20Sopenharmony_ci						"\x00\x00\x00\x00\x00\x00");
47398c2ecf20Sopenharmony_ci		}
47408c2ecf20Sopenharmony_ci
47418c2ecf20Sopenharmony_ci		if (!rc)
47428c2ecf20Sopenharmony_ci			rc = mwl8k_cmd_set_rateadapt_mode(hw, 0);
47438c2ecf20Sopenharmony_ci
47448c2ecf20Sopenharmony_ci		if (!rc)
47458c2ecf20Sopenharmony_ci			rc = mwl8k_cmd_set_wmm_mode(hw, 0);
47468c2ecf20Sopenharmony_ci
47478c2ecf20Sopenharmony_ci		mwl8k_fw_unlock(hw);
47488c2ecf20Sopenharmony_ci	}
47498c2ecf20Sopenharmony_ci
47508c2ecf20Sopenharmony_ci	if (rc) {
47518c2ecf20Sopenharmony_ci		iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
47528c2ecf20Sopenharmony_ci		free_irq(priv->pdev->irq, hw);
47538c2ecf20Sopenharmony_ci		priv->irq = -1;
47548c2ecf20Sopenharmony_ci		tasklet_disable(&priv->poll_tx_task);
47558c2ecf20Sopenharmony_ci		tasklet_disable(&priv->poll_rx_task);
47568c2ecf20Sopenharmony_ci	} else {
47578c2ecf20Sopenharmony_ci		ieee80211_wake_queues(hw);
47588c2ecf20Sopenharmony_ci	}
47598c2ecf20Sopenharmony_ci
47608c2ecf20Sopenharmony_ci	return rc;
47618c2ecf20Sopenharmony_ci}
47628c2ecf20Sopenharmony_ci
47638c2ecf20Sopenharmony_cistatic void mwl8k_stop(struct ieee80211_hw *hw)
47648c2ecf20Sopenharmony_ci{
47658c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
47668c2ecf20Sopenharmony_ci	int i;
47678c2ecf20Sopenharmony_ci
47688c2ecf20Sopenharmony_ci	if (!priv->hw_restart_in_progress)
47698c2ecf20Sopenharmony_ci		mwl8k_cmd_radio_disable(hw);
47708c2ecf20Sopenharmony_ci
47718c2ecf20Sopenharmony_ci	ieee80211_stop_queues(hw);
47728c2ecf20Sopenharmony_ci
47738c2ecf20Sopenharmony_ci	/* Disable interrupts */
47748c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
47758c2ecf20Sopenharmony_ci	if (priv->irq != -1) {
47768c2ecf20Sopenharmony_ci		free_irq(priv->pdev->irq, hw);
47778c2ecf20Sopenharmony_ci		priv->irq = -1;
47788c2ecf20Sopenharmony_ci	}
47798c2ecf20Sopenharmony_ci
47808c2ecf20Sopenharmony_ci	/* Stop finalize join worker */
47818c2ecf20Sopenharmony_ci	cancel_work_sync(&priv->finalize_join_worker);
47828c2ecf20Sopenharmony_ci	cancel_work_sync(&priv->watchdog_ba_handle);
47838c2ecf20Sopenharmony_ci	if (priv->beacon_skb != NULL)
47848c2ecf20Sopenharmony_ci		dev_kfree_skb(priv->beacon_skb);
47858c2ecf20Sopenharmony_ci
47868c2ecf20Sopenharmony_ci	/* Stop TX reclaim and RX tasklets.  */
47878c2ecf20Sopenharmony_ci	tasklet_disable(&priv->poll_tx_task);
47888c2ecf20Sopenharmony_ci	tasklet_disable(&priv->poll_rx_task);
47898c2ecf20Sopenharmony_ci
47908c2ecf20Sopenharmony_ci	/* Return all skbs to mac80211 */
47918c2ecf20Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
47928c2ecf20Sopenharmony_ci		mwl8k_txq_reclaim(hw, i, INT_MAX, 1);
47938c2ecf20Sopenharmony_ci}
47948c2ecf20Sopenharmony_ci
47958c2ecf20Sopenharmony_cistatic int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image);
47968c2ecf20Sopenharmony_ci
47978c2ecf20Sopenharmony_cistatic int mwl8k_add_interface(struct ieee80211_hw *hw,
47988c2ecf20Sopenharmony_ci			       struct ieee80211_vif *vif)
47998c2ecf20Sopenharmony_ci{
48008c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
48018c2ecf20Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif;
48028c2ecf20Sopenharmony_ci	u32 macids_supported;
48038c2ecf20Sopenharmony_ci	int macid, rc;
48048c2ecf20Sopenharmony_ci	struct mwl8k_device_info *di;
48058c2ecf20Sopenharmony_ci
48068c2ecf20Sopenharmony_ci	/*
48078c2ecf20Sopenharmony_ci	 * Reject interface creation if sniffer mode is active, as
48088c2ecf20Sopenharmony_ci	 * STA operation is mutually exclusive with hardware sniffer
48098c2ecf20Sopenharmony_ci	 * mode.  (Sniffer mode is only used on STA firmware.)
48108c2ecf20Sopenharmony_ci	 */
48118c2ecf20Sopenharmony_ci	if (priv->sniffer_enabled) {
48128c2ecf20Sopenharmony_ci		wiphy_info(hw->wiphy,
48138c2ecf20Sopenharmony_ci			   "unable to create STA interface because sniffer mode is enabled\n");
48148c2ecf20Sopenharmony_ci		return -EINVAL;
48158c2ecf20Sopenharmony_ci	}
48168c2ecf20Sopenharmony_ci
48178c2ecf20Sopenharmony_ci	di = priv->device_info;
48188c2ecf20Sopenharmony_ci	switch (vif->type) {
48198c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_AP:
48208c2ecf20Sopenharmony_ci		if (!priv->ap_fw && di->fw_image_ap) {
48218c2ecf20Sopenharmony_ci			/* we must load the ap fw to meet this request */
48228c2ecf20Sopenharmony_ci			if (!list_empty(&priv->vif_list))
48238c2ecf20Sopenharmony_ci				return -EBUSY;
48248c2ecf20Sopenharmony_ci			rc = mwl8k_reload_firmware(hw, di->fw_image_ap);
48258c2ecf20Sopenharmony_ci			if (rc)
48268c2ecf20Sopenharmony_ci				return rc;
48278c2ecf20Sopenharmony_ci		}
48288c2ecf20Sopenharmony_ci		macids_supported = priv->ap_macids_supported;
48298c2ecf20Sopenharmony_ci		break;
48308c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
48318c2ecf20Sopenharmony_ci		if (priv->ap_fw && di->fw_image_sta) {
48328c2ecf20Sopenharmony_ci			if (!list_empty(&priv->vif_list)) {
48338c2ecf20Sopenharmony_ci				wiphy_warn(hw->wiphy, "AP interface is running.\n"
48348c2ecf20Sopenharmony_ci					   "Adding STA interface for WDS");
48358c2ecf20Sopenharmony_ci			} else {
48368c2ecf20Sopenharmony_ci				/* we must load the sta fw to
48378c2ecf20Sopenharmony_ci				 * meet this request.
48388c2ecf20Sopenharmony_ci				 */
48398c2ecf20Sopenharmony_ci				rc = mwl8k_reload_firmware(hw,
48408c2ecf20Sopenharmony_ci							   di->fw_image_sta);
48418c2ecf20Sopenharmony_ci				if (rc)
48428c2ecf20Sopenharmony_ci					return rc;
48438c2ecf20Sopenharmony_ci			}
48448c2ecf20Sopenharmony_ci		}
48458c2ecf20Sopenharmony_ci		macids_supported = priv->sta_macids_supported;
48468c2ecf20Sopenharmony_ci		break;
48478c2ecf20Sopenharmony_ci	default:
48488c2ecf20Sopenharmony_ci		return -EINVAL;
48498c2ecf20Sopenharmony_ci	}
48508c2ecf20Sopenharmony_ci
48518c2ecf20Sopenharmony_ci	macid = ffs(macids_supported & ~priv->macids_used);
48528c2ecf20Sopenharmony_ci	if (!macid--)
48538c2ecf20Sopenharmony_ci		return -EBUSY;
48548c2ecf20Sopenharmony_ci
48558c2ecf20Sopenharmony_ci	/* Setup driver private area. */
48568c2ecf20Sopenharmony_ci	mwl8k_vif = MWL8K_VIF(vif);
48578c2ecf20Sopenharmony_ci	memset(mwl8k_vif, 0, sizeof(*mwl8k_vif));
48588c2ecf20Sopenharmony_ci	mwl8k_vif->vif = vif;
48598c2ecf20Sopenharmony_ci	mwl8k_vif->macid = macid;
48608c2ecf20Sopenharmony_ci	mwl8k_vif->seqno = 0;
48618c2ecf20Sopenharmony_ci	memcpy(mwl8k_vif->bssid, vif->addr, ETH_ALEN);
48628c2ecf20Sopenharmony_ci	mwl8k_vif->is_hw_crypto_enabled = false;
48638c2ecf20Sopenharmony_ci
48648c2ecf20Sopenharmony_ci	/* Set the mac address.  */
48658c2ecf20Sopenharmony_ci	mwl8k_cmd_set_mac_addr(hw, vif, vif->addr);
48668c2ecf20Sopenharmony_ci
48678c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_AP)
48688c2ecf20Sopenharmony_ci		mwl8k_cmd_set_new_stn_add_self(hw, vif);
48698c2ecf20Sopenharmony_ci
48708c2ecf20Sopenharmony_ci	priv->macids_used |= 1 << mwl8k_vif->macid;
48718c2ecf20Sopenharmony_ci	list_add_tail(&mwl8k_vif->list, &priv->vif_list);
48728c2ecf20Sopenharmony_ci
48738c2ecf20Sopenharmony_ci	return 0;
48748c2ecf20Sopenharmony_ci}
48758c2ecf20Sopenharmony_ci
48768c2ecf20Sopenharmony_cistatic void mwl8k_remove_vif(struct mwl8k_priv *priv, struct mwl8k_vif *vif)
48778c2ecf20Sopenharmony_ci{
48788c2ecf20Sopenharmony_ci	/* Has ieee80211_restart_hw re-added the removed interfaces? */
48798c2ecf20Sopenharmony_ci	if (!priv->macids_used)
48808c2ecf20Sopenharmony_ci		return;
48818c2ecf20Sopenharmony_ci
48828c2ecf20Sopenharmony_ci	priv->macids_used &= ~(1 << vif->macid);
48838c2ecf20Sopenharmony_ci	list_del(&vif->list);
48848c2ecf20Sopenharmony_ci}
48858c2ecf20Sopenharmony_ci
48868c2ecf20Sopenharmony_cistatic void mwl8k_remove_interface(struct ieee80211_hw *hw,
48878c2ecf20Sopenharmony_ci				   struct ieee80211_vif *vif)
48888c2ecf20Sopenharmony_ci{
48898c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
48908c2ecf20Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
48918c2ecf20Sopenharmony_ci
48928c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_AP)
48938c2ecf20Sopenharmony_ci		mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr);
48948c2ecf20Sopenharmony_ci
48958c2ecf20Sopenharmony_ci	mwl8k_cmd_del_mac_addr(hw, vif, vif->addr);
48968c2ecf20Sopenharmony_ci
48978c2ecf20Sopenharmony_ci	mwl8k_remove_vif(priv, mwl8k_vif);
48988c2ecf20Sopenharmony_ci}
48998c2ecf20Sopenharmony_ci
49008c2ecf20Sopenharmony_cistatic void mwl8k_hw_restart_work(struct work_struct *work)
49018c2ecf20Sopenharmony_ci{
49028c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv =
49038c2ecf20Sopenharmony_ci		container_of(work, struct mwl8k_priv, fw_reload);
49048c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = priv->hw;
49058c2ecf20Sopenharmony_ci	struct mwl8k_device_info *di;
49068c2ecf20Sopenharmony_ci	int rc;
49078c2ecf20Sopenharmony_ci
49088c2ecf20Sopenharmony_ci	/* If some command is waiting for a response, clear it */
49098c2ecf20Sopenharmony_ci	if (priv->hostcmd_wait != NULL) {
49108c2ecf20Sopenharmony_ci		complete(priv->hostcmd_wait);
49118c2ecf20Sopenharmony_ci		priv->hostcmd_wait = NULL;
49128c2ecf20Sopenharmony_ci	}
49138c2ecf20Sopenharmony_ci
49148c2ecf20Sopenharmony_ci	priv->hw_restart_owner = current;
49158c2ecf20Sopenharmony_ci	di = priv->device_info;
49168c2ecf20Sopenharmony_ci	mwl8k_fw_lock(hw);
49178c2ecf20Sopenharmony_ci
49188c2ecf20Sopenharmony_ci	if (priv->ap_fw)
49198c2ecf20Sopenharmony_ci		rc = mwl8k_reload_firmware(hw, di->fw_image_ap);
49208c2ecf20Sopenharmony_ci	else
49218c2ecf20Sopenharmony_ci		rc = mwl8k_reload_firmware(hw, di->fw_image_sta);
49228c2ecf20Sopenharmony_ci
49238c2ecf20Sopenharmony_ci	if (rc)
49248c2ecf20Sopenharmony_ci		goto fail;
49258c2ecf20Sopenharmony_ci
49268c2ecf20Sopenharmony_ci	priv->hw_restart_owner = NULL;
49278c2ecf20Sopenharmony_ci	priv->hw_restart_in_progress = false;
49288c2ecf20Sopenharmony_ci
49298c2ecf20Sopenharmony_ci	/*
49308c2ecf20Sopenharmony_ci	 * This unlock will wake up the queues and
49318c2ecf20Sopenharmony_ci	 * also opens the command path for other
49328c2ecf20Sopenharmony_ci	 * commands
49338c2ecf20Sopenharmony_ci	 */
49348c2ecf20Sopenharmony_ci	mwl8k_fw_unlock(hw);
49358c2ecf20Sopenharmony_ci
49368c2ecf20Sopenharmony_ci	ieee80211_restart_hw(hw);
49378c2ecf20Sopenharmony_ci
49388c2ecf20Sopenharmony_ci	wiphy_err(hw->wiphy, "Firmware restarted successfully\n");
49398c2ecf20Sopenharmony_ci
49408c2ecf20Sopenharmony_ci	return;
49418c2ecf20Sopenharmony_cifail:
49428c2ecf20Sopenharmony_ci	mwl8k_fw_unlock(hw);
49438c2ecf20Sopenharmony_ci
49448c2ecf20Sopenharmony_ci	wiphy_err(hw->wiphy, "Firmware restart failed\n");
49458c2ecf20Sopenharmony_ci}
49468c2ecf20Sopenharmony_ci
49478c2ecf20Sopenharmony_cistatic int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
49488c2ecf20Sopenharmony_ci{
49498c2ecf20Sopenharmony_ci	struct ieee80211_conf *conf = &hw->conf;
49508c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
49518c2ecf20Sopenharmony_ci	int rc;
49528c2ecf20Sopenharmony_ci
49538c2ecf20Sopenharmony_ci	rc = mwl8k_fw_lock(hw);
49548c2ecf20Sopenharmony_ci	if (rc)
49558c2ecf20Sopenharmony_ci		return rc;
49568c2ecf20Sopenharmony_ci
49578c2ecf20Sopenharmony_ci	if (conf->flags & IEEE80211_CONF_IDLE)
49588c2ecf20Sopenharmony_ci		rc = mwl8k_cmd_radio_disable(hw);
49598c2ecf20Sopenharmony_ci	else
49608c2ecf20Sopenharmony_ci		rc = mwl8k_cmd_radio_enable(hw);
49618c2ecf20Sopenharmony_ci	if (rc)
49628c2ecf20Sopenharmony_ci		goto out;
49638c2ecf20Sopenharmony_ci
49648c2ecf20Sopenharmony_ci	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
49658c2ecf20Sopenharmony_ci		rc = mwl8k_cmd_set_rf_channel(hw, conf);
49668c2ecf20Sopenharmony_ci		if (rc)
49678c2ecf20Sopenharmony_ci			goto out;
49688c2ecf20Sopenharmony_ci	}
49698c2ecf20Sopenharmony_ci
49708c2ecf20Sopenharmony_ci	if (conf->power_level > 18)
49718c2ecf20Sopenharmony_ci		conf->power_level = 18;
49728c2ecf20Sopenharmony_ci
49738c2ecf20Sopenharmony_ci	if (priv->ap_fw) {
49748c2ecf20Sopenharmony_ci
49758c2ecf20Sopenharmony_ci		if (conf->flags & IEEE80211_CONF_CHANGE_POWER) {
49768c2ecf20Sopenharmony_ci			rc = mwl8k_cmd_tx_power(hw, conf, conf->power_level);
49778c2ecf20Sopenharmony_ci			if (rc)
49788c2ecf20Sopenharmony_ci				goto out;
49798c2ecf20Sopenharmony_ci		}
49808c2ecf20Sopenharmony_ci
49818c2ecf20Sopenharmony_ci
49828c2ecf20Sopenharmony_ci	} else {
49838c2ecf20Sopenharmony_ci		rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level);
49848c2ecf20Sopenharmony_ci		if (rc)
49858c2ecf20Sopenharmony_ci			goto out;
49868c2ecf20Sopenharmony_ci		rc = mwl8k_cmd_mimo_config(hw, 0x7, 0x7);
49878c2ecf20Sopenharmony_ci	}
49888c2ecf20Sopenharmony_ci
49898c2ecf20Sopenharmony_ciout:
49908c2ecf20Sopenharmony_ci	mwl8k_fw_unlock(hw);
49918c2ecf20Sopenharmony_ci
49928c2ecf20Sopenharmony_ci	return rc;
49938c2ecf20Sopenharmony_ci}
49948c2ecf20Sopenharmony_ci
49958c2ecf20Sopenharmony_cistatic void
49968c2ecf20Sopenharmony_cimwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
49978c2ecf20Sopenharmony_ci			   struct ieee80211_bss_conf *info, u32 changed)
49988c2ecf20Sopenharmony_ci{
49998c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
50008c2ecf20Sopenharmony_ci	u32 ap_legacy_rates = 0;
50018c2ecf20Sopenharmony_ci	u8 ap_mcs_rates[16];
50028c2ecf20Sopenharmony_ci	int rc;
50038c2ecf20Sopenharmony_ci
50048c2ecf20Sopenharmony_ci	if (mwl8k_fw_lock(hw))
50058c2ecf20Sopenharmony_ci		return;
50068c2ecf20Sopenharmony_ci
50078c2ecf20Sopenharmony_ci	/*
50088c2ecf20Sopenharmony_ci	 * No need to capture a beacon if we're no longer associated.
50098c2ecf20Sopenharmony_ci	 */
50108c2ecf20Sopenharmony_ci	if ((changed & BSS_CHANGED_ASSOC) && !vif->bss_conf.assoc)
50118c2ecf20Sopenharmony_ci		priv->capture_beacon = false;
50128c2ecf20Sopenharmony_ci
50138c2ecf20Sopenharmony_ci	/*
50148c2ecf20Sopenharmony_ci	 * Get the AP's legacy and MCS rates.
50158c2ecf20Sopenharmony_ci	 */
50168c2ecf20Sopenharmony_ci	if (vif->bss_conf.assoc) {
50178c2ecf20Sopenharmony_ci		struct ieee80211_sta *ap;
50188c2ecf20Sopenharmony_ci
50198c2ecf20Sopenharmony_ci		rcu_read_lock();
50208c2ecf20Sopenharmony_ci
50218c2ecf20Sopenharmony_ci		ap = ieee80211_find_sta(vif, vif->bss_conf.bssid);
50228c2ecf20Sopenharmony_ci		if (ap == NULL) {
50238c2ecf20Sopenharmony_ci			rcu_read_unlock();
50248c2ecf20Sopenharmony_ci			goto out;
50258c2ecf20Sopenharmony_ci		}
50268c2ecf20Sopenharmony_ci
50278c2ecf20Sopenharmony_ci		if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ) {
50288c2ecf20Sopenharmony_ci			ap_legacy_rates = ap->supp_rates[NL80211_BAND_2GHZ];
50298c2ecf20Sopenharmony_ci		} else {
50308c2ecf20Sopenharmony_ci			ap_legacy_rates =
50318c2ecf20Sopenharmony_ci				ap->supp_rates[NL80211_BAND_5GHZ] << 5;
50328c2ecf20Sopenharmony_ci		}
50338c2ecf20Sopenharmony_ci		memcpy(ap_mcs_rates, ap->ht_cap.mcs.rx_mask, 16);
50348c2ecf20Sopenharmony_ci
50358c2ecf20Sopenharmony_ci		rcu_read_unlock();
50368c2ecf20Sopenharmony_ci
50378c2ecf20Sopenharmony_ci		if (changed & BSS_CHANGED_ASSOC) {
50388c2ecf20Sopenharmony_ci			if (!priv->ap_fw) {
50398c2ecf20Sopenharmony_ci				rc = mwl8k_cmd_set_rate(hw, vif,
50408c2ecf20Sopenharmony_ci							ap_legacy_rates,
50418c2ecf20Sopenharmony_ci							ap_mcs_rates);
50428c2ecf20Sopenharmony_ci				if (rc)
50438c2ecf20Sopenharmony_ci					goto out;
50448c2ecf20Sopenharmony_ci
50458c2ecf20Sopenharmony_ci				rc = mwl8k_cmd_use_fixed_rate_sta(hw);
50468c2ecf20Sopenharmony_ci				if (rc)
50478c2ecf20Sopenharmony_ci					goto out;
50488c2ecf20Sopenharmony_ci			} else {
50498c2ecf20Sopenharmony_ci				int idx;
50508c2ecf20Sopenharmony_ci				int rate;
50518c2ecf20Sopenharmony_ci
50528c2ecf20Sopenharmony_ci				/* Use AP firmware specific rate command.
50538c2ecf20Sopenharmony_ci				 */
50548c2ecf20Sopenharmony_ci				idx = ffs(vif->bss_conf.basic_rates);
50558c2ecf20Sopenharmony_ci				if (idx)
50568c2ecf20Sopenharmony_ci					idx--;
50578c2ecf20Sopenharmony_ci
50588c2ecf20Sopenharmony_ci				if (hw->conf.chandef.chan->band ==
50598c2ecf20Sopenharmony_ci				    NL80211_BAND_2GHZ)
50608c2ecf20Sopenharmony_ci					rate = mwl8k_rates_24[idx].hw_value;
50618c2ecf20Sopenharmony_ci				else
50628c2ecf20Sopenharmony_ci					rate = mwl8k_rates_50[idx].hw_value;
50638c2ecf20Sopenharmony_ci
50648c2ecf20Sopenharmony_ci				mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate);
50658c2ecf20Sopenharmony_ci			}
50668c2ecf20Sopenharmony_ci		}
50678c2ecf20Sopenharmony_ci	}
50688c2ecf20Sopenharmony_ci
50698c2ecf20Sopenharmony_ci	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
50708c2ecf20Sopenharmony_ci		rc = mwl8k_set_radio_preamble(hw,
50718c2ecf20Sopenharmony_ci				vif->bss_conf.use_short_preamble);
50728c2ecf20Sopenharmony_ci		if (rc)
50738c2ecf20Sopenharmony_ci			goto out;
50748c2ecf20Sopenharmony_ci	}
50758c2ecf20Sopenharmony_ci
50768c2ecf20Sopenharmony_ci	if ((changed & BSS_CHANGED_ERP_SLOT) && !priv->ap_fw)  {
50778c2ecf20Sopenharmony_ci		rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot);
50788c2ecf20Sopenharmony_ci		if (rc)
50798c2ecf20Sopenharmony_ci			goto out;
50808c2ecf20Sopenharmony_ci	}
50818c2ecf20Sopenharmony_ci
50828c2ecf20Sopenharmony_ci	if (vif->bss_conf.assoc && !priv->ap_fw &&
50838c2ecf20Sopenharmony_ci	    (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT |
50848c2ecf20Sopenharmony_ci			BSS_CHANGED_HT))) {
50858c2ecf20Sopenharmony_ci		rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates);
50868c2ecf20Sopenharmony_ci		if (rc)
50878c2ecf20Sopenharmony_ci			goto out;
50888c2ecf20Sopenharmony_ci	}
50898c2ecf20Sopenharmony_ci
50908c2ecf20Sopenharmony_ci	if (vif->bss_conf.assoc &&
50918c2ecf20Sopenharmony_ci	    (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INT))) {
50928c2ecf20Sopenharmony_ci		/*
50938c2ecf20Sopenharmony_ci		 * Finalize the join.  Tell rx handler to process
50948c2ecf20Sopenharmony_ci		 * next beacon from our BSSID.
50958c2ecf20Sopenharmony_ci		 */
50968c2ecf20Sopenharmony_ci		memcpy(priv->capture_bssid, vif->bss_conf.bssid, ETH_ALEN);
50978c2ecf20Sopenharmony_ci		priv->capture_beacon = true;
50988c2ecf20Sopenharmony_ci	}
50998c2ecf20Sopenharmony_ci
51008c2ecf20Sopenharmony_ciout:
51018c2ecf20Sopenharmony_ci	mwl8k_fw_unlock(hw);
51028c2ecf20Sopenharmony_ci}
51038c2ecf20Sopenharmony_ci
51048c2ecf20Sopenharmony_cistatic void
51058c2ecf20Sopenharmony_cimwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
51068c2ecf20Sopenharmony_ci			  struct ieee80211_bss_conf *info, u32 changed)
51078c2ecf20Sopenharmony_ci{
51088c2ecf20Sopenharmony_ci	int rc;
51098c2ecf20Sopenharmony_ci
51108c2ecf20Sopenharmony_ci	if (mwl8k_fw_lock(hw))
51118c2ecf20Sopenharmony_ci		return;
51128c2ecf20Sopenharmony_ci
51138c2ecf20Sopenharmony_ci	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
51148c2ecf20Sopenharmony_ci		rc = mwl8k_set_radio_preamble(hw,
51158c2ecf20Sopenharmony_ci				vif->bss_conf.use_short_preamble);
51168c2ecf20Sopenharmony_ci		if (rc)
51178c2ecf20Sopenharmony_ci			goto out;
51188c2ecf20Sopenharmony_ci	}
51198c2ecf20Sopenharmony_ci
51208c2ecf20Sopenharmony_ci	if (changed & BSS_CHANGED_BASIC_RATES) {
51218c2ecf20Sopenharmony_ci		int idx;
51228c2ecf20Sopenharmony_ci		int rate;
51238c2ecf20Sopenharmony_ci
51248c2ecf20Sopenharmony_ci		/*
51258c2ecf20Sopenharmony_ci		 * Use lowest supported basic rate for multicasts
51268c2ecf20Sopenharmony_ci		 * and management frames (such as probe responses --
51278c2ecf20Sopenharmony_ci		 * beacons will always go out at 1 Mb/s).
51288c2ecf20Sopenharmony_ci		 */
51298c2ecf20Sopenharmony_ci		idx = ffs(vif->bss_conf.basic_rates);
51308c2ecf20Sopenharmony_ci		if (idx)
51318c2ecf20Sopenharmony_ci			idx--;
51328c2ecf20Sopenharmony_ci
51338c2ecf20Sopenharmony_ci		if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ)
51348c2ecf20Sopenharmony_ci			rate = mwl8k_rates_24[idx].hw_value;
51358c2ecf20Sopenharmony_ci		else
51368c2ecf20Sopenharmony_ci			rate = mwl8k_rates_50[idx].hw_value;
51378c2ecf20Sopenharmony_ci
51388c2ecf20Sopenharmony_ci		mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate);
51398c2ecf20Sopenharmony_ci	}
51408c2ecf20Sopenharmony_ci
51418c2ecf20Sopenharmony_ci	if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) {
51428c2ecf20Sopenharmony_ci		struct sk_buff *skb;
51438c2ecf20Sopenharmony_ci
51448c2ecf20Sopenharmony_ci		skb = ieee80211_beacon_get(hw, vif);
51458c2ecf20Sopenharmony_ci		if (skb != NULL) {
51468c2ecf20Sopenharmony_ci			mwl8k_cmd_set_beacon(hw, vif, skb->data, skb->len);
51478c2ecf20Sopenharmony_ci			kfree_skb(skb);
51488c2ecf20Sopenharmony_ci		}
51498c2ecf20Sopenharmony_ci	}
51508c2ecf20Sopenharmony_ci
51518c2ecf20Sopenharmony_ci	if (changed & BSS_CHANGED_BEACON_ENABLED)
51528c2ecf20Sopenharmony_ci		mwl8k_cmd_bss_start(hw, vif, info->enable_beacon);
51538c2ecf20Sopenharmony_ci
51548c2ecf20Sopenharmony_ciout:
51558c2ecf20Sopenharmony_ci	mwl8k_fw_unlock(hw);
51568c2ecf20Sopenharmony_ci}
51578c2ecf20Sopenharmony_ci
51588c2ecf20Sopenharmony_cistatic void
51598c2ecf20Sopenharmony_cimwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
51608c2ecf20Sopenharmony_ci		       struct ieee80211_bss_conf *info, u32 changed)
51618c2ecf20Sopenharmony_ci{
51628c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_STATION)
51638c2ecf20Sopenharmony_ci		mwl8k_bss_info_changed_sta(hw, vif, info, changed);
51648c2ecf20Sopenharmony_ci	if (vif->type == NL80211_IFTYPE_AP)
51658c2ecf20Sopenharmony_ci		mwl8k_bss_info_changed_ap(hw, vif, info, changed);
51668c2ecf20Sopenharmony_ci}
51678c2ecf20Sopenharmony_ci
51688c2ecf20Sopenharmony_cistatic u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw,
51698c2ecf20Sopenharmony_ci				   struct netdev_hw_addr_list *mc_list)
51708c2ecf20Sopenharmony_ci{
51718c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt *cmd;
51728c2ecf20Sopenharmony_ci
51738c2ecf20Sopenharmony_ci	/*
51748c2ecf20Sopenharmony_ci	 * Synthesize and return a command packet that programs the
51758c2ecf20Sopenharmony_ci	 * hardware multicast address filter.  At this point we don't
51768c2ecf20Sopenharmony_ci	 * know whether FIF_ALLMULTI is being requested, but if it is,
51778c2ecf20Sopenharmony_ci	 * we'll end up throwing this packet away and creating a new
51788c2ecf20Sopenharmony_ci	 * one in mwl8k_configure_filter().
51798c2ecf20Sopenharmony_ci	 */
51808c2ecf20Sopenharmony_ci	cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_list);
51818c2ecf20Sopenharmony_ci
51828c2ecf20Sopenharmony_ci	return (unsigned long)cmd;
51838c2ecf20Sopenharmony_ci}
51848c2ecf20Sopenharmony_ci
51858c2ecf20Sopenharmony_cistatic int
51868c2ecf20Sopenharmony_cimwl8k_configure_filter_sniffer(struct ieee80211_hw *hw,
51878c2ecf20Sopenharmony_ci			       unsigned int changed_flags,
51888c2ecf20Sopenharmony_ci			       unsigned int *total_flags)
51898c2ecf20Sopenharmony_ci{
51908c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
51918c2ecf20Sopenharmony_ci
51928c2ecf20Sopenharmony_ci	/*
51938c2ecf20Sopenharmony_ci	 * Hardware sniffer mode is mutually exclusive with STA
51948c2ecf20Sopenharmony_ci	 * operation, so refuse to enable sniffer mode if a STA
51958c2ecf20Sopenharmony_ci	 * interface is active.
51968c2ecf20Sopenharmony_ci	 */
51978c2ecf20Sopenharmony_ci	if (!list_empty(&priv->vif_list)) {
51988c2ecf20Sopenharmony_ci		if (net_ratelimit())
51998c2ecf20Sopenharmony_ci			wiphy_info(hw->wiphy,
52008c2ecf20Sopenharmony_ci				   "not enabling sniffer mode because STA interface is active\n");
52018c2ecf20Sopenharmony_ci		return 0;
52028c2ecf20Sopenharmony_ci	}
52038c2ecf20Sopenharmony_ci
52048c2ecf20Sopenharmony_ci	if (!priv->sniffer_enabled) {
52058c2ecf20Sopenharmony_ci		if (mwl8k_cmd_enable_sniffer(hw, 1))
52068c2ecf20Sopenharmony_ci			return 0;
52078c2ecf20Sopenharmony_ci		priv->sniffer_enabled = true;
52088c2ecf20Sopenharmony_ci	}
52098c2ecf20Sopenharmony_ci
52108c2ecf20Sopenharmony_ci	*total_flags &=	FIF_ALLMULTI |
52118c2ecf20Sopenharmony_ci			FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL |
52128c2ecf20Sopenharmony_ci			FIF_OTHER_BSS;
52138c2ecf20Sopenharmony_ci
52148c2ecf20Sopenharmony_ci	return 1;
52158c2ecf20Sopenharmony_ci}
52168c2ecf20Sopenharmony_ci
52178c2ecf20Sopenharmony_cistatic struct mwl8k_vif *mwl8k_first_vif(struct mwl8k_priv *priv)
52188c2ecf20Sopenharmony_ci{
52198c2ecf20Sopenharmony_ci	if (!list_empty(&priv->vif_list))
52208c2ecf20Sopenharmony_ci		return list_entry(priv->vif_list.next, struct mwl8k_vif, list);
52218c2ecf20Sopenharmony_ci
52228c2ecf20Sopenharmony_ci	return NULL;
52238c2ecf20Sopenharmony_ci}
52248c2ecf20Sopenharmony_ci
52258c2ecf20Sopenharmony_cistatic void mwl8k_configure_filter(struct ieee80211_hw *hw,
52268c2ecf20Sopenharmony_ci				   unsigned int changed_flags,
52278c2ecf20Sopenharmony_ci				   unsigned int *total_flags,
52288c2ecf20Sopenharmony_ci				   u64 multicast)
52298c2ecf20Sopenharmony_ci{
52308c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
52318c2ecf20Sopenharmony_ci	struct mwl8k_cmd_pkt *cmd = (void *)(unsigned long)multicast;
52328c2ecf20Sopenharmony_ci
52338c2ecf20Sopenharmony_ci	/*
52348c2ecf20Sopenharmony_ci	 * AP firmware doesn't allow fine-grained control over
52358c2ecf20Sopenharmony_ci	 * the receive filter.
52368c2ecf20Sopenharmony_ci	 */
52378c2ecf20Sopenharmony_ci	if (priv->ap_fw) {
52388c2ecf20Sopenharmony_ci		*total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC;
52398c2ecf20Sopenharmony_ci		kfree(cmd);
52408c2ecf20Sopenharmony_ci		return;
52418c2ecf20Sopenharmony_ci	}
52428c2ecf20Sopenharmony_ci
52438c2ecf20Sopenharmony_ci	/*
52448c2ecf20Sopenharmony_ci	 * Enable hardware sniffer mode if FIF_CONTROL or
52458c2ecf20Sopenharmony_ci	 * FIF_OTHER_BSS is requested.
52468c2ecf20Sopenharmony_ci	 */
52478c2ecf20Sopenharmony_ci	if (*total_flags & (FIF_CONTROL | FIF_OTHER_BSS) &&
52488c2ecf20Sopenharmony_ci	    mwl8k_configure_filter_sniffer(hw, changed_flags, total_flags)) {
52498c2ecf20Sopenharmony_ci		kfree(cmd);
52508c2ecf20Sopenharmony_ci		return;
52518c2ecf20Sopenharmony_ci	}
52528c2ecf20Sopenharmony_ci
52538c2ecf20Sopenharmony_ci	/* Clear unsupported feature flags */
52548c2ecf20Sopenharmony_ci	*total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC;
52558c2ecf20Sopenharmony_ci
52568c2ecf20Sopenharmony_ci	if (mwl8k_fw_lock(hw)) {
52578c2ecf20Sopenharmony_ci		kfree(cmd);
52588c2ecf20Sopenharmony_ci		return;
52598c2ecf20Sopenharmony_ci	}
52608c2ecf20Sopenharmony_ci
52618c2ecf20Sopenharmony_ci	if (priv->sniffer_enabled) {
52628c2ecf20Sopenharmony_ci		mwl8k_cmd_enable_sniffer(hw, 0);
52638c2ecf20Sopenharmony_ci		priv->sniffer_enabled = false;
52648c2ecf20Sopenharmony_ci	}
52658c2ecf20Sopenharmony_ci
52668c2ecf20Sopenharmony_ci	if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
52678c2ecf20Sopenharmony_ci		if (*total_flags & FIF_BCN_PRBRESP_PROMISC) {
52688c2ecf20Sopenharmony_ci			/*
52698c2ecf20Sopenharmony_ci			 * Disable the BSS filter.
52708c2ecf20Sopenharmony_ci			 */
52718c2ecf20Sopenharmony_ci			mwl8k_cmd_set_pre_scan(hw);
52728c2ecf20Sopenharmony_ci		} else {
52738c2ecf20Sopenharmony_ci			struct mwl8k_vif *mwl8k_vif;
52748c2ecf20Sopenharmony_ci			const u8 *bssid;
52758c2ecf20Sopenharmony_ci
52768c2ecf20Sopenharmony_ci			/*
52778c2ecf20Sopenharmony_ci			 * Enable the BSS filter.
52788c2ecf20Sopenharmony_ci			 *
52798c2ecf20Sopenharmony_ci			 * If there is an active STA interface, use that
52808c2ecf20Sopenharmony_ci			 * interface's BSSID, otherwise use a dummy one
52818c2ecf20Sopenharmony_ci			 * (where the OUI part needs to be nonzero for
52828c2ecf20Sopenharmony_ci			 * the BSSID to be accepted by POST_SCAN).
52838c2ecf20Sopenharmony_ci			 */
52848c2ecf20Sopenharmony_ci			mwl8k_vif = mwl8k_first_vif(priv);
52858c2ecf20Sopenharmony_ci			if (mwl8k_vif != NULL)
52868c2ecf20Sopenharmony_ci				bssid = mwl8k_vif->vif->bss_conf.bssid;
52878c2ecf20Sopenharmony_ci			else
52888c2ecf20Sopenharmony_ci				bssid = "\x01\x00\x00\x00\x00\x00";
52898c2ecf20Sopenharmony_ci
52908c2ecf20Sopenharmony_ci			mwl8k_cmd_set_post_scan(hw, bssid);
52918c2ecf20Sopenharmony_ci		}
52928c2ecf20Sopenharmony_ci	}
52938c2ecf20Sopenharmony_ci
52948c2ecf20Sopenharmony_ci	/*
52958c2ecf20Sopenharmony_ci	 * If FIF_ALLMULTI is being requested, throw away the command
52968c2ecf20Sopenharmony_ci	 * packet that ->prepare_multicast() built and replace it with
52978c2ecf20Sopenharmony_ci	 * a command packet that enables reception of all multicast
52988c2ecf20Sopenharmony_ci	 * packets.
52998c2ecf20Sopenharmony_ci	 */
53008c2ecf20Sopenharmony_ci	if (*total_flags & FIF_ALLMULTI) {
53018c2ecf20Sopenharmony_ci		kfree(cmd);
53028c2ecf20Sopenharmony_ci		cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, NULL);
53038c2ecf20Sopenharmony_ci	}
53048c2ecf20Sopenharmony_ci
53058c2ecf20Sopenharmony_ci	if (cmd != NULL) {
53068c2ecf20Sopenharmony_ci		mwl8k_post_cmd(hw, cmd);
53078c2ecf20Sopenharmony_ci		kfree(cmd);
53088c2ecf20Sopenharmony_ci	}
53098c2ecf20Sopenharmony_ci
53108c2ecf20Sopenharmony_ci	mwl8k_fw_unlock(hw);
53118c2ecf20Sopenharmony_ci}
53128c2ecf20Sopenharmony_ci
53138c2ecf20Sopenharmony_cistatic int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
53148c2ecf20Sopenharmony_ci{
53158c2ecf20Sopenharmony_ci	return mwl8k_cmd_set_rts_threshold(hw, value);
53168c2ecf20Sopenharmony_ci}
53178c2ecf20Sopenharmony_ci
53188c2ecf20Sopenharmony_cistatic int mwl8k_sta_remove(struct ieee80211_hw *hw,
53198c2ecf20Sopenharmony_ci			    struct ieee80211_vif *vif,
53208c2ecf20Sopenharmony_ci			    struct ieee80211_sta *sta)
53218c2ecf20Sopenharmony_ci{
53228c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
53238c2ecf20Sopenharmony_ci
53248c2ecf20Sopenharmony_ci	if (priv->ap_fw)
53258c2ecf20Sopenharmony_ci		return mwl8k_cmd_set_new_stn_del(hw, vif, sta->addr);
53268c2ecf20Sopenharmony_ci	else
53278c2ecf20Sopenharmony_ci		return mwl8k_cmd_update_stadb_del(hw, vif, sta->addr);
53288c2ecf20Sopenharmony_ci}
53298c2ecf20Sopenharmony_ci
53308c2ecf20Sopenharmony_cistatic int mwl8k_sta_add(struct ieee80211_hw *hw,
53318c2ecf20Sopenharmony_ci			 struct ieee80211_vif *vif,
53328c2ecf20Sopenharmony_ci			 struct ieee80211_sta *sta)
53338c2ecf20Sopenharmony_ci{
53348c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
53358c2ecf20Sopenharmony_ci	int ret;
53368c2ecf20Sopenharmony_ci	int i;
53378c2ecf20Sopenharmony_ci	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
53388c2ecf20Sopenharmony_ci	struct ieee80211_key_conf *key;
53398c2ecf20Sopenharmony_ci
53408c2ecf20Sopenharmony_ci	if (!priv->ap_fw) {
53418c2ecf20Sopenharmony_ci		ret = mwl8k_cmd_update_stadb_add(hw, vif, sta);
53428c2ecf20Sopenharmony_ci		if (ret >= 0) {
53438c2ecf20Sopenharmony_ci			MWL8K_STA(sta)->peer_id = ret;
53448c2ecf20Sopenharmony_ci			if (sta->ht_cap.ht_supported)
53458c2ecf20Sopenharmony_ci				MWL8K_STA(sta)->is_ampdu_allowed = true;
53468c2ecf20Sopenharmony_ci			ret = 0;
53478c2ecf20Sopenharmony_ci		}
53488c2ecf20Sopenharmony_ci
53498c2ecf20Sopenharmony_ci	} else {
53508c2ecf20Sopenharmony_ci		ret = mwl8k_cmd_set_new_stn_add(hw, vif, sta);
53518c2ecf20Sopenharmony_ci	}
53528c2ecf20Sopenharmony_ci
53538c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_WEP_KEYS; i++) {
53548c2ecf20Sopenharmony_ci		key = IEEE80211_KEY_CONF(mwl8k_vif->wep_key_conf[i].key);
53558c2ecf20Sopenharmony_ci		if (mwl8k_vif->wep_key_conf[i].enabled)
53568c2ecf20Sopenharmony_ci			mwl8k_set_key(hw, SET_KEY, vif, sta, key);
53578c2ecf20Sopenharmony_ci	}
53588c2ecf20Sopenharmony_ci	return ret;
53598c2ecf20Sopenharmony_ci}
53608c2ecf20Sopenharmony_ci
53618c2ecf20Sopenharmony_cistatic int mwl8k_conf_tx(struct ieee80211_hw *hw,
53628c2ecf20Sopenharmony_ci			 struct ieee80211_vif *vif, u16 queue,
53638c2ecf20Sopenharmony_ci			 const struct ieee80211_tx_queue_params *params)
53648c2ecf20Sopenharmony_ci{
53658c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
53668c2ecf20Sopenharmony_ci	int rc;
53678c2ecf20Sopenharmony_ci
53688c2ecf20Sopenharmony_ci	rc = mwl8k_fw_lock(hw);
53698c2ecf20Sopenharmony_ci	if (!rc) {
53708c2ecf20Sopenharmony_ci		BUG_ON(queue > MWL8K_TX_WMM_QUEUES - 1);
53718c2ecf20Sopenharmony_ci		memcpy(&priv->wmm_params[queue], params, sizeof(*params));
53728c2ecf20Sopenharmony_ci
53738c2ecf20Sopenharmony_ci		if (!priv->wmm_enabled)
53748c2ecf20Sopenharmony_ci			rc = mwl8k_cmd_set_wmm_mode(hw, 1);
53758c2ecf20Sopenharmony_ci
53768c2ecf20Sopenharmony_ci		if (!rc) {
53778c2ecf20Sopenharmony_ci			int q = MWL8K_TX_WMM_QUEUES - 1 - queue;
53788c2ecf20Sopenharmony_ci			rc = mwl8k_cmd_set_edca_params(hw, q,
53798c2ecf20Sopenharmony_ci						       params->cw_min,
53808c2ecf20Sopenharmony_ci						       params->cw_max,
53818c2ecf20Sopenharmony_ci						       params->aifs,
53828c2ecf20Sopenharmony_ci						       params->txop);
53838c2ecf20Sopenharmony_ci		}
53848c2ecf20Sopenharmony_ci
53858c2ecf20Sopenharmony_ci		mwl8k_fw_unlock(hw);
53868c2ecf20Sopenharmony_ci	}
53878c2ecf20Sopenharmony_ci
53888c2ecf20Sopenharmony_ci	return rc;
53898c2ecf20Sopenharmony_ci}
53908c2ecf20Sopenharmony_ci
53918c2ecf20Sopenharmony_cistatic int mwl8k_get_stats(struct ieee80211_hw *hw,
53928c2ecf20Sopenharmony_ci			   struct ieee80211_low_level_stats *stats)
53938c2ecf20Sopenharmony_ci{
53948c2ecf20Sopenharmony_ci	return mwl8k_cmd_get_stat(hw, stats);
53958c2ecf20Sopenharmony_ci}
53968c2ecf20Sopenharmony_ci
53978c2ecf20Sopenharmony_cistatic int mwl8k_get_survey(struct ieee80211_hw *hw, int idx,
53988c2ecf20Sopenharmony_ci				struct survey_info *survey)
53998c2ecf20Sopenharmony_ci{
54008c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
54018c2ecf20Sopenharmony_ci	struct ieee80211_conf *conf = &hw->conf;
54028c2ecf20Sopenharmony_ci	struct ieee80211_supported_band *sband;
54038c2ecf20Sopenharmony_ci
54048c2ecf20Sopenharmony_ci	if (priv->ap_fw) {
54058c2ecf20Sopenharmony_ci		sband = hw->wiphy->bands[NL80211_BAND_2GHZ];
54068c2ecf20Sopenharmony_ci
54078c2ecf20Sopenharmony_ci		if (sband && idx >= sband->n_channels) {
54088c2ecf20Sopenharmony_ci			idx -= sband->n_channels;
54098c2ecf20Sopenharmony_ci			sband = NULL;
54108c2ecf20Sopenharmony_ci		}
54118c2ecf20Sopenharmony_ci
54128c2ecf20Sopenharmony_ci		if (!sband)
54138c2ecf20Sopenharmony_ci			sband = hw->wiphy->bands[NL80211_BAND_5GHZ];
54148c2ecf20Sopenharmony_ci
54158c2ecf20Sopenharmony_ci		if (!sband || idx >= sband->n_channels)
54168c2ecf20Sopenharmony_ci			return -ENOENT;
54178c2ecf20Sopenharmony_ci
54188c2ecf20Sopenharmony_ci		memcpy(survey, &priv->survey[idx], sizeof(*survey));
54198c2ecf20Sopenharmony_ci		survey->channel = &sband->channels[idx];
54208c2ecf20Sopenharmony_ci
54218c2ecf20Sopenharmony_ci		return 0;
54228c2ecf20Sopenharmony_ci	}
54238c2ecf20Sopenharmony_ci
54248c2ecf20Sopenharmony_ci	if (idx != 0)
54258c2ecf20Sopenharmony_ci		return -ENOENT;
54268c2ecf20Sopenharmony_ci
54278c2ecf20Sopenharmony_ci	survey->channel = conf->chandef.chan;
54288c2ecf20Sopenharmony_ci	survey->filled = SURVEY_INFO_NOISE_DBM;
54298c2ecf20Sopenharmony_ci	survey->noise = priv->noise;
54308c2ecf20Sopenharmony_ci
54318c2ecf20Sopenharmony_ci	return 0;
54328c2ecf20Sopenharmony_ci}
54338c2ecf20Sopenharmony_ci
54348c2ecf20Sopenharmony_ci#define MAX_AMPDU_ATTEMPTS 5
54358c2ecf20Sopenharmony_ci
54368c2ecf20Sopenharmony_cistatic int
54378c2ecf20Sopenharmony_cimwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
54388c2ecf20Sopenharmony_ci		   struct ieee80211_ampdu_params *params)
54398c2ecf20Sopenharmony_ci{
54408c2ecf20Sopenharmony_ci	struct ieee80211_sta *sta = params->sta;
54418c2ecf20Sopenharmony_ci	enum ieee80211_ampdu_mlme_action action = params->action;
54428c2ecf20Sopenharmony_ci	u16 tid = params->tid;
54438c2ecf20Sopenharmony_ci	u16 *ssn = &params->ssn;
54448c2ecf20Sopenharmony_ci	u8 buf_size = params->buf_size;
54458c2ecf20Sopenharmony_ci	int i, rc = 0;
54468c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
54478c2ecf20Sopenharmony_ci	struct mwl8k_ampdu_stream *stream;
54488c2ecf20Sopenharmony_ci	u8 *addr = sta->addr, idx;
54498c2ecf20Sopenharmony_ci	struct mwl8k_sta *sta_info = MWL8K_STA(sta);
54508c2ecf20Sopenharmony_ci
54518c2ecf20Sopenharmony_ci	if (!ieee80211_hw_check(hw, AMPDU_AGGREGATION))
54528c2ecf20Sopenharmony_ci		return -ENOTSUPP;
54538c2ecf20Sopenharmony_ci
54548c2ecf20Sopenharmony_ci	spin_lock(&priv->stream_lock);
54558c2ecf20Sopenharmony_ci	stream = mwl8k_lookup_stream(hw, addr, tid);
54568c2ecf20Sopenharmony_ci
54578c2ecf20Sopenharmony_ci	switch (action) {
54588c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_RX_START:
54598c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_RX_STOP:
54608c2ecf20Sopenharmony_ci		break;
54618c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_TX_START:
54628c2ecf20Sopenharmony_ci		/* By the time we get here the hw queues may contain outgoing
54638c2ecf20Sopenharmony_ci		 * packets for this RA/TID that are not part of this BA
54648c2ecf20Sopenharmony_ci		 * session.  The hw will assign sequence numbers to these
54658c2ecf20Sopenharmony_ci		 * packets as they go out.  So if we query the hw for its next
54668c2ecf20Sopenharmony_ci		 * sequence number and use that for the SSN here, it may end up
54678c2ecf20Sopenharmony_ci		 * being wrong, which will lead to sequence number mismatch at
54688c2ecf20Sopenharmony_ci		 * the recipient.  To avoid this, we reset the sequence number
54698c2ecf20Sopenharmony_ci		 * to O for the first MPDU in this BA stream.
54708c2ecf20Sopenharmony_ci		 */
54718c2ecf20Sopenharmony_ci		*ssn = 0;
54728c2ecf20Sopenharmony_ci		if (stream == NULL) {
54738c2ecf20Sopenharmony_ci			/* This means that somebody outside this driver called
54748c2ecf20Sopenharmony_ci			 * ieee80211_start_tx_ba_session.  This is unexpected
54758c2ecf20Sopenharmony_ci			 * because we do our own rate control.  Just warn and
54768c2ecf20Sopenharmony_ci			 * move on.
54778c2ecf20Sopenharmony_ci			 */
54788c2ecf20Sopenharmony_ci			wiphy_warn(hw->wiphy, "Unexpected call to %s.  "
54798c2ecf20Sopenharmony_ci				   "Proceeding anyway.\n", __func__);
54808c2ecf20Sopenharmony_ci			stream = mwl8k_add_stream(hw, sta, tid);
54818c2ecf20Sopenharmony_ci		}
54828c2ecf20Sopenharmony_ci		if (stream == NULL) {
54838c2ecf20Sopenharmony_ci			wiphy_debug(hw->wiphy, "no free AMPDU streams\n");
54848c2ecf20Sopenharmony_ci			rc = -EBUSY;
54858c2ecf20Sopenharmony_ci			break;
54868c2ecf20Sopenharmony_ci		}
54878c2ecf20Sopenharmony_ci		stream->state = AMPDU_STREAM_IN_PROGRESS;
54888c2ecf20Sopenharmony_ci
54898c2ecf20Sopenharmony_ci		/* Release the lock before we do the time consuming stuff */
54908c2ecf20Sopenharmony_ci		spin_unlock(&priv->stream_lock);
54918c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_AMPDU_ATTEMPTS; i++) {
54928c2ecf20Sopenharmony_ci
54938c2ecf20Sopenharmony_ci			/* Check if link is still valid */
54948c2ecf20Sopenharmony_ci			if (!sta_info->is_ampdu_allowed) {
54958c2ecf20Sopenharmony_ci				spin_lock(&priv->stream_lock);
54968c2ecf20Sopenharmony_ci				mwl8k_remove_stream(hw, stream);
54978c2ecf20Sopenharmony_ci				spin_unlock(&priv->stream_lock);
54988c2ecf20Sopenharmony_ci				return -EBUSY;
54998c2ecf20Sopenharmony_ci			}
55008c2ecf20Sopenharmony_ci
55018c2ecf20Sopenharmony_ci			rc = mwl8k_check_ba(hw, stream, vif);
55028c2ecf20Sopenharmony_ci
55038c2ecf20Sopenharmony_ci			/* If HW restart is in progress mwl8k_post_cmd will
55048c2ecf20Sopenharmony_ci			 * return -EBUSY. Avoid retrying mwl8k_check_ba in
55058c2ecf20Sopenharmony_ci			 * such cases
55068c2ecf20Sopenharmony_ci			 */
55078c2ecf20Sopenharmony_ci			if (!rc || rc == -EBUSY)
55088c2ecf20Sopenharmony_ci				break;
55098c2ecf20Sopenharmony_ci			/*
55108c2ecf20Sopenharmony_ci			 * HW queues take time to be flushed, give them
55118c2ecf20Sopenharmony_ci			 * sufficient time
55128c2ecf20Sopenharmony_ci			 */
55138c2ecf20Sopenharmony_ci
55148c2ecf20Sopenharmony_ci			msleep(1000);
55158c2ecf20Sopenharmony_ci		}
55168c2ecf20Sopenharmony_ci		spin_lock(&priv->stream_lock);
55178c2ecf20Sopenharmony_ci		if (rc) {
55188c2ecf20Sopenharmony_ci			wiphy_err(hw->wiphy, "Stream for tid %d busy after %d"
55198c2ecf20Sopenharmony_ci				" attempts\n", tid, MAX_AMPDU_ATTEMPTS);
55208c2ecf20Sopenharmony_ci			mwl8k_remove_stream(hw, stream);
55218c2ecf20Sopenharmony_ci			rc = -EBUSY;
55228c2ecf20Sopenharmony_ci			break;
55238c2ecf20Sopenharmony_ci		}
55248c2ecf20Sopenharmony_ci		rc = IEEE80211_AMPDU_TX_START_IMMEDIATE;
55258c2ecf20Sopenharmony_ci		break;
55268c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_CONT:
55278c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_FLUSH:
55288c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
55298c2ecf20Sopenharmony_ci		if (stream) {
55308c2ecf20Sopenharmony_ci			if (stream->state == AMPDU_STREAM_ACTIVE) {
55318c2ecf20Sopenharmony_ci				idx = stream->idx;
55328c2ecf20Sopenharmony_ci				spin_unlock(&priv->stream_lock);
55338c2ecf20Sopenharmony_ci				mwl8k_destroy_ba(hw, idx);
55348c2ecf20Sopenharmony_ci				spin_lock(&priv->stream_lock);
55358c2ecf20Sopenharmony_ci			}
55368c2ecf20Sopenharmony_ci			mwl8k_remove_stream(hw, stream);
55378c2ecf20Sopenharmony_ci		}
55388c2ecf20Sopenharmony_ci		ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid);
55398c2ecf20Sopenharmony_ci		break;
55408c2ecf20Sopenharmony_ci	case IEEE80211_AMPDU_TX_OPERATIONAL:
55418c2ecf20Sopenharmony_ci		BUG_ON(stream == NULL);
55428c2ecf20Sopenharmony_ci		BUG_ON(stream->state != AMPDU_STREAM_IN_PROGRESS);
55438c2ecf20Sopenharmony_ci		spin_unlock(&priv->stream_lock);
55448c2ecf20Sopenharmony_ci		rc = mwl8k_create_ba(hw, stream, buf_size, vif);
55458c2ecf20Sopenharmony_ci		spin_lock(&priv->stream_lock);
55468c2ecf20Sopenharmony_ci		if (!rc)
55478c2ecf20Sopenharmony_ci			stream->state = AMPDU_STREAM_ACTIVE;
55488c2ecf20Sopenharmony_ci		else {
55498c2ecf20Sopenharmony_ci			idx = stream->idx;
55508c2ecf20Sopenharmony_ci			spin_unlock(&priv->stream_lock);
55518c2ecf20Sopenharmony_ci			mwl8k_destroy_ba(hw, idx);
55528c2ecf20Sopenharmony_ci			spin_lock(&priv->stream_lock);
55538c2ecf20Sopenharmony_ci			wiphy_debug(hw->wiphy,
55548c2ecf20Sopenharmony_ci				"Failed adding stream for sta %pM tid %d\n",
55558c2ecf20Sopenharmony_ci				addr, tid);
55568c2ecf20Sopenharmony_ci			mwl8k_remove_stream(hw, stream);
55578c2ecf20Sopenharmony_ci		}
55588c2ecf20Sopenharmony_ci		break;
55598c2ecf20Sopenharmony_ci
55608c2ecf20Sopenharmony_ci	default:
55618c2ecf20Sopenharmony_ci		rc = -ENOTSUPP;
55628c2ecf20Sopenharmony_ci	}
55638c2ecf20Sopenharmony_ci
55648c2ecf20Sopenharmony_ci	spin_unlock(&priv->stream_lock);
55658c2ecf20Sopenharmony_ci	return rc;
55668c2ecf20Sopenharmony_ci}
55678c2ecf20Sopenharmony_ci
55688c2ecf20Sopenharmony_cistatic void mwl8k_sw_scan_start(struct ieee80211_hw *hw,
55698c2ecf20Sopenharmony_ci				struct ieee80211_vif *vif,
55708c2ecf20Sopenharmony_ci				const u8 *mac_addr)
55718c2ecf20Sopenharmony_ci{
55728c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
55738c2ecf20Sopenharmony_ci	u8 tmp;
55748c2ecf20Sopenharmony_ci
55758c2ecf20Sopenharmony_ci	if (!priv->ap_fw)
55768c2ecf20Sopenharmony_ci		return;
55778c2ecf20Sopenharmony_ci
55788c2ecf20Sopenharmony_ci	/* clear all stats */
55798c2ecf20Sopenharmony_ci	priv->channel_time = 0;
55808c2ecf20Sopenharmony_ci	ioread32(priv->regs + BBU_RXRDY_CNT_REG);
55818c2ecf20Sopenharmony_ci	ioread32(priv->regs + NOK_CCA_CNT_REG);
55828c2ecf20Sopenharmony_ci	mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp);
55838c2ecf20Sopenharmony_ci
55848c2ecf20Sopenharmony_ci	priv->sw_scan_start = true;
55858c2ecf20Sopenharmony_ci}
55868c2ecf20Sopenharmony_ci
55878c2ecf20Sopenharmony_cistatic void mwl8k_sw_scan_complete(struct ieee80211_hw *hw,
55888c2ecf20Sopenharmony_ci				   struct ieee80211_vif *vif)
55898c2ecf20Sopenharmony_ci{
55908c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
55918c2ecf20Sopenharmony_ci	u8 tmp;
55928c2ecf20Sopenharmony_ci
55938c2ecf20Sopenharmony_ci	if (!priv->ap_fw)
55948c2ecf20Sopenharmony_ci		return;
55958c2ecf20Sopenharmony_ci
55968c2ecf20Sopenharmony_ci	priv->sw_scan_start = false;
55978c2ecf20Sopenharmony_ci
55988c2ecf20Sopenharmony_ci	/* clear all stats */
55998c2ecf20Sopenharmony_ci	priv->channel_time = 0;
56008c2ecf20Sopenharmony_ci	ioread32(priv->regs + BBU_RXRDY_CNT_REG);
56018c2ecf20Sopenharmony_ci	ioread32(priv->regs + NOK_CCA_CNT_REG);
56028c2ecf20Sopenharmony_ci	mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp);
56038c2ecf20Sopenharmony_ci}
56048c2ecf20Sopenharmony_ci
56058c2ecf20Sopenharmony_cistatic const struct ieee80211_ops mwl8k_ops = {
56068c2ecf20Sopenharmony_ci	.tx			= mwl8k_tx,
56078c2ecf20Sopenharmony_ci	.start			= mwl8k_start,
56088c2ecf20Sopenharmony_ci	.stop			= mwl8k_stop,
56098c2ecf20Sopenharmony_ci	.add_interface		= mwl8k_add_interface,
56108c2ecf20Sopenharmony_ci	.remove_interface	= mwl8k_remove_interface,
56118c2ecf20Sopenharmony_ci	.config			= mwl8k_config,
56128c2ecf20Sopenharmony_ci	.bss_info_changed	= mwl8k_bss_info_changed,
56138c2ecf20Sopenharmony_ci	.prepare_multicast	= mwl8k_prepare_multicast,
56148c2ecf20Sopenharmony_ci	.configure_filter	= mwl8k_configure_filter,
56158c2ecf20Sopenharmony_ci	.set_key                = mwl8k_set_key,
56168c2ecf20Sopenharmony_ci	.set_rts_threshold	= mwl8k_set_rts_threshold,
56178c2ecf20Sopenharmony_ci	.sta_add		= mwl8k_sta_add,
56188c2ecf20Sopenharmony_ci	.sta_remove		= mwl8k_sta_remove,
56198c2ecf20Sopenharmony_ci	.conf_tx		= mwl8k_conf_tx,
56208c2ecf20Sopenharmony_ci	.get_stats		= mwl8k_get_stats,
56218c2ecf20Sopenharmony_ci	.get_survey		= mwl8k_get_survey,
56228c2ecf20Sopenharmony_ci	.ampdu_action		= mwl8k_ampdu_action,
56238c2ecf20Sopenharmony_ci	.sw_scan_start		= mwl8k_sw_scan_start,
56248c2ecf20Sopenharmony_ci	.sw_scan_complete	= mwl8k_sw_scan_complete,
56258c2ecf20Sopenharmony_ci};
56268c2ecf20Sopenharmony_ci
56278c2ecf20Sopenharmony_cistatic void mwl8k_finalize_join_worker(struct work_struct *work)
56288c2ecf20Sopenharmony_ci{
56298c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv =
56308c2ecf20Sopenharmony_ci		container_of(work, struct mwl8k_priv, finalize_join_worker);
56318c2ecf20Sopenharmony_ci	struct sk_buff *skb = priv->beacon_skb;
56328c2ecf20Sopenharmony_ci	struct ieee80211_mgmt *mgmt = (void *)skb->data;
56338c2ecf20Sopenharmony_ci	int len = skb->len - offsetof(struct ieee80211_mgmt, u.beacon.variable);
56348c2ecf20Sopenharmony_ci	const u8 *tim = cfg80211_find_ie(WLAN_EID_TIM,
56358c2ecf20Sopenharmony_ci					 mgmt->u.beacon.variable, len);
56368c2ecf20Sopenharmony_ci	int dtim_period = 1;
56378c2ecf20Sopenharmony_ci
56388c2ecf20Sopenharmony_ci	if (tim && tim[1] >= 2)
56398c2ecf20Sopenharmony_ci		dtim_period = tim[3];
56408c2ecf20Sopenharmony_ci
56418c2ecf20Sopenharmony_ci	mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len, dtim_period);
56428c2ecf20Sopenharmony_ci
56438c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
56448c2ecf20Sopenharmony_ci	priv->beacon_skb = NULL;
56458c2ecf20Sopenharmony_ci}
56468c2ecf20Sopenharmony_ci
56478c2ecf20Sopenharmony_cienum {
56488c2ecf20Sopenharmony_ci	MWL8363 = 0,
56498c2ecf20Sopenharmony_ci	MWL8687,
56508c2ecf20Sopenharmony_ci	MWL8366,
56518c2ecf20Sopenharmony_ci	MWL8764,
56528c2ecf20Sopenharmony_ci};
56538c2ecf20Sopenharmony_ci
56548c2ecf20Sopenharmony_ci#define MWL8K_8366_AP_FW_API 3
56558c2ecf20Sopenharmony_ci#define _MWL8K_8366_AP_FW(api) "mwl8k/fmimage_8366_ap-" #api ".fw"
56568c2ecf20Sopenharmony_ci#define MWL8K_8366_AP_FW(api) _MWL8K_8366_AP_FW(api)
56578c2ecf20Sopenharmony_ci
56588c2ecf20Sopenharmony_ci#define MWL8K_8764_AP_FW_API 1
56598c2ecf20Sopenharmony_ci#define _MWL8K_8764_AP_FW(api) "mwl8k/fmimage_8764_ap-" #api ".fw"
56608c2ecf20Sopenharmony_ci#define MWL8K_8764_AP_FW(api) _MWL8K_8764_AP_FW(api)
56618c2ecf20Sopenharmony_ci
56628c2ecf20Sopenharmony_cistatic struct mwl8k_device_info mwl8k_info_tbl[] = {
56638c2ecf20Sopenharmony_ci	[MWL8363] = {
56648c2ecf20Sopenharmony_ci		.part_name	= "88w8363",
56658c2ecf20Sopenharmony_ci		.helper_image	= "mwl8k/helper_8363.fw",
56668c2ecf20Sopenharmony_ci		.fw_image_sta	= "mwl8k/fmimage_8363.fw",
56678c2ecf20Sopenharmony_ci	},
56688c2ecf20Sopenharmony_ci	[MWL8687] = {
56698c2ecf20Sopenharmony_ci		.part_name	= "88w8687",
56708c2ecf20Sopenharmony_ci		.helper_image	= "mwl8k/helper_8687.fw",
56718c2ecf20Sopenharmony_ci		.fw_image_sta	= "mwl8k/fmimage_8687.fw",
56728c2ecf20Sopenharmony_ci	},
56738c2ecf20Sopenharmony_ci	[MWL8366] = {
56748c2ecf20Sopenharmony_ci		.part_name	= "88w8366",
56758c2ecf20Sopenharmony_ci		.helper_image	= "mwl8k/helper_8366.fw",
56768c2ecf20Sopenharmony_ci		.fw_image_sta	= "mwl8k/fmimage_8366.fw",
56778c2ecf20Sopenharmony_ci		.fw_image_ap	= MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API),
56788c2ecf20Sopenharmony_ci		.fw_api_ap	= MWL8K_8366_AP_FW_API,
56798c2ecf20Sopenharmony_ci		.ap_rxd_ops	= &rxd_ap_ops,
56808c2ecf20Sopenharmony_ci	},
56818c2ecf20Sopenharmony_ci	[MWL8764] = {
56828c2ecf20Sopenharmony_ci		.part_name	= "88w8764",
56838c2ecf20Sopenharmony_ci		.fw_image_ap	= MWL8K_8764_AP_FW(MWL8K_8764_AP_FW_API),
56848c2ecf20Sopenharmony_ci		.fw_api_ap	= MWL8K_8764_AP_FW_API,
56858c2ecf20Sopenharmony_ci		.ap_rxd_ops	= &rxd_ap_ops,
56868c2ecf20Sopenharmony_ci	},
56878c2ecf20Sopenharmony_ci};
56888c2ecf20Sopenharmony_ci
56898c2ecf20Sopenharmony_ciMODULE_FIRMWARE("mwl8k/helper_8363.fw");
56908c2ecf20Sopenharmony_ciMODULE_FIRMWARE("mwl8k/fmimage_8363.fw");
56918c2ecf20Sopenharmony_ciMODULE_FIRMWARE("mwl8k/helper_8687.fw");
56928c2ecf20Sopenharmony_ciMODULE_FIRMWARE("mwl8k/fmimage_8687.fw");
56938c2ecf20Sopenharmony_ciMODULE_FIRMWARE("mwl8k/helper_8366.fw");
56948c2ecf20Sopenharmony_ciMODULE_FIRMWARE("mwl8k/fmimage_8366.fw");
56958c2ecf20Sopenharmony_ciMODULE_FIRMWARE(MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API));
56968c2ecf20Sopenharmony_ci
56978c2ecf20Sopenharmony_cistatic const struct pci_device_id mwl8k_pci_id_table[] = {
56988c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a0a), .driver_data = MWL8363, },
56998c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, },
57008c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, },
57018c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, },
57028c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, },
57038c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, },
57048c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a41), .driver_data = MWL8366, },
57058c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a42), .driver_data = MWL8366, },
57068c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2a43), .driver_data = MWL8366, },
57078c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MARVELL, 0x2b36), .driver_data = MWL8764, },
57088c2ecf20Sopenharmony_ci	{ },
57098c2ecf20Sopenharmony_ci};
57108c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table);
57118c2ecf20Sopenharmony_ci
57128c2ecf20Sopenharmony_cistatic int mwl8k_request_alt_fw(struct mwl8k_priv *priv)
57138c2ecf20Sopenharmony_ci{
57148c2ecf20Sopenharmony_ci	int rc;
57158c2ecf20Sopenharmony_ci	printk(KERN_ERR "%s: Error requesting preferred fw %s.\n"
57168c2ecf20Sopenharmony_ci	       "Trying alternative firmware %s\n", pci_name(priv->pdev),
57178c2ecf20Sopenharmony_ci	       priv->fw_pref, priv->fw_alt);
57188c2ecf20Sopenharmony_ci	rc = mwl8k_request_fw(priv, priv->fw_alt, &priv->fw_ucode, true);
57198c2ecf20Sopenharmony_ci	if (rc) {
57208c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: Error requesting alt fw %s\n",
57218c2ecf20Sopenharmony_ci		       pci_name(priv->pdev), priv->fw_alt);
57228c2ecf20Sopenharmony_ci		return rc;
57238c2ecf20Sopenharmony_ci	}
57248c2ecf20Sopenharmony_ci	return 0;
57258c2ecf20Sopenharmony_ci}
57268c2ecf20Sopenharmony_ci
57278c2ecf20Sopenharmony_cistatic int mwl8k_firmware_load_success(struct mwl8k_priv *priv);
57288c2ecf20Sopenharmony_cistatic void mwl8k_fw_state_machine(const struct firmware *fw, void *context)
57298c2ecf20Sopenharmony_ci{
57308c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = context;
57318c2ecf20Sopenharmony_ci	struct mwl8k_device_info *di = priv->device_info;
57328c2ecf20Sopenharmony_ci	int rc;
57338c2ecf20Sopenharmony_ci
57348c2ecf20Sopenharmony_ci	switch (priv->fw_state) {
57358c2ecf20Sopenharmony_ci	case FW_STATE_INIT:
57368c2ecf20Sopenharmony_ci		if (!fw) {
57378c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: Error requesting helper fw %s\n",
57388c2ecf20Sopenharmony_ci			       pci_name(priv->pdev), di->helper_image);
57398c2ecf20Sopenharmony_ci			goto fail;
57408c2ecf20Sopenharmony_ci		}
57418c2ecf20Sopenharmony_ci		priv->fw_helper = fw;
57428c2ecf20Sopenharmony_ci		rc = mwl8k_request_fw(priv, priv->fw_pref, &priv->fw_ucode,
57438c2ecf20Sopenharmony_ci				      true);
57448c2ecf20Sopenharmony_ci		if (rc && priv->fw_alt) {
57458c2ecf20Sopenharmony_ci			rc = mwl8k_request_alt_fw(priv);
57468c2ecf20Sopenharmony_ci			if (rc)
57478c2ecf20Sopenharmony_ci				goto fail;
57488c2ecf20Sopenharmony_ci			priv->fw_state = FW_STATE_LOADING_ALT;
57498c2ecf20Sopenharmony_ci		} else if (rc)
57508c2ecf20Sopenharmony_ci			goto fail;
57518c2ecf20Sopenharmony_ci		else
57528c2ecf20Sopenharmony_ci			priv->fw_state = FW_STATE_LOADING_PREF;
57538c2ecf20Sopenharmony_ci		break;
57548c2ecf20Sopenharmony_ci
57558c2ecf20Sopenharmony_ci	case FW_STATE_LOADING_PREF:
57568c2ecf20Sopenharmony_ci		if (!fw) {
57578c2ecf20Sopenharmony_ci			if (priv->fw_alt) {
57588c2ecf20Sopenharmony_ci				rc = mwl8k_request_alt_fw(priv);
57598c2ecf20Sopenharmony_ci				if (rc)
57608c2ecf20Sopenharmony_ci					goto fail;
57618c2ecf20Sopenharmony_ci				priv->fw_state = FW_STATE_LOADING_ALT;
57628c2ecf20Sopenharmony_ci			} else
57638c2ecf20Sopenharmony_ci				goto fail;
57648c2ecf20Sopenharmony_ci		} else {
57658c2ecf20Sopenharmony_ci			priv->fw_ucode = fw;
57668c2ecf20Sopenharmony_ci			rc = mwl8k_firmware_load_success(priv);
57678c2ecf20Sopenharmony_ci			if (rc)
57688c2ecf20Sopenharmony_ci				goto fail;
57698c2ecf20Sopenharmony_ci			else
57708c2ecf20Sopenharmony_ci				complete(&priv->firmware_loading_complete);
57718c2ecf20Sopenharmony_ci		}
57728c2ecf20Sopenharmony_ci		break;
57738c2ecf20Sopenharmony_ci
57748c2ecf20Sopenharmony_ci	case FW_STATE_LOADING_ALT:
57758c2ecf20Sopenharmony_ci		if (!fw) {
57768c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: Error requesting alt fw %s\n",
57778c2ecf20Sopenharmony_ci			       pci_name(priv->pdev), di->helper_image);
57788c2ecf20Sopenharmony_ci			goto fail;
57798c2ecf20Sopenharmony_ci		}
57808c2ecf20Sopenharmony_ci		priv->fw_ucode = fw;
57818c2ecf20Sopenharmony_ci		rc = mwl8k_firmware_load_success(priv);
57828c2ecf20Sopenharmony_ci		if (rc)
57838c2ecf20Sopenharmony_ci			goto fail;
57848c2ecf20Sopenharmony_ci		else
57858c2ecf20Sopenharmony_ci			complete(&priv->firmware_loading_complete);
57868c2ecf20Sopenharmony_ci		break;
57878c2ecf20Sopenharmony_ci
57888c2ecf20Sopenharmony_ci	default:
57898c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: Unexpected firmware loading state: %d\n",
57908c2ecf20Sopenharmony_ci		       MWL8K_NAME, priv->fw_state);
57918c2ecf20Sopenharmony_ci		BUG_ON(1);
57928c2ecf20Sopenharmony_ci	}
57938c2ecf20Sopenharmony_ci
57948c2ecf20Sopenharmony_ci	return;
57958c2ecf20Sopenharmony_ci
57968c2ecf20Sopenharmony_cifail:
57978c2ecf20Sopenharmony_ci	priv->fw_state = FW_STATE_ERROR;
57988c2ecf20Sopenharmony_ci	complete(&priv->firmware_loading_complete);
57998c2ecf20Sopenharmony_ci	mwl8k_release_firmware(priv);
58008c2ecf20Sopenharmony_ci	device_release_driver(&priv->pdev->dev);
58018c2ecf20Sopenharmony_ci}
58028c2ecf20Sopenharmony_ci
58038c2ecf20Sopenharmony_ci#define MAX_RESTART_ATTEMPTS 1
58048c2ecf20Sopenharmony_cistatic int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image,
58058c2ecf20Sopenharmony_ci			       bool nowait)
58068c2ecf20Sopenharmony_ci{
58078c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
58088c2ecf20Sopenharmony_ci	int rc;
58098c2ecf20Sopenharmony_ci	int count = MAX_RESTART_ATTEMPTS;
58108c2ecf20Sopenharmony_ci
58118c2ecf20Sopenharmony_ciretry:
58128c2ecf20Sopenharmony_ci	/* Reset firmware and hardware */
58138c2ecf20Sopenharmony_ci	mwl8k_hw_reset(priv);
58148c2ecf20Sopenharmony_ci
58158c2ecf20Sopenharmony_ci	/* Ask userland hotplug daemon for the device firmware */
58168c2ecf20Sopenharmony_ci	rc = mwl8k_request_firmware(priv, fw_image, nowait);
58178c2ecf20Sopenharmony_ci	if (rc) {
58188c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy, "Firmware files not found\n");
58198c2ecf20Sopenharmony_ci		return rc;
58208c2ecf20Sopenharmony_ci	}
58218c2ecf20Sopenharmony_ci
58228c2ecf20Sopenharmony_ci	if (nowait)
58238c2ecf20Sopenharmony_ci		return rc;
58248c2ecf20Sopenharmony_ci
58258c2ecf20Sopenharmony_ci	/* Load firmware into hardware */
58268c2ecf20Sopenharmony_ci	rc = mwl8k_load_firmware(hw);
58278c2ecf20Sopenharmony_ci	if (rc)
58288c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy, "Cannot start firmware\n");
58298c2ecf20Sopenharmony_ci
58308c2ecf20Sopenharmony_ci	/* Reclaim memory once firmware is successfully loaded */
58318c2ecf20Sopenharmony_ci	mwl8k_release_firmware(priv);
58328c2ecf20Sopenharmony_ci
58338c2ecf20Sopenharmony_ci	if (rc && count) {
58348c2ecf20Sopenharmony_ci		/* FW did not start successfully;
58358c2ecf20Sopenharmony_ci		 * lets try one more time
58368c2ecf20Sopenharmony_ci		 */
58378c2ecf20Sopenharmony_ci		count--;
58388c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy, "Trying to reload the firmware again\n");
58398c2ecf20Sopenharmony_ci		msleep(20);
58408c2ecf20Sopenharmony_ci		goto retry;
58418c2ecf20Sopenharmony_ci	}
58428c2ecf20Sopenharmony_ci
58438c2ecf20Sopenharmony_ci	return rc;
58448c2ecf20Sopenharmony_ci}
58458c2ecf20Sopenharmony_ci
58468c2ecf20Sopenharmony_cistatic int mwl8k_init_txqs(struct ieee80211_hw *hw)
58478c2ecf20Sopenharmony_ci{
58488c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
58498c2ecf20Sopenharmony_ci	int rc = 0;
58508c2ecf20Sopenharmony_ci	int i;
58518c2ecf20Sopenharmony_ci
58528c2ecf20Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++) {
58538c2ecf20Sopenharmony_ci		rc = mwl8k_txq_init(hw, i);
58548c2ecf20Sopenharmony_ci		if (rc)
58558c2ecf20Sopenharmony_ci			break;
58568c2ecf20Sopenharmony_ci		if (priv->ap_fw)
58578c2ecf20Sopenharmony_ci			iowrite32(priv->txq[i].txd_dma,
58588c2ecf20Sopenharmony_ci				  priv->sram + priv->txq_offset[i]);
58598c2ecf20Sopenharmony_ci	}
58608c2ecf20Sopenharmony_ci	return rc;
58618c2ecf20Sopenharmony_ci}
58628c2ecf20Sopenharmony_ci
58638c2ecf20Sopenharmony_ci/* initialize hw after successfully loading a firmware image */
58648c2ecf20Sopenharmony_cistatic int mwl8k_probe_hw(struct ieee80211_hw *hw)
58658c2ecf20Sopenharmony_ci{
58668c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
58678c2ecf20Sopenharmony_ci	int rc = 0;
58688c2ecf20Sopenharmony_ci	int i;
58698c2ecf20Sopenharmony_ci
58708c2ecf20Sopenharmony_ci	if (priv->ap_fw) {
58718c2ecf20Sopenharmony_ci		priv->rxd_ops = priv->device_info->ap_rxd_ops;
58728c2ecf20Sopenharmony_ci		if (priv->rxd_ops == NULL) {
58738c2ecf20Sopenharmony_ci			wiphy_err(hw->wiphy,
58748c2ecf20Sopenharmony_ci				  "Driver does not have AP firmware image support for this hardware\n");
58758c2ecf20Sopenharmony_ci			rc = -ENOENT;
58768c2ecf20Sopenharmony_ci			goto err_stop_firmware;
58778c2ecf20Sopenharmony_ci		}
58788c2ecf20Sopenharmony_ci	} else {
58798c2ecf20Sopenharmony_ci		priv->rxd_ops = &rxd_sta_ops;
58808c2ecf20Sopenharmony_ci	}
58818c2ecf20Sopenharmony_ci
58828c2ecf20Sopenharmony_ci	priv->sniffer_enabled = false;
58838c2ecf20Sopenharmony_ci	priv->wmm_enabled = false;
58848c2ecf20Sopenharmony_ci	priv->pending_tx_pkts = 0;
58858c2ecf20Sopenharmony_ci	atomic_set(&priv->watchdog_event_pending, 0);
58868c2ecf20Sopenharmony_ci
58878c2ecf20Sopenharmony_ci	rc = mwl8k_rxq_init(hw, 0);
58888c2ecf20Sopenharmony_ci	if (rc)
58898c2ecf20Sopenharmony_ci		goto err_stop_firmware;
58908c2ecf20Sopenharmony_ci	rxq_refill(hw, 0, INT_MAX);
58918c2ecf20Sopenharmony_ci
58928c2ecf20Sopenharmony_ci	/* For the sta firmware, we need to know the dma addresses of tx queues
58938c2ecf20Sopenharmony_ci	 * before sending MWL8K_CMD_GET_HW_SPEC.  So we must initialize them
58948c2ecf20Sopenharmony_ci	 * prior to issuing this command.  But for the AP case, we learn the
58958c2ecf20Sopenharmony_ci	 * total number of queues from the result CMD_GET_HW_SPEC, so for this
58968c2ecf20Sopenharmony_ci	 * case we must initialize the tx queues after.
58978c2ecf20Sopenharmony_ci	 */
58988c2ecf20Sopenharmony_ci	priv->num_ampdu_queues = 0;
58998c2ecf20Sopenharmony_ci	if (!priv->ap_fw) {
59008c2ecf20Sopenharmony_ci		rc = mwl8k_init_txqs(hw);
59018c2ecf20Sopenharmony_ci		if (rc)
59028c2ecf20Sopenharmony_ci			goto err_free_queues;
59038c2ecf20Sopenharmony_ci	}
59048c2ecf20Sopenharmony_ci
59058c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
59068c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
59078c2ecf20Sopenharmony_ci	iowrite32(MWL8K_A2H_INT_TX_DONE|MWL8K_A2H_INT_RX_READY|
59088c2ecf20Sopenharmony_ci		  MWL8K_A2H_INT_BA_WATCHDOG,
59098c2ecf20Sopenharmony_ci		  priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL);
59108c2ecf20Sopenharmony_ci	iowrite32(MWL8K_A2H_INT_OPC_DONE,
59118c2ecf20Sopenharmony_ci		  priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
59128c2ecf20Sopenharmony_ci
59138c2ecf20Sopenharmony_ci	rc = request_irq(priv->pdev->irq, mwl8k_interrupt,
59148c2ecf20Sopenharmony_ci			 IRQF_SHARED, MWL8K_NAME, hw);
59158c2ecf20Sopenharmony_ci	if (rc) {
59168c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy, "failed to register IRQ handler\n");
59178c2ecf20Sopenharmony_ci		goto err_free_queues;
59188c2ecf20Sopenharmony_ci	}
59198c2ecf20Sopenharmony_ci
59208c2ecf20Sopenharmony_ci	/*
59218c2ecf20Sopenharmony_ci	 * When hw restart is requested,
59228c2ecf20Sopenharmony_ci	 * mac80211 will take care of clearing
59238c2ecf20Sopenharmony_ci	 * the ampdu streams, so do not clear
59248c2ecf20Sopenharmony_ci	 * the ampdu state here
59258c2ecf20Sopenharmony_ci	 */
59268c2ecf20Sopenharmony_ci	if (!priv->hw_restart_in_progress)
59278c2ecf20Sopenharmony_ci		memset(priv->ampdu, 0, sizeof(priv->ampdu));
59288c2ecf20Sopenharmony_ci
59298c2ecf20Sopenharmony_ci	/*
59308c2ecf20Sopenharmony_ci	 * Temporarily enable interrupts.  Initial firmware host
59318c2ecf20Sopenharmony_ci	 * commands use interrupts and avoid polling.  Disable
59328c2ecf20Sopenharmony_ci	 * interrupts when done.
59338c2ecf20Sopenharmony_ci	 */
59348c2ecf20Sopenharmony_ci	iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
59358c2ecf20Sopenharmony_ci
59368c2ecf20Sopenharmony_ci	/* Get config data, mac addrs etc */
59378c2ecf20Sopenharmony_ci	if (priv->ap_fw) {
59388c2ecf20Sopenharmony_ci		rc = mwl8k_cmd_get_hw_spec_ap(hw);
59398c2ecf20Sopenharmony_ci		if (!rc)
59408c2ecf20Sopenharmony_ci			rc = mwl8k_init_txqs(hw);
59418c2ecf20Sopenharmony_ci		if (!rc)
59428c2ecf20Sopenharmony_ci			rc = mwl8k_cmd_set_hw_spec(hw);
59438c2ecf20Sopenharmony_ci	} else {
59448c2ecf20Sopenharmony_ci		rc = mwl8k_cmd_get_hw_spec_sta(hw);
59458c2ecf20Sopenharmony_ci	}
59468c2ecf20Sopenharmony_ci	if (rc) {
59478c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy, "Cannot initialise firmware\n");
59488c2ecf20Sopenharmony_ci		goto err_free_irq;
59498c2ecf20Sopenharmony_ci	}
59508c2ecf20Sopenharmony_ci
59518c2ecf20Sopenharmony_ci	/* Turn radio off */
59528c2ecf20Sopenharmony_ci	rc = mwl8k_cmd_radio_disable(hw);
59538c2ecf20Sopenharmony_ci	if (rc) {
59548c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy, "Cannot disable\n");
59558c2ecf20Sopenharmony_ci		goto err_free_irq;
59568c2ecf20Sopenharmony_ci	}
59578c2ecf20Sopenharmony_ci
59588c2ecf20Sopenharmony_ci	/* Clear MAC address */
59598c2ecf20Sopenharmony_ci	rc = mwl8k_cmd_set_mac_addr(hw, NULL, "\x00\x00\x00\x00\x00\x00");
59608c2ecf20Sopenharmony_ci	if (rc) {
59618c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy, "Cannot clear MAC address\n");
59628c2ecf20Sopenharmony_ci		goto err_free_irq;
59638c2ecf20Sopenharmony_ci	}
59648c2ecf20Sopenharmony_ci
59658c2ecf20Sopenharmony_ci	/* Configure Antennas */
59668c2ecf20Sopenharmony_ci	rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3);
59678c2ecf20Sopenharmony_ci	if (rc)
59688c2ecf20Sopenharmony_ci		wiphy_warn(hw->wiphy, "failed to set # of RX antennas");
59698c2ecf20Sopenharmony_ci	rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7);
59708c2ecf20Sopenharmony_ci	if (rc)
59718c2ecf20Sopenharmony_ci		wiphy_warn(hw->wiphy, "failed to set # of TX antennas");
59728c2ecf20Sopenharmony_ci
59738c2ecf20Sopenharmony_ci
59748c2ecf20Sopenharmony_ci	/* Disable interrupts */
59758c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
59768c2ecf20Sopenharmony_ci	free_irq(priv->pdev->irq, hw);
59778c2ecf20Sopenharmony_ci
59788c2ecf20Sopenharmony_ci	wiphy_info(hw->wiphy, "%s v%d, %pm, %s firmware %u.%u.%u.%u\n",
59798c2ecf20Sopenharmony_ci		   priv->device_info->part_name,
59808c2ecf20Sopenharmony_ci		   priv->hw_rev, hw->wiphy->perm_addr,
59818c2ecf20Sopenharmony_ci		   priv->ap_fw ? "AP" : "STA",
59828c2ecf20Sopenharmony_ci		   (priv->fw_rev >> 24) & 0xff, (priv->fw_rev >> 16) & 0xff,
59838c2ecf20Sopenharmony_ci		   (priv->fw_rev >> 8) & 0xff, priv->fw_rev & 0xff);
59848c2ecf20Sopenharmony_ci
59858c2ecf20Sopenharmony_ci	return 0;
59868c2ecf20Sopenharmony_ci
59878c2ecf20Sopenharmony_cierr_free_irq:
59888c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
59898c2ecf20Sopenharmony_ci	free_irq(priv->pdev->irq, hw);
59908c2ecf20Sopenharmony_ci
59918c2ecf20Sopenharmony_cierr_free_queues:
59928c2ecf20Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
59938c2ecf20Sopenharmony_ci		mwl8k_txq_deinit(hw, i);
59948c2ecf20Sopenharmony_ci	mwl8k_rxq_deinit(hw, 0);
59958c2ecf20Sopenharmony_ci
59968c2ecf20Sopenharmony_cierr_stop_firmware:
59978c2ecf20Sopenharmony_ci	mwl8k_hw_reset(priv);
59988c2ecf20Sopenharmony_ci
59998c2ecf20Sopenharmony_ci	return rc;
60008c2ecf20Sopenharmony_ci}
60018c2ecf20Sopenharmony_ci
60028c2ecf20Sopenharmony_ci/*
60038c2ecf20Sopenharmony_ci * invoke mwl8k_reload_firmware to change the firmware image after the device
60048c2ecf20Sopenharmony_ci * has already been registered
60058c2ecf20Sopenharmony_ci */
60068c2ecf20Sopenharmony_cistatic int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)
60078c2ecf20Sopenharmony_ci{
60088c2ecf20Sopenharmony_ci	int i, rc = 0;
60098c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv = hw->priv;
60108c2ecf20Sopenharmony_ci	struct mwl8k_vif *vif, *tmp_vif;
60118c2ecf20Sopenharmony_ci
60128c2ecf20Sopenharmony_ci	mwl8k_stop(hw);
60138c2ecf20Sopenharmony_ci	mwl8k_rxq_deinit(hw, 0);
60148c2ecf20Sopenharmony_ci
60158c2ecf20Sopenharmony_ci	/*
60168c2ecf20Sopenharmony_ci	 * All the existing interfaces are re-added by the ieee80211_reconfig;
60178c2ecf20Sopenharmony_ci	 * which means driver should remove existing interfaces before calling
60188c2ecf20Sopenharmony_ci	 * ieee80211_restart_hw
60198c2ecf20Sopenharmony_ci	 */
60208c2ecf20Sopenharmony_ci	if (priv->hw_restart_in_progress)
60218c2ecf20Sopenharmony_ci		list_for_each_entry_safe(vif, tmp_vif, &priv->vif_list, list)
60228c2ecf20Sopenharmony_ci			mwl8k_remove_vif(priv, vif);
60238c2ecf20Sopenharmony_ci
60248c2ecf20Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
60258c2ecf20Sopenharmony_ci		mwl8k_txq_deinit(hw, i);
60268c2ecf20Sopenharmony_ci
60278c2ecf20Sopenharmony_ci	rc = mwl8k_init_firmware(hw, fw_image, false);
60288c2ecf20Sopenharmony_ci	if (rc)
60298c2ecf20Sopenharmony_ci		goto fail;
60308c2ecf20Sopenharmony_ci
60318c2ecf20Sopenharmony_ci	rc = mwl8k_probe_hw(hw);
60328c2ecf20Sopenharmony_ci	if (rc)
60338c2ecf20Sopenharmony_ci		goto fail;
60348c2ecf20Sopenharmony_ci
60358c2ecf20Sopenharmony_ci	if (priv->hw_restart_in_progress)
60368c2ecf20Sopenharmony_ci		return rc;
60378c2ecf20Sopenharmony_ci
60388c2ecf20Sopenharmony_ci	rc = mwl8k_start(hw);
60398c2ecf20Sopenharmony_ci	if (rc)
60408c2ecf20Sopenharmony_ci		goto fail;
60418c2ecf20Sopenharmony_ci
60428c2ecf20Sopenharmony_ci	rc = mwl8k_config(hw, ~0);
60438c2ecf20Sopenharmony_ci	if (rc)
60448c2ecf20Sopenharmony_ci		goto fail;
60458c2ecf20Sopenharmony_ci
60468c2ecf20Sopenharmony_ci	for (i = 0; i < MWL8K_TX_WMM_QUEUES; i++) {
60478c2ecf20Sopenharmony_ci		rc = mwl8k_conf_tx(hw, NULL, i, &priv->wmm_params[i]);
60488c2ecf20Sopenharmony_ci		if (rc)
60498c2ecf20Sopenharmony_ci			goto fail;
60508c2ecf20Sopenharmony_ci	}
60518c2ecf20Sopenharmony_ci
60528c2ecf20Sopenharmony_ci	return rc;
60538c2ecf20Sopenharmony_ci
60548c2ecf20Sopenharmony_cifail:
60558c2ecf20Sopenharmony_ci	printk(KERN_WARNING "mwl8k: Failed to reload firmware image.\n");
60568c2ecf20Sopenharmony_ci	return rc;
60578c2ecf20Sopenharmony_ci}
60588c2ecf20Sopenharmony_ci
60598c2ecf20Sopenharmony_cistatic const struct ieee80211_iface_limit ap_if_limits[] = {
60608c2ecf20Sopenharmony_ci	{ .max = 8,	.types = BIT(NL80211_IFTYPE_AP) },
60618c2ecf20Sopenharmony_ci	{ .max = 1,	.types = BIT(NL80211_IFTYPE_STATION) },
60628c2ecf20Sopenharmony_ci};
60638c2ecf20Sopenharmony_ci
60648c2ecf20Sopenharmony_cistatic const struct ieee80211_iface_combination ap_if_comb = {
60658c2ecf20Sopenharmony_ci	.limits = ap_if_limits,
60668c2ecf20Sopenharmony_ci	.n_limits = ARRAY_SIZE(ap_if_limits),
60678c2ecf20Sopenharmony_ci	.max_interfaces = 8,
60688c2ecf20Sopenharmony_ci	.num_different_channels = 1,
60698c2ecf20Sopenharmony_ci};
60708c2ecf20Sopenharmony_ci
60718c2ecf20Sopenharmony_ci
60728c2ecf20Sopenharmony_cistatic int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
60738c2ecf20Sopenharmony_ci{
60748c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = priv->hw;
60758c2ecf20Sopenharmony_ci	int i, rc;
60768c2ecf20Sopenharmony_ci
60778c2ecf20Sopenharmony_ci	rc = mwl8k_load_firmware(hw);
60788c2ecf20Sopenharmony_ci	mwl8k_release_firmware(priv);
60798c2ecf20Sopenharmony_ci	if (rc) {
60808c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy, "Cannot start firmware\n");
60818c2ecf20Sopenharmony_ci		return rc;
60828c2ecf20Sopenharmony_ci	}
60838c2ecf20Sopenharmony_ci
60848c2ecf20Sopenharmony_ci	/*
60858c2ecf20Sopenharmony_ci	 * Extra headroom is the size of the required DMA header
60868c2ecf20Sopenharmony_ci	 * minus the size of the smallest 802.11 frame (CTS frame).
60878c2ecf20Sopenharmony_ci	 */
60888c2ecf20Sopenharmony_ci	hw->extra_tx_headroom =
60898c2ecf20Sopenharmony_ci		sizeof(struct mwl8k_dma_data) - sizeof(struct ieee80211_cts);
60908c2ecf20Sopenharmony_ci
60918c2ecf20Sopenharmony_ci	hw->extra_tx_headroom -= priv->ap_fw ? REDUCED_TX_HEADROOM : 0;
60928c2ecf20Sopenharmony_ci
60938c2ecf20Sopenharmony_ci	hw->queues = MWL8K_TX_WMM_QUEUES;
60948c2ecf20Sopenharmony_ci
60958c2ecf20Sopenharmony_ci	/* Set rssi values to dBm */
60968c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, SIGNAL_DBM);
60978c2ecf20Sopenharmony_ci	ieee80211_hw_set(hw, HAS_RATE_CONTROL);
60988c2ecf20Sopenharmony_ci
60998c2ecf20Sopenharmony_ci	/*
61008c2ecf20Sopenharmony_ci	 * Ask mac80211 to not to trigger PS mode
61018c2ecf20Sopenharmony_ci	 * based on PM bit of incoming frames.
61028c2ecf20Sopenharmony_ci	 */
61038c2ecf20Sopenharmony_ci	if (priv->ap_fw)
61048c2ecf20Sopenharmony_ci		ieee80211_hw_set(hw, AP_LINK_PS);
61058c2ecf20Sopenharmony_ci
61068c2ecf20Sopenharmony_ci	hw->vif_data_size = sizeof(struct mwl8k_vif);
61078c2ecf20Sopenharmony_ci	hw->sta_data_size = sizeof(struct mwl8k_sta);
61088c2ecf20Sopenharmony_ci
61098c2ecf20Sopenharmony_ci	priv->macids_used = 0;
61108c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&priv->vif_list);
61118c2ecf20Sopenharmony_ci
61128c2ecf20Sopenharmony_ci	/* Set default radio state and preamble */
61138c2ecf20Sopenharmony_ci	priv->radio_on = false;
61148c2ecf20Sopenharmony_ci	priv->radio_short_preamble = false;
61158c2ecf20Sopenharmony_ci
61168c2ecf20Sopenharmony_ci	/* Finalize join worker */
61178c2ecf20Sopenharmony_ci	INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker);
61188c2ecf20Sopenharmony_ci	/* Handle watchdog ba events */
61198c2ecf20Sopenharmony_ci	INIT_WORK(&priv->watchdog_ba_handle, mwl8k_watchdog_ba_events);
61208c2ecf20Sopenharmony_ci	/* To reload the firmware if it crashes */
61218c2ecf20Sopenharmony_ci	INIT_WORK(&priv->fw_reload, mwl8k_hw_restart_work);
61228c2ecf20Sopenharmony_ci
61238c2ecf20Sopenharmony_ci	/* TX reclaim and RX tasklets.  */
61248c2ecf20Sopenharmony_ci	tasklet_setup(&priv->poll_tx_task, mwl8k_tx_poll);
61258c2ecf20Sopenharmony_ci	tasklet_disable(&priv->poll_tx_task);
61268c2ecf20Sopenharmony_ci	tasklet_setup(&priv->poll_rx_task, mwl8k_rx_poll);
61278c2ecf20Sopenharmony_ci	tasklet_disable(&priv->poll_rx_task);
61288c2ecf20Sopenharmony_ci
61298c2ecf20Sopenharmony_ci	/* Power management cookie */
61308c2ecf20Sopenharmony_ci	priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma);
61318c2ecf20Sopenharmony_ci	if (priv->cookie == NULL)
61328c2ecf20Sopenharmony_ci		return -ENOMEM;
61338c2ecf20Sopenharmony_ci
61348c2ecf20Sopenharmony_ci	mutex_init(&priv->fw_mutex);
61358c2ecf20Sopenharmony_ci	priv->fw_mutex_owner = NULL;
61368c2ecf20Sopenharmony_ci	priv->fw_mutex_depth = 0;
61378c2ecf20Sopenharmony_ci	priv->hostcmd_wait = NULL;
61388c2ecf20Sopenharmony_ci
61398c2ecf20Sopenharmony_ci	spin_lock_init(&priv->tx_lock);
61408c2ecf20Sopenharmony_ci
61418c2ecf20Sopenharmony_ci	spin_lock_init(&priv->stream_lock);
61428c2ecf20Sopenharmony_ci
61438c2ecf20Sopenharmony_ci	priv->tx_wait = NULL;
61448c2ecf20Sopenharmony_ci
61458c2ecf20Sopenharmony_ci	rc = mwl8k_probe_hw(hw);
61468c2ecf20Sopenharmony_ci	if (rc)
61478c2ecf20Sopenharmony_ci		goto err_free_cookie;
61488c2ecf20Sopenharmony_ci
61498c2ecf20Sopenharmony_ci	hw->wiphy->interface_modes = 0;
61508c2ecf20Sopenharmony_ci
61518c2ecf20Sopenharmony_ci	if (priv->ap_macids_supported || priv->device_info->fw_image_ap) {
61528c2ecf20Sopenharmony_ci		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP);
61538c2ecf20Sopenharmony_ci		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
61548c2ecf20Sopenharmony_ci		hw->wiphy->iface_combinations = &ap_if_comb;
61558c2ecf20Sopenharmony_ci		hw->wiphy->n_iface_combinations = 1;
61568c2ecf20Sopenharmony_ci	}
61578c2ecf20Sopenharmony_ci
61588c2ecf20Sopenharmony_ci	if (priv->sta_macids_supported || priv->device_info->fw_image_sta)
61598c2ecf20Sopenharmony_ci		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
61608c2ecf20Sopenharmony_ci
61618c2ecf20Sopenharmony_ci	wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
61628c2ecf20Sopenharmony_ci
61638c2ecf20Sopenharmony_ci	rc = ieee80211_register_hw(hw);
61648c2ecf20Sopenharmony_ci	if (rc) {
61658c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy, "Cannot register device\n");
61668c2ecf20Sopenharmony_ci		goto err_unprobe_hw;
61678c2ecf20Sopenharmony_ci	}
61688c2ecf20Sopenharmony_ci
61698c2ecf20Sopenharmony_ci	return 0;
61708c2ecf20Sopenharmony_ci
61718c2ecf20Sopenharmony_cierr_unprobe_hw:
61728c2ecf20Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
61738c2ecf20Sopenharmony_ci		mwl8k_txq_deinit(hw, i);
61748c2ecf20Sopenharmony_ci	mwl8k_rxq_deinit(hw, 0);
61758c2ecf20Sopenharmony_ci
61768c2ecf20Sopenharmony_cierr_free_cookie:
61778c2ecf20Sopenharmony_ci	if (priv->cookie != NULL)
61788c2ecf20Sopenharmony_ci		pci_free_consistent(priv->pdev, 4,
61798c2ecf20Sopenharmony_ci				priv->cookie, priv->cookie_dma);
61808c2ecf20Sopenharmony_ci
61818c2ecf20Sopenharmony_ci	return rc;
61828c2ecf20Sopenharmony_ci}
61838c2ecf20Sopenharmony_cistatic int mwl8k_probe(struct pci_dev *pdev,
61848c2ecf20Sopenharmony_ci				 const struct pci_device_id *id)
61858c2ecf20Sopenharmony_ci{
61868c2ecf20Sopenharmony_ci	static int printed_version;
61878c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw;
61888c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv;
61898c2ecf20Sopenharmony_ci	struct mwl8k_device_info *di;
61908c2ecf20Sopenharmony_ci	int rc;
61918c2ecf20Sopenharmony_ci
61928c2ecf20Sopenharmony_ci	if (!printed_version) {
61938c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s version %s\n", MWL8K_DESC, MWL8K_VERSION);
61948c2ecf20Sopenharmony_ci		printed_version = 1;
61958c2ecf20Sopenharmony_ci	}
61968c2ecf20Sopenharmony_ci
61978c2ecf20Sopenharmony_ci
61988c2ecf20Sopenharmony_ci	rc = pci_enable_device(pdev);
61998c2ecf20Sopenharmony_ci	if (rc) {
62008c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: Cannot enable new PCI device\n",
62018c2ecf20Sopenharmony_ci		       MWL8K_NAME);
62028c2ecf20Sopenharmony_ci		return rc;
62038c2ecf20Sopenharmony_ci	}
62048c2ecf20Sopenharmony_ci
62058c2ecf20Sopenharmony_ci	rc = pci_request_regions(pdev, MWL8K_NAME);
62068c2ecf20Sopenharmony_ci	if (rc) {
62078c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: Cannot obtain PCI resources\n",
62088c2ecf20Sopenharmony_ci		       MWL8K_NAME);
62098c2ecf20Sopenharmony_ci		goto err_disable_device;
62108c2ecf20Sopenharmony_ci	}
62118c2ecf20Sopenharmony_ci
62128c2ecf20Sopenharmony_ci	pci_set_master(pdev);
62138c2ecf20Sopenharmony_ci
62148c2ecf20Sopenharmony_ci
62158c2ecf20Sopenharmony_ci	hw = ieee80211_alloc_hw(sizeof(*priv), &mwl8k_ops);
62168c2ecf20Sopenharmony_ci	if (hw == NULL) {
62178c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: ieee80211 alloc failed\n", MWL8K_NAME);
62188c2ecf20Sopenharmony_ci		rc = -ENOMEM;
62198c2ecf20Sopenharmony_ci		goto err_free_reg;
62208c2ecf20Sopenharmony_ci	}
62218c2ecf20Sopenharmony_ci
62228c2ecf20Sopenharmony_ci	SET_IEEE80211_DEV(hw, &pdev->dev);
62238c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, hw);
62248c2ecf20Sopenharmony_ci
62258c2ecf20Sopenharmony_ci	priv = hw->priv;
62268c2ecf20Sopenharmony_ci	priv->hw = hw;
62278c2ecf20Sopenharmony_ci	priv->pdev = pdev;
62288c2ecf20Sopenharmony_ci	priv->device_info = &mwl8k_info_tbl[id->driver_data];
62298c2ecf20Sopenharmony_ci
62308c2ecf20Sopenharmony_ci	if (id->driver_data == MWL8764)
62318c2ecf20Sopenharmony_ci		priv->is_8764 = true;
62328c2ecf20Sopenharmony_ci
62338c2ecf20Sopenharmony_ci	priv->sram = pci_iomap(pdev, 0, 0x10000);
62348c2ecf20Sopenharmony_ci	if (priv->sram == NULL) {
62358c2ecf20Sopenharmony_ci		wiphy_err(hw->wiphy, "Cannot map device SRAM\n");
62368c2ecf20Sopenharmony_ci		rc = -EIO;
62378c2ecf20Sopenharmony_ci		goto err_iounmap;
62388c2ecf20Sopenharmony_ci	}
62398c2ecf20Sopenharmony_ci
62408c2ecf20Sopenharmony_ci	/*
62418c2ecf20Sopenharmony_ci	 * If BAR0 is a 32 bit BAR, the register BAR will be BAR1.
62428c2ecf20Sopenharmony_ci	 * If BAR0 is a 64 bit BAR, the register BAR will be BAR2.
62438c2ecf20Sopenharmony_ci	 */
62448c2ecf20Sopenharmony_ci	priv->regs = pci_iomap(pdev, 1, 0x10000);
62458c2ecf20Sopenharmony_ci	if (priv->regs == NULL) {
62468c2ecf20Sopenharmony_ci		priv->regs = pci_iomap(pdev, 2, 0x10000);
62478c2ecf20Sopenharmony_ci		if (priv->regs == NULL) {
62488c2ecf20Sopenharmony_ci			wiphy_err(hw->wiphy, "Cannot map device registers\n");
62498c2ecf20Sopenharmony_ci			rc = -EIO;
62508c2ecf20Sopenharmony_ci			goto err_iounmap;
62518c2ecf20Sopenharmony_ci		}
62528c2ecf20Sopenharmony_ci	}
62538c2ecf20Sopenharmony_ci
62548c2ecf20Sopenharmony_ci	/*
62558c2ecf20Sopenharmony_ci	 * Choose the initial fw image depending on user input.  If a second
62568c2ecf20Sopenharmony_ci	 * image is available, make it the alternative image that will be
62578c2ecf20Sopenharmony_ci	 * loaded if the first one fails.
62588c2ecf20Sopenharmony_ci	 */
62598c2ecf20Sopenharmony_ci	init_completion(&priv->firmware_loading_complete);
62608c2ecf20Sopenharmony_ci	di = priv->device_info;
62618c2ecf20Sopenharmony_ci	if (ap_mode_default && di->fw_image_ap) {
62628c2ecf20Sopenharmony_ci		priv->fw_pref = di->fw_image_ap;
62638c2ecf20Sopenharmony_ci		priv->fw_alt = di->fw_image_sta;
62648c2ecf20Sopenharmony_ci	} else if (!ap_mode_default && di->fw_image_sta) {
62658c2ecf20Sopenharmony_ci		priv->fw_pref = di->fw_image_sta;
62668c2ecf20Sopenharmony_ci		priv->fw_alt = di->fw_image_ap;
62678c2ecf20Sopenharmony_ci	} else if (ap_mode_default && !di->fw_image_ap && di->fw_image_sta) {
62688c2ecf20Sopenharmony_ci		printk(KERN_WARNING "AP fw is unavailable.  Using STA fw.");
62698c2ecf20Sopenharmony_ci		priv->fw_pref = di->fw_image_sta;
62708c2ecf20Sopenharmony_ci	} else if (!ap_mode_default && !di->fw_image_sta && di->fw_image_ap) {
62718c2ecf20Sopenharmony_ci		printk(KERN_WARNING "STA fw is unavailable.  Using AP fw.");
62728c2ecf20Sopenharmony_ci		priv->fw_pref = di->fw_image_ap;
62738c2ecf20Sopenharmony_ci	}
62748c2ecf20Sopenharmony_ci	rc = mwl8k_init_firmware(hw, priv->fw_pref, true);
62758c2ecf20Sopenharmony_ci	if (rc)
62768c2ecf20Sopenharmony_ci		goto err_stop_firmware;
62778c2ecf20Sopenharmony_ci
62788c2ecf20Sopenharmony_ci	priv->hw_restart_in_progress = false;
62798c2ecf20Sopenharmony_ci
62808c2ecf20Sopenharmony_ci	priv->running_bsses = 0;
62818c2ecf20Sopenharmony_ci
62828c2ecf20Sopenharmony_ci	return rc;
62838c2ecf20Sopenharmony_ci
62848c2ecf20Sopenharmony_cierr_stop_firmware:
62858c2ecf20Sopenharmony_ci	mwl8k_hw_reset(priv);
62868c2ecf20Sopenharmony_ci
62878c2ecf20Sopenharmony_cierr_iounmap:
62888c2ecf20Sopenharmony_ci	if (priv->regs != NULL)
62898c2ecf20Sopenharmony_ci		pci_iounmap(pdev, priv->regs);
62908c2ecf20Sopenharmony_ci
62918c2ecf20Sopenharmony_ci	if (priv->sram != NULL)
62928c2ecf20Sopenharmony_ci		pci_iounmap(pdev, priv->sram);
62938c2ecf20Sopenharmony_ci
62948c2ecf20Sopenharmony_ci	ieee80211_free_hw(hw);
62958c2ecf20Sopenharmony_ci
62968c2ecf20Sopenharmony_cierr_free_reg:
62978c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
62988c2ecf20Sopenharmony_ci
62998c2ecf20Sopenharmony_cierr_disable_device:
63008c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
63018c2ecf20Sopenharmony_ci
63028c2ecf20Sopenharmony_ci	return rc;
63038c2ecf20Sopenharmony_ci}
63048c2ecf20Sopenharmony_ci
63058c2ecf20Sopenharmony_cistatic void mwl8k_remove(struct pci_dev *pdev)
63068c2ecf20Sopenharmony_ci{
63078c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
63088c2ecf20Sopenharmony_ci	struct mwl8k_priv *priv;
63098c2ecf20Sopenharmony_ci	int i;
63108c2ecf20Sopenharmony_ci
63118c2ecf20Sopenharmony_ci	if (hw == NULL)
63128c2ecf20Sopenharmony_ci		return;
63138c2ecf20Sopenharmony_ci	priv = hw->priv;
63148c2ecf20Sopenharmony_ci
63158c2ecf20Sopenharmony_ci	wait_for_completion(&priv->firmware_loading_complete);
63168c2ecf20Sopenharmony_ci
63178c2ecf20Sopenharmony_ci	if (priv->fw_state == FW_STATE_ERROR) {
63188c2ecf20Sopenharmony_ci		mwl8k_hw_reset(priv);
63198c2ecf20Sopenharmony_ci		goto unmap;
63208c2ecf20Sopenharmony_ci	}
63218c2ecf20Sopenharmony_ci
63228c2ecf20Sopenharmony_ci	ieee80211_stop_queues(hw);
63238c2ecf20Sopenharmony_ci
63248c2ecf20Sopenharmony_ci	ieee80211_unregister_hw(hw);
63258c2ecf20Sopenharmony_ci
63268c2ecf20Sopenharmony_ci	/* Remove TX reclaim and RX tasklets.  */
63278c2ecf20Sopenharmony_ci	tasklet_kill(&priv->poll_tx_task);
63288c2ecf20Sopenharmony_ci	tasklet_kill(&priv->poll_rx_task);
63298c2ecf20Sopenharmony_ci
63308c2ecf20Sopenharmony_ci	/* Stop hardware */
63318c2ecf20Sopenharmony_ci	mwl8k_hw_reset(priv);
63328c2ecf20Sopenharmony_ci
63338c2ecf20Sopenharmony_ci	/* Return all skbs to mac80211 */
63348c2ecf20Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
63358c2ecf20Sopenharmony_ci		mwl8k_txq_reclaim(hw, i, INT_MAX, 1);
63368c2ecf20Sopenharmony_ci
63378c2ecf20Sopenharmony_ci	for (i = 0; i < mwl8k_tx_queues(priv); i++)
63388c2ecf20Sopenharmony_ci		mwl8k_txq_deinit(hw, i);
63398c2ecf20Sopenharmony_ci
63408c2ecf20Sopenharmony_ci	mwl8k_rxq_deinit(hw, 0);
63418c2ecf20Sopenharmony_ci
63428c2ecf20Sopenharmony_ci	pci_free_consistent(priv->pdev, 4, priv->cookie, priv->cookie_dma);
63438c2ecf20Sopenharmony_ci
63448c2ecf20Sopenharmony_ciunmap:
63458c2ecf20Sopenharmony_ci	pci_iounmap(pdev, priv->regs);
63468c2ecf20Sopenharmony_ci	pci_iounmap(pdev, priv->sram);
63478c2ecf20Sopenharmony_ci	ieee80211_free_hw(hw);
63488c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
63498c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
63508c2ecf20Sopenharmony_ci}
63518c2ecf20Sopenharmony_ci
63528c2ecf20Sopenharmony_cistatic struct pci_driver mwl8k_driver = {
63538c2ecf20Sopenharmony_ci	.name		= MWL8K_NAME,
63548c2ecf20Sopenharmony_ci	.id_table	= mwl8k_pci_id_table,
63558c2ecf20Sopenharmony_ci	.probe		= mwl8k_probe,
63568c2ecf20Sopenharmony_ci	.remove		= mwl8k_remove,
63578c2ecf20Sopenharmony_ci};
63588c2ecf20Sopenharmony_ci
63598c2ecf20Sopenharmony_cimodule_pci_driver(mwl8k_driver);
63608c2ecf20Sopenharmony_ci
63618c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(MWL8K_DESC);
63628c2ecf20Sopenharmony_ciMODULE_VERSION(MWL8K_VERSION);
63638c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com>");
63648c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
6365