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