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