18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. 48c2ecf20Sopenharmony_ci * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 88c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 98c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "wil6210.h" 128c2ecf20Sopenharmony_ci#include "txrx.h" 138c2ecf20Sopenharmony_ci#include "wmi.h" 148c2ecf20Sopenharmony_ci#include "trace.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* set the default max assoc sta to max supported by driver */ 178c2ecf20Sopenharmony_ciuint max_assoc_sta = WIL6210_MAX_CID; 188c2ecf20Sopenharmony_cimodule_param(max_assoc_sta, uint, 0444); 198c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_assoc_sta, " Max number of stations associated to the AP"); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ciint agg_wsize; /* = 0; */ 228c2ecf20Sopenharmony_cimodule_param(agg_wsize, int, 0644); 238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(agg_wsize, " Window size for Tx Block Ack after connect;" 248c2ecf20Sopenharmony_ci " 0 - use default; < 0 - don't auto-establish"); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciu8 led_id = WIL_LED_INVALID_ID; 278c2ecf20Sopenharmony_cimodule_param(led_id, byte, 0444); 288c2ecf20Sopenharmony_ciMODULE_PARM_DESC(led_id, 298c2ecf20Sopenharmony_ci " 60G device led enablement. Set the led ID (0-2) to enable"); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define WIL_WAIT_FOR_SUSPEND_RESUME_COMP 200 328c2ecf20Sopenharmony_ci#define WIL_WMI_PCP_STOP_TO_MS 5000 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/** 358c2ecf20Sopenharmony_ci * DOC: WMI event receiving - theory of operations 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * When firmware about to report WMI event, it fills memory area 388c2ecf20Sopenharmony_ci * in the mailbox and raises misc. IRQ. Thread interrupt handler invoked for 398c2ecf20Sopenharmony_ci * the misc IRQ, function @wmi_recv_cmd called by thread IRQ handler. 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * @wmi_recv_cmd reads event, allocates memory chunk and attaches it to the 428c2ecf20Sopenharmony_ci * event list @wil->pending_wmi_ev. Then, work queue @wil->wmi_wq wakes up 438c2ecf20Sopenharmony_ci * and handles events within the @wmi_event_worker. Every event get detached 448c2ecf20Sopenharmony_ci * from list, processed and deleted. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * Purpose for this mechanism is to release IRQ thread; otherwise, 478c2ecf20Sopenharmony_ci * if WMI event handling involves another WMI command flow, this 2-nd flow 488c2ecf20Sopenharmony_ci * won't be completed because of blocked IRQ thread. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/** 528c2ecf20Sopenharmony_ci * DOC: Addressing - theory of operations 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * There are several buses present on the WIL6210 card. 558c2ecf20Sopenharmony_ci * Same memory areas are visible at different address on 568c2ecf20Sopenharmony_ci * the different busses. There are 3 main bus masters: 578c2ecf20Sopenharmony_ci * - MAC CPU (ucode) 588c2ecf20Sopenharmony_ci * - User CPU (firmware) 598c2ecf20Sopenharmony_ci * - AHB (host) 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * On the PCI bus, there is one BAR (BAR0) of 2Mb size, exposing 628c2ecf20Sopenharmony_ci * AHB addresses starting from 0x880000 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * Internally, firmware uses addresses that allow faster access but 658c2ecf20Sopenharmony_ci * are invisible from the host. To read from these addresses, alternative 668c2ecf20Sopenharmony_ci * AHB address must be used. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* sparrow_fw_mapping provides memory remapping table for sparrow 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * array size should be in sync with the declaration in the wil6210.h 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * Sparrow memory mapping: 748c2ecf20Sopenharmony_ci * Linker address PCI/Host address 758c2ecf20Sopenharmony_ci * 0x880000 .. 0xa80000 2Mb BAR0 768c2ecf20Sopenharmony_ci * 0x800000 .. 0x808000 0x900000 .. 0x908000 32k DCCM 778c2ecf20Sopenharmony_ci * 0x840000 .. 0x860000 0x908000 .. 0x928000 128k PERIPH 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ciconst struct fw_map sparrow_fw_mapping[] = { 808c2ecf20Sopenharmony_ci /* FW code RAM 256k */ 818c2ecf20Sopenharmony_ci {0x000000, 0x040000, 0x8c0000, "fw_code", true, true}, 828c2ecf20Sopenharmony_ci /* FW data RAM 32k */ 838c2ecf20Sopenharmony_ci {0x800000, 0x808000, 0x900000, "fw_data", true, true}, 848c2ecf20Sopenharmony_ci /* periph data 128k */ 858c2ecf20Sopenharmony_ci {0x840000, 0x860000, 0x908000, "fw_peri", true, true}, 868c2ecf20Sopenharmony_ci /* various RGF 40k */ 878c2ecf20Sopenharmony_ci {0x880000, 0x88a000, 0x880000, "rgf", true, true}, 888c2ecf20Sopenharmony_ci /* AGC table 4k */ 898c2ecf20Sopenharmony_ci {0x88a000, 0x88b000, 0x88a000, "AGC_tbl", true, true}, 908c2ecf20Sopenharmony_ci /* Pcie_ext_rgf 4k */ 918c2ecf20Sopenharmony_ci {0x88b000, 0x88c000, 0x88b000, "rgf_ext", true, true}, 928c2ecf20Sopenharmony_ci /* mac_ext_rgf 512b */ 938c2ecf20Sopenharmony_ci {0x88c000, 0x88c200, 0x88c000, "mac_rgf_ext", true, true}, 948c2ecf20Sopenharmony_ci /* upper area 548k */ 958c2ecf20Sopenharmony_ci {0x8c0000, 0x949000, 0x8c0000, "upper", true, true}, 968c2ecf20Sopenharmony_ci /* UCODE areas - accessible by debugfs blobs but not by 978c2ecf20Sopenharmony_ci * wmi_addr_remap. UCODE areas MUST be added AFTER FW areas! 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ci /* ucode code RAM 128k */ 1008c2ecf20Sopenharmony_ci {0x000000, 0x020000, 0x920000, "uc_code", false, false}, 1018c2ecf20Sopenharmony_ci /* ucode data RAM 16k */ 1028c2ecf20Sopenharmony_ci {0x800000, 0x804000, 0x940000, "uc_data", false, false}, 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* sparrow_d0_mac_rgf_ext - mac_rgf_ext section for Sparrow D0 1068c2ecf20Sopenharmony_ci * it is a bit larger to support extra features 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_ciconst struct fw_map sparrow_d0_mac_rgf_ext = { 1098c2ecf20Sopenharmony_ci 0x88c000, 0x88c500, 0x88c000, "mac_rgf_ext", true, true 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* talyn_fw_mapping provides memory remapping table for Talyn 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * array size should be in sync with the declaration in the wil6210.h 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * Talyn memory mapping: 1178c2ecf20Sopenharmony_ci * Linker address PCI/Host address 1188c2ecf20Sopenharmony_ci * 0x880000 .. 0xc80000 4Mb BAR0 1198c2ecf20Sopenharmony_ci * 0x800000 .. 0x820000 0xa00000 .. 0xa20000 128k DCCM 1208c2ecf20Sopenharmony_ci * 0x840000 .. 0x858000 0xa20000 .. 0xa38000 96k PERIPH 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ciconst struct fw_map talyn_fw_mapping[] = { 1238c2ecf20Sopenharmony_ci /* FW code RAM 1M */ 1248c2ecf20Sopenharmony_ci {0x000000, 0x100000, 0x900000, "fw_code", true, true}, 1258c2ecf20Sopenharmony_ci /* FW data RAM 128k */ 1268c2ecf20Sopenharmony_ci {0x800000, 0x820000, 0xa00000, "fw_data", true, true}, 1278c2ecf20Sopenharmony_ci /* periph. data RAM 96k */ 1288c2ecf20Sopenharmony_ci {0x840000, 0x858000, 0xa20000, "fw_peri", true, true}, 1298c2ecf20Sopenharmony_ci /* various RGF 40k */ 1308c2ecf20Sopenharmony_ci {0x880000, 0x88a000, 0x880000, "rgf", true, true}, 1318c2ecf20Sopenharmony_ci /* AGC table 4k */ 1328c2ecf20Sopenharmony_ci {0x88a000, 0x88b000, 0x88a000, "AGC_tbl", true, true}, 1338c2ecf20Sopenharmony_ci /* Pcie_ext_rgf 4k */ 1348c2ecf20Sopenharmony_ci {0x88b000, 0x88c000, 0x88b000, "rgf_ext", true, true}, 1358c2ecf20Sopenharmony_ci /* mac_ext_rgf 1344b */ 1368c2ecf20Sopenharmony_ci {0x88c000, 0x88c540, 0x88c000, "mac_rgf_ext", true, true}, 1378c2ecf20Sopenharmony_ci /* ext USER RGF 4k */ 1388c2ecf20Sopenharmony_ci {0x88d000, 0x88e000, 0x88d000, "ext_user_rgf", true, true}, 1398c2ecf20Sopenharmony_ci /* OTP 4k */ 1408c2ecf20Sopenharmony_ci {0x8a0000, 0x8a1000, 0x8a0000, "otp", true, false}, 1418c2ecf20Sopenharmony_ci /* DMA EXT RGF 64k */ 1428c2ecf20Sopenharmony_ci {0x8b0000, 0x8c0000, 0x8b0000, "dma_ext_rgf", true, true}, 1438c2ecf20Sopenharmony_ci /* upper area 1536k */ 1448c2ecf20Sopenharmony_ci {0x900000, 0xa80000, 0x900000, "upper", true, true}, 1458c2ecf20Sopenharmony_ci /* UCODE areas - accessible by debugfs blobs but not by 1468c2ecf20Sopenharmony_ci * wmi_addr_remap. UCODE areas MUST be added AFTER FW areas! 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci /* ucode code RAM 256k */ 1498c2ecf20Sopenharmony_ci {0x000000, 0x040000, 0xa38000, "uc_code", false, false}, 1508c2ecf20Sopenharmony_ci /* ucode data RAM 32k */ 1518c2ecf20Sopenharmony_ci {0x800000, 0x808000, 0xa78000, "uc_data", false, false}, 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* talyn_mb_fw_mapping provides memory remapping table for Talyn-MB 1558c2ecf20Sopenharmony_ci * 1568c2ecf20Sopenharmony_ci * array size should be in sync with the declaration in the wil6210.h 1578c2ecf20Sopenharmony_ci * 1588c2ecf20Sopenharmony_ci * Talyn MB memory mapping: 1598c2ecf20Sopenharmony_ci * Linker address PCI/Host address 1608c2ecf20Sopenharmony_ci * 0x880000 .. 0xc80000 4Mb BAR0 1618c2ecf20Sopenharmony_ci * 0x800000 .. 0x820000 0xa00000 .. 0xa20000 128k DCCM 1628c2ecf20Sopenharmony_ci * 0x840000 .. 0x858000 0xa20000 .. 0xa38000 96k PERIPH 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ciconst struct fw_map talyn_mb_fw_mapping[] = { 1658c2ecf20Sopenharmony_ci /* FW code RAM 768k */ 1668c2ecf20Sopenharmony_ci {0x000000, 0x0c0000, 0x900000, "fw_code", true, true}, 1678c2ecf20Sopenharmony_ci /* FW data RAM 128k */ 1688c2ecf20Sopenharmony_ci {0x800000, 0x820000, 0xa00000, "fw_data", true, true}, 1698c2ecf20Sopenharmony_ci /* periph. data RAM 96k */ 1708c2ecf20Sopenharmony_ci {0x840000, 0x858000, 0xa20000, "fw_peri", true, true}, 1718c2ecf20Sopenharmony_ci /* various RGF 40k */ 1728c2ecf20Sopenharmony_ci {0x880000, 0x88a000, 0x880000, "rgf", true, true}, 1738c2ecf20Sopenharmony_ci /* AGC table 4k */ 1748c2ecf20Sopenharmony_ci {0x88a000, 0x88b000, 0x88a000, "AGC_tbl", true, true}, 1758c2ecf20Sopenharmony_ci /* Pcie_ext_rgf 4k */ 1768c2ecf20Sopenharmony_ci {0x88b000, 0x88c000, 0x88b000, "rgf_ext", true, true}, 1778c2ecf20Sopenharmony_ci /* mac_ext_rgf 2256b */ 1788c2ecf20Sopenharmony_ci {0x88c000, 0x88c8d0, 0x88c000, "mac_rgf_ext", true, true}, 1798c2ecf20Sopenharmony_ci /* ext USER RGF 4k */ 1808c2ecf20Sopenharmony_ci {0x88d000, 0x88e000, 0x88d000, "ext_user_rgf", true, true}, 1818c2ecf20Sopenharmony_ci /* SEC PKA 16k */ 1828c2ecf20Sopenharmony_ci {0x890000, 0x894000, 0x890000, "sec_pka", true, true}, 1838c2ecf20Sopenharmony_ci /* SEC KDF RGF 3096b */ 1848c2ecf20Sopenharmony_ci {0x898000, 0x898c18, 0x898000, "sec_kdf_rgf", true, true}, 1858c2ecf20Sopenharmony_ci /* SEC MAIN 2124b */ 1868c2ecf20Sopenharmony_ci {0x89a000, 0x89a84c, 0x89a000, "sec_main", true, true}, 1878c2ecf20Sopenharmony_ci /* OTP 4k */ 1888c2ecf20Sopenharmony_ci {0x8a0000, 0x8a1000, 0x8a0000, "otp", true, false}, 1898c2ecf20Sopenharmony_ci /* DMA EXT RGF 64k */ 1908c2ecf20Sopenharmony_ci {0x8b0000, 0x8c0000, 0x8b0000, "dma_ext_rgf", true, true}, 1918c2ecf20Sopenharmony_ci /* DUM USER RGF 528b */ 1928c2ecf20Sopenharmony_ci {0x8c0000, 0x8c0210, 0x8c0000, "dum_user_rgf", true, true}, 1938c2ecf20Sopenharmony_ci /* DMA OFU 296b */ 1948c2ecf20Sopenharmony_ci {0x8c2000, 0x8c2128, 0x8c2000, "dma_ofu", true, true}, 1958c2ecf20Sopenharmony_ci /* ucode debug 256b */ 1968c2ecf20Sopenharmony_ci {0x8c3000, 0x8c3100, 0x8c3000, "ucode_debug", true, true}, 1978c2ecf20Sopenharmony_ci /* upper area 1536k */ 1988c2ecf20Sopenharmony_ci {0x900000, 0xa80000, 0x900000, "upper", true, true}, 1998c2ecf20Sopenharmony_ci /* UCODE areas - accessible by debugfs blobs but not by 2008c2ecf20Sopenharmony_ci * wmi_addr_remap. UCODE areas MUST be added AFTER FW areas! 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci /* ucode code RAM 256k */ 2038c2ecf20Sopenharmony_ci {0x000000, 0x040000, 0xa38000, "uc_code", false, false}, 2048c2ecf20Sopenharmony_ci /* ucode data RAM 32k */ 2058c2ecf20Sopenharmony_ci {0x800000, 0x808000, 0xa78000, "uc_data", false, false}, 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistruct fw_map fw_mapping[MAX_FW_MAPPING_TABLE_SIZE]; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistruct blink_on_off_time led_blink_time[] = { 2118c2ecf20Sopenharmony_ci {WIL_LED_BLINK_ON_SLOW_MS, WIL_LED_BLINK_OFF_SLOW_MS}, 2128c2ecf20Sopenharmony_ci {WIL_LED_BLINK_ON_MED_MS, WIL_LED_BLINK_OFF_MED_MS}, 2138c2ecf20Sopenharmony_ci {WIL_LED_BLINK_ON_FAST_MS, WIL_LED_BLINK_OFF_FAST_MS}, 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistruct auth_no_hdr { 2178c2ecf20Sopenharmony_ci __le16 auth_alg; 2188c2ecf20Sopenharmony_ci __le16 auth_transaction; 2198c2ecf20Sopenharmony_ci __le16 status_code; 2208c2ecf20Sopenharmony_ci /* possibly followed by Challenge text */ 2218c2ecf20Sopenharmony_ci u8 variable[]; 2228c2ecf20Sopenharmony_ci} __packed; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ciu8 led_polarity = LED_POLARITY_LOW_ACTIVE; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/** 2278c2ecf20Sopenharmony_ci * return AHB address for given firmware internal (linker) address 2288c2ecf20Sopenharmony_ci * @x: internal address 2298c2ecf20Sopenharmony_ci * If address have no valid AHB mapping, return 0 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_cistatic u32 wmi_addr_remap(u32 x) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci uint i; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { 2368c2ecf20Sopenharmony_ci if (fw_mapping[i].fw && 2378c2ecf20Sopenharmony_ci ((x >= fw_mapping[i].from) && (x < fw_mapping[i].to))) 2388c2ecf20Sopenharmony_ci return x + fw_mapping[i].host - fw_mapping[i].from; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/** 2458c2ecf20Sopenharmony_ci * find fw_mapping entry by section name 2468c2ecf20Sopenharmony_ci * @section: section name 2478c2ecf20Sopenharmony_ci * 2488c2ecf20Sopenharmony_ci * Return pointer to section or NULL if not found 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_cistruct fw_map *wil_find_fw_mapping(const char *section) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci int i; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) 2558c2ecf20Sopenharmony_ci if (fw_mapping[i].name && 2568c2ecf20Sopenharmony_ci !strcmp(section, fw_mapping[i].name)) 2578c2ecf20Sopenharmony_ci return &fw_mapping[i]; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return NULL; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/** 2638c2ecf20Sopenharmony_ci * Check address validity for WMI buffer; remap if needed 2648c2ecf20Sopenharmony_ci * @wil: driver data 2658c2ecf20Sopenharmony_ci * @ptr: internal (linker) fw/ucode address 2668c2ecf20Sopenharmony_ci * @size: if non zero, validate the block does not 2678c2ecf20Sopenharmony_ci * exceed the device memory (bar) 2688c2ecf20Sopenharmony_ci * 2698c2ecf20Sopenharmony_ci * Valid buffer should be DWORD aligned 2708c2ecf20Sopenharmony_ci * 2718c2ecf20Sopenharmony_ci * return address for accessing buffer from the host; 2728c2ecf20Sopenharmony_ci * if buffer is not valid, return NULL. 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_civoid __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr_, u32 size) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci u32 off; 2778c2ecf20Sopenharmony_ci u32 ptr = le32_to_cpu(ptr_); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (ptr % 4) 2808c2ecf20Sopenharmony_ci return NULL; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci ptr = wmi_addr_remap(ptr); 2838c2ecf20Sopenharmony_ci if (ptr < WIL6210_FW_HOST_OFF) 2848c2ecf20Sopenharmony_ci return NULL; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci off = HOSTADDR(ptr); 2878c2ecf20Sopenharmony_ci if (off > wil->bar_size - 4) 2888c2ecf20Sopenharmony_ci return NULL; 2898c2ecf20Sopenharmony_ci if (size && ((off + size > wil->bar_size) || (off + size < off))) 2908c2ecf20Sopenharmony_ci return NULL; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return wil->csr + off; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_civoid __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci return wmi_buffer_block(wil, ptr_, 0); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/* Check address validity */ 3018c2ecf20Sopenharmony_civoid __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci u32 off; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (ptr % 4) 3068c2ecf20Sopenharmony_ci return NULL; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (ptr < WIL6210_FW_HOST_OFF) 3098c2ecf20Sopenharmony_ci return NULL; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci off = HOSTADDR(ptr); 3128c2ecf20Sopenharmony_ci if (off > wil->bar_size - 4) 3138c2ecf20Sopenharmony_ci return NULL; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return wil->csr + off; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ciint wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, 3198c2ecf20Sopenharmony_ci struct wil6210_mbox_hdr *hdr) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci void __iomem *src = wmi_buffer(wil, ptr); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (!src) 3248c2ecf20Sopenharmony_ci return -EINVAL; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci wil_memcpy_fromio_32(hdr, src, sizeof(*hdr)); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic const char *cmdid2name(u16 cmdid) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci switch (cmdid) { 3348c2ecf20Sopenharmony_ci case WMI_NOTIFY_REQ_CMDID: 3358c2ecf20Sopenharmony_ci return "WMI_NOTIFY_REQ_CMD"; 3368c2ecf20Sopenharmony_ci case WMI_START_SCAN_CMDID: 3378c2ecf20Sopenharmony_ci return "WMI_START_SCAN_CMD"; 3388c2ecf20Sopenharmony_ci case WMI_CONNECT_CMDID: 3398c2ecf20Sopenharmony_ci return "WMI_CONNECT_CMD"; 3408c2ecf20Sopenharmony_ci case WMI_DISCONNECT_CMDID: 3418c2ecf20Sopenharmony_ci return "WMI_DISCONNECT_CMD"; 3428c2ecf20Sopenharmony_ci case WMI_SW_TX_REQ_CMDID: 3438c2ecf20Sopenharmony_ci return "WMI_SW_TX_REQ_CMD"; 3448c2ecf20Sopenharmony_ci case WMI_GET_RF_SECTOR_PARAMS_CMDID: 3458c2ecf20Sopenharmony_ci return "WMI_GET_RF_SECTOR_PARAMS_CMD"; 3468c2ecf20Sopenharmony_ci case WMI_SET_RF_SECTOR_PARAMS_CMDID: 3478c2ecf20Sopenharmony_ci return "WMI_SET_RF_SECTOR_PARAMS_CMD"; 3488c2ecf20Sopenharmony_ci case WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID: 3498c2ecf20Sopenharmony_ci return "WMI_GET_SELECTED_RF_SECTOR_INDEX_CMD"; 3508c2ecf20Sopenharmony_ci case WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID: 3518c2ecf20Sopenharmony_ci return "WMI_SET_SELECTED_RF_SECTOR_INDEX_CMD"; 3528c2ecf20Sopenharmony_ci case WMI_BRP_SET_ANT_LIMIT_CMDID: 3538c2ecf20Sopenharmony_ci return "WMI_BRP_SET_ANT_LIMIT_CMD"; 3548c2ecf20Sopenharmony_ci case WMI_TOF_SESSION_START_CMDID: 3558c2ecf20Sopenharmony_ci return "WMI_TOF_SESSION_START_CMD"; 3568c2ecf20Sopenharmony_ci case WMI_AOA_MEAS_CMDID: 3578c2ecf20Sopenharmony_ci return "WMI_AOA_MEAS_CMD"; 3588c2ecf20Sopenharmony_ci case WMI_PMC_CMDID: 3598c2ecf20Sopenharmony_ci return "WMI_PMC_CMD"; 3608c2ecf20Sopenharmony_ci case WMI_TOF_GET_TX_RX_OFFSET_CMDID: 3618c2ecf20Sopenharmony_ci return "WMI_TOF_GET_TX_RX_OFFSET_CMD"; 3628c2ecf20Sopenharmony_ci case WMI_TOF_SET_TX_RX_OFFSET_CMDID: 3638c2ecf20Sopenharmony_ci return "WMI_TOF_SET_TX_RX_OFFSET_CMD"; 3648c2ecf20Sopenharmony_ci case WMI_VRING_CFG_CMDID: 3658c2ecf20Sopenharmony_ci return "WMI_VRING_CFG_CMD"; 3668c2ecf20Sopenharmony_ci case WMI_BCAST_VRING_CFG_CMDID: 3678c2ecf20Sopenharmony_ci return "WMI_BCAST_VRING_CFG_CMD"; 3688c2ecf20Sopenharmony_ci case WMI_TRAFFIC_SUSPEND_CMDID: 3698c2ecf20Sopenharmony_ci return "WMI_TRAFFIC_SUSPEND_CMD"; 3708c2ecf20Sopenharmony_ci case WMI_TRAFFIC_RESUME_CMDID: 3718c2ecf20Sopenharmony_ci return "WMI_TRAFFIC_RESUME_CMD"; 3728c2ecf20Sopenharmony_ci case WMI_ECHO_CMDID: 3738c2ecf20Sopenharmony_ci return "WMI_ECHO_CMD"; 3748c2ecf20Sopenharmony_ci case WMI_SET_MAC_ADDRESS_CMDID: 3758c2ecf20Sopenharmony_ci return "WMI_SET_MAC_ADDRESS_CMD"; 3768c2ecf20Sopenharmony_ci case WMI_LED_CFG_CMDID: 3778c2ecf20Sopenharmony_ci return "WMI_LED_CFG_CMD"; 3788c2ecf20Sopenharmony_ci case WMI_PCP_START_CMDID: 3798c2ecf20Sopenharmony_ci return "WMI_PCP_START_CMD"; 3808c2ecf20Sopenharmony_ci case WMI_PCP_STOP_CMDID: 3818c2ecf20Sopenharmony_ci return "WMI_PCP_STOP_CMD"; 3828c2ecf20Sopenharmony_ci case WMI_SET_SSID_CMDID: 3838c2ecf20Sopenharmony_ci return "WMI_SET_SSID_CMD"; 3848c2ecf20Sopenharmony_ci case WMI_GET_SSID_CMDID: 3858c2ecf20Sopenharmony_ci return "WMI_GET_SSID_CMD"; 3868c2ecf20Sopenharmony_ci case WMI_SET_PCP_CHANNEL_CMDID: 3878c2ecf20Sopenharmony_ci return "WMI_SET_PCP_CHANNEL_CMD"; 3888c2ecf20Sopenharmony_ci case WMI_GET_PCP_CHANNEL_CMDID: 3898c2ecf20Sopenharmony_ci return "WMI_GET_PCP_CHANNEL_CMD"; 3908c2ecf20Sopenharmony_ci case WMI_P2P_CFG_CMDID: 3918c2ecf20Sopenharmony_ci return "WMI_P2P_CFG_CMD"; 3928c2ecf20Sopenharmony_ci case WMI_PORT_ALLOCATE_CMDID: 3938c2ecf20Sopenharmony_ci return "WMI_PORT_ALLOCATE_CMD"; 3948c2ecf20Sopenharmony_ci case WMI_PORT_DELETE_CMDID: 3958c2ecf20Sopenharmony_ci return "WMI_PORT_DELETE_CMD"; 3968c2ecf20Sopenharmony_ci case WMI_START_LISTEN_CMDID: 3978c2ecf20Sopenharmony_ci return "WMI_START_LISTEN_CMD"; 3988c2ecf20Sopenharmony_ci case WMI_START_SEARCH_CMDID: 3998c2ecf20Sopenharmony_ci return "WMI_START_SEARCH_CMD"; 4008c2ecf20Sopenharmony_ci case WMI_DISCOVERY_STOP_CMDID: 4018c2ecf20Sopenharmony_ci return "WMI_DISCOVERY_STOP_CMD"; 4028c2ecf20Sopenharmony_ci case WMI_DELETE_CIPHER_KEY_CMDID: 4038c2ecf20Sopenharmony_ci return "WMI_DELETE_CIPHER_KEY_CMD"; 4048c2ecf20Sopenharmony_ci case WMI_ADD_CIPHER_KEY_CMDID: 4058c2ecf20Sopenharmony_ci return "WMI_ADD_CIPHER_KEY_CMD"; 4068c2ecf20Sopenharmony_ci case WMI_SET_APPIE_CMDID: 4078c2ecf20Sopenharmony_ci return "WMI_SET_APPIE_CMD"; 4088c2ecf20Sopenharmony_ci case WMI_CFG_RX_CHAIN_CMDID: 4098c2ecf20Sopenharmony_ci return "WMI_CFG_RX_CHAIN_CMD"; 4108c2ecf20Sopenharmony_ci case WMI_TEMP_SENSE_CMDID: 4118c2ecf20Sopenharmony_ci return "WMI_TEMP_SENSE_CMD"; 4128c2ecf20Sopenharmony_ci case WMI_DEL_STA_CMDID: 4138c2ecf20Sopenharmony_ci return "WMI_DEL_STA_CMD"; 4148c2ecf20Sopenharmony_ci case WMI_DISCONNECT_STA_CMDID: 4158c2ecf20Sopenharmony_ci return "WMI_DISCONNECT_STA_CMD"; 4168c2ecf20Sopenharmony_ci case WMI_RING_BA_EN_CMDID: 4178c2ecf20Sopenharmony_ci return "WMI_RING_BA_EN_CMD"; 4188c2ecf20Sopenharmony_ci case WMI_RING_BA_DIS_CMDID: 4198c2ecf20Sopenharmony_ci return "WMI_RING_BA_DIS_CMD"; 4208c2ecf20Sopenharmony_ci case WMI_RCP_DELBA_CMDID: 4218c2ecf20Sopenharmony_ci return "WMI_RCP_DELBA_CMD"; 4228c2ecf20Sopenharmony_ci case WMI_RCP_ADDBA_RESP_CMDID: 4238c2ecf20Sopenharmony_ci return "WMI_RCP_ADDBA_RESP_CMD"; 4248c2ecf20Sopenharmony_ci case WMI_RCP_ADDBA_RESP_EDMA_CMDID: 4258c2ecf20Sopenharmony_ci return "WMI_RCP_ADDBA_RESP_EDMA_CMD"; 4268c2ecf20Sopenharmony_ci case WMI_PS_DEV_PROFILE_CFG_CMDID: 4278c2ecf20Sopenharmony_ci return "WMI_PS_DEV_PROFILE_CFG_CMD"; 4288c2ecf20Sopenharmony_ci case WMI_SET_MGMT_RETRY_LIMIT_CMDID: 4298c2ecf20Sopenharmony_ci return "WMI_SET_MGMT_RETRY_LIMIT_CMD"; 4308c2ecf20Sopenharmony_ci case WMI_GET_MGMT_RETRY_LIMIT_CMDID: 4318c2ecf20Sopenharmony_ci return "WMI_GET_MGMT_RETRY_LIMIT_CMD"; 4328c2ecf20Sopenharmony_ci case WMI_ABORT_SCAN_CMDID: 4338c2ecf20Sopenharmony_ci return "WMI_ABORT_SCAN_CMD"; 4348c2ecf20Sopenharmony_ci case WMI_NEW_STA_CMDID: 4358c2ecf20Sopenharmony_ci return "WMI_NEW_STA_CMD"; 4368c2ecf20Sopenharmony_ci case WMI_SET_THERMAL_THROTTLING_CFG_CMDID: 4378c2ecf20Sopenharmony_ci return "WMI_SET_THERMAL_THROTTLING_CFG_CMD"; 4388c2ecf20Sopenharmony_ci case WMI_GET_THERMAL_THROTTLING_CFG_CMDID: 4398c2ecf20Sopenharmony_ci return "WMI_GET_THERMAL_THROTTLING_CFG_CMD"; 4408c2ecf20Sopenharmony_ci case WMI_LINK_MAINTAIN_CFG_WRITE_CMDID: 4418c2ecf20Sopenharmony_ci return "WMI_LINK_MAINTAIN_CFG_WRITE_CMD"; 4428c2ecf20Sopenharmony_ci case WMI_LO_POWER_CALIB_FROM_OTP_CMDID: 4438c2ecf20Sopenharmony_ci return "WMI_LO_POWER_CALIB_FROM_OTP_CMD"; 4448c2ecf20Sopenharmony_ci case WMI_START_SCHED_SCAN_CMDID: 4458c2ecf20Sopenharmony_ci return "WMI_START_SCHED_SCAN_CMD"; 4468c2ecf20Sopenharmony_ci case WMI_STOP_SCHED_SCAN_CMDID: 4478c2ecf20Sopenharmony_ci return "WMI_STOP_SCHED_SCAN_CMD"; 4488c2ecf20Sopenharmony_ci case WMI_TX_STATUS_RING_ADD_CMDID: 4498c2ecf20Sopenharmony_ci return "WMI_TX_STATUS_RING_ADD_CMD"; 4508c2ecf20Sopenharmony_ci case WMI_RX_STATUS_RING_ADD_CMDID: 4518c2ecf20Sopenharmony_ci return "WMI_RX_STATUS_RING_ADD_CMD"; 4528c2ecf20Sopenharmony_ci case WMI_TX_DESC_RING_ADD_CMDID: 4538c2ecf20Sopenharmony_ci return "WMI_TX_DESC_RING_ADD_CMD"; 4548c2ecf20Sopenharmony_ci case WMI_RX_DESC_RING_ADD_CMDID: 4558c2ecf20Sopenharmony_ci return "WMI_RX_DESC_RING_ADD_CMD"; 4568c2ecf20Sopenharmony_ci case WMI_BCAST_DESC_RING_ADD_CMDID: 4578c2ecf20Sopenharmony_ci return "WMI_BCAST_DESC_RING_ADD_CMD"; 4588c2ecf20Sopenharmony_ci case WMI_CFG_DEF_RX_OFFLOAD_CMDID: 4598c2ecf20Sopenharmony_ci return "WMI_CFG_DEF_RX_OFFLOAD_CMD"; 4608c2ecf20Sopenharmony_ci case WMI_LINK_STATS_CMDID: 4618c2ecf20Sopenharmony_ci return "WMI_LINK_STATS_CMD"; 4628c2ecf20Sopenharmony_ci case WMI_SW_TX_REQ_EXT_CMDID: 4638c2ecf20Sopenharmony_ci return "WMI_SW_TX_REQ_EXT_CMDID"; 4648c2ecf20Sopenharmony_ci case WMI_FT_AUTH_CMDID: 4658c2ecf20Sopenharmony_ci return "WMI_FT_AUTH_CMD"; 4668c2ecf20Sopenharmony_ci case WMI_FT_REASSOC_CMDID: 4678c2ecf20Sopenharmony_ci return "WMI_FT_REASSOC_CMD"; 4688c2ecf20Sopenharmony_ci case WMI_UPDATE_FT_IES_CMDID: 4698c2ecf20Sopenharmony_ci return "WMI_UPDATE_FT_IES_CMD"; 4708c2ecf20Sopenharmony_ci case WMI_RBUFCAP_CFG_CMDID: 4718c2ecf20Sopenharmony_ci return "WMI_RBUFCAP_CFG_CMD"; 4728c2ecf20Sopenharmony_ci case WMI_TEMP_SENSE_ALL_CMDID: 4738c2ecf20Sopenharmony_ci return "WMI_TEMP_SENSE_ALL_CMDID"; 4748c2ecf20Sopenharmony_ci case WMI_SET_LINK_MONITOR_CMDID: 4758c2ecf20Sopenharmony_ci return "WMI_SET_LINK_MONITOR_CMD"; 4768c2ecf20Sopenharmony_ci default: 4778c2ecf20Sopenharmony_ci return "Untracked CMD"; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic const char *eventid2name(u16 eventid) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci switch (eventid) { 4848c2ecf20Sopenharmony_ci case WMI_NOTIFY_REQ_DONE_EVENTID: 4858c2ecf20Sopenharmony_ci return "WMI_NOTIFY_REQ_DONE_EVENT"; 4868c2ecf20Sopenharmony_ci case WMI_DISCONNECT_EVENTID: 4878c2ecf20Sopenharmony_ci return "WMI_DISCONNECT_EVENT"; 4888c2ecf20Sopenharmony_ci case WMI_SW_TX_COMPLETE_EVENTID: 4898c2ecf20Sopenharmony_ci return "WMI_SW_TX_COMPLETE_EVENT"; 4908c2ecf20Sopenharmony_ci case WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID: 4918c2ecf20Sopenharmony_ci return "WMI_GET_RF_SECTOR_PARAMS_DONE_EVENT"; 4928c2ecf20Sopenharmony_ci case WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID: 4938c2ecf20Sopenharmony_ci return "WMI_SET_RF_SECTOR_PARAMS_DONE_EVENT"; 4948c2ecf20Sopenharmony_ci case WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID: 4958c2ecf20Sopenharmony_ci return "WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENT"; 4968c2ecf20Sopenharmony_ci case WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID: 4978c2ecf20Sopenharmony_ci return "WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENT"; 4988c2ecf20Sopenharmony_ci case WMI_BRP_SET_ANT_LIMIT_EVENTID: 4998c2ecf20Sopenharmony_ci return "WMI_BRP_SET_ANT_LIMIT_EVENT"; 5008c2ecf20Sopenharmony_ci case WMI_FW_READY_EVENTID: 5018c2ecf20Sopenharmony_ci return "WMI_FW_READY_EVENT"; 5028c2ecf20Sopenharmony_ci case WMI_TRAFFIC_RESUME_EVENTID: 5038c2ecf20Sopenharmony_ci return "WMI_TRAFFIC_RESUME_EVENT"; 5048c2ecf20Sopenharmony_ci case WMI_TOF_GET_TX_RX_OFFSET_EVENTID: 5058c2ecf20Sopenharmony_ci return "WMI_TOF_GET_TX_RX_OFFSET_EVENT"; 5068c2ecf20Sopenharmony_ci case WMI_TOF_SET_TX_RX_OFFSET_EVENTID: 5078c2ecf20Sopenharmony_ci return "WMI_TOF_SET_TX_RX_OFFSET_EVENT"; 5088c2ecf20Sopenharmony_ci case WMI_VRING_CFG_DONE_EVENTID: 5098c2ecf20Sopenharmony_ci return "WMI_VRING_CFG_DONE_EVENT"; 5108c2ecf20Sopenharmony_ci case WMI_READY_EVENTID: 5118c2ecf20Sopenharmony_ci return "WMI_READY_EVENT"; 5128c2ecf20Sopenharmony_ci case WMI_RX_MGMT_PACKET_EVENTID: 5138c2ecf20Sopenharmony_ci return "WMI_RX_MGMT_PACKET_EVENT"; 5148c2ecf20Sopenharmony_ci case WMI_TX_MGMT_PACKET_EVENTID: 5158c2ecf20Sopenharmony_ci return "WMI_TX_MGMT_PACKET_EVENT"; 5168c2ecf20Sopenharmony_ci case WMI_SCAN_COMPLETE_EVENTID: 5178c2ecf20Sopenharmony_ci return "WMI_SCAN_COMPLETE_EVENT"; 5188c2ecf20Sopenharmony_ci case WMI_ACS_PASSIVE_SCAN_COMPLETE_EVENTID: 5198c2ecf20Sopenharmony_ci return "WMI_ACS_PASSIVE_SCAN_COMPLETE_EVENT"; 5208c2ecf20Sopenharmony_ci case WMI_CONNECT_EVENTID: 5218c2ecf20Sopenharmony_ci return "WMI_CONNECT_EVENT"; 5228c2ecf20Sopenharmony_ci case WMI_EAPOL_RX_EVENTID: 5238c2ecf20Sopenharmony_ci return "WMI_EAPOL_RX_EVENT"; 5248c2ecf20Sopenharmony_ci case WMI_BA_STATUS_EVENTID: 5258c2ecf20Sopenharmony_ci return "WMI_BA_STATUS_EVENT"; 5268c2ecf20Sopenharmony_ci case WMI_RCP_ADDBA_REQ_EVENTID: 5278c2ecf20Sopenharmony_ci return "WMI_RCP_ADDBA_REQ_EVENT"; 5288c2ecf20Sopenharmony_ci case WMI_DELBA_EVENTID: 5298c2ecf20Sopenharmony_ci return "WMI_DELBA_EVENT"; 5308c2ecf20Sopenharmony_ci case WMI_RING_EN_EVENTID: 5318c2ecf20Sopenharmony_ci return "WMI_RING_EN_EVENT"; 5328c2ecf20Sopenharmony_ci case WMI_DATA_PORT_OPEN_EVENTID: 5338c2ecf20Sopenharmony_ci return "WMI_DATA_PORT_OPEN_EVENT"; 5348c2ecf20Sopenharmony_ci case WMI_AOA_MEAS_EVENTID: 5358c2ecf20Sopenharmony_ci return "WMI_AOA_MEAS_EVENT"; 5368c2ecf20Sopenharmony_ci case WMI_TOF_SESSION_END_EVENTID: 5378c2ecf20Sopenharmony_ci return "WMI_TOF_SESSION_END_EVENT"; 5388c2ecf20Sopenharmony_ci case WMI_TOF_GET_CAPABILITIES_EVENTID: 5398c2ecf20Sopenharmony_ci return "WMI_TOF_GET_CAPABILITIES_EVENT"; 5408c2ecf20Sopenharmony_ci case WMI_TOF_SET_LCR_EVENTID: 5418c2ecf20Sopenharmony_ci return "WMI_TOF_SET_LCR_EVENT"; 5428c2ecf20Sopenharmony_ci case WMI_TOF_SET_LCI_EVENTID: 5438c2ecf20Sopenharmony_ci return "WMI_TOF_SET_LCI_EVENT"; 5448c2ecf20Sopenharmony_ci case WMI_TOF_FTM_PER_DEST_RES_EVENTID: 5458c2ecf20Sopenharmony_ci return "WMI_TOF_FTM_PER_DEST_RES_EVENT"; 5468c2ecf20Sopenharmony_ci case WMI_TOF_CHANNEL_INFO_EVENTID: 5478c2ecf20Sopenharmony_ci return "WMI_TOF_CHANNEL_INFO_EVENT"; 5488c2ecf20Sopenharmony_ci case WMI_TRAFFIC_SUSPEND_EVENTID: 5498c2ecf20Sopenharmony_ci return "WMI_TRAFFIC_SUSPEND_EVENT"; 5508c2ecf20Sopenharmony_ci case WMI_ECHO_RSP_EVENTID: 5518c2ecf20Sopenharmony_ci return "WMI_ECHO_RSP_EVENT"; 5528c2ecf20Sopenharmony_ci case WMI_LED_CFG_DONE_EVENTID: 5538c2ecf20Sopenharmony_ci return "WMI_LED_CFG_DONE_EVENT"; 5548c2ecf20Sopenharmony_ci case WMI_PCP_STARTED_EVENTID: 5558c2ecf20Sopenharmony_ci return "WMI_PCP_STARTED_EVENT"; 5568c2ecf20Sopenharmony_ci case WMI_PCP_STOPPED_EVENTID: 5578c2ecf20Sopenharmony_ci return "WMI_PCP_STOPPED_EVENT"; 5588c2ecf20Sopenharmony_ci case WMI_GET_SSID_EVENTID: 5598c2ecf20Sopenharmony_ci return "WMI_GET_SSID_EVENT"; 5608c2ecf20Sopenharmony_ci case WMI_GET_PCP_CHANNEL_EVENTID: 5618c2ecf20Sopenharmony_ci return "WMI_GET_PCP_CHANNEL_EVENT"; 5628c2ecf20Sopenharmony_ci case WMI_P2P_CFG_DONE_EVENTID: 5638c2ecf20Sopenharmony_ci return "WMI_P2P_CFG_DONE_EVENT"; 5648c2ecf20Sopenharmony_ci case WMI_PORT_ALLOCATED_EVENTID: 5658c2ecf20Sopenharmony_ci return "WMI_PORT_ALLOCATED_EVENT"; 5668c2ecf20Sopenharmony_ci case WMI_PORT_DELETED_EVENTID: 5678c2ecf20Sopenharmony_ci return "WMI_PORT_DELETED_EVENT"; 5688c2ecf20Sopenharmony_ci case WMI_LISTEN_STARTED_EVENTID: 5698c2ecf20Sopenharmony_ci return "WMI_LISTEN_STARTED_EVENT"; 5708c2ecf20Sopenharmony_ci case WMI_SEARCH_STARTED_EVENTID: 5718c2ecf20Sopenharmony_ci return "WMI_SEARCH_STARTED_EVENT"; 5728c2ecf20Sopenharmony_ci case WMI_DISCOVERY_STOPPED_EVENTID: 5738c2ecf20Sopenharmony_ci return "WMI_DISCOVERY_STOPPED_EVENT"; 5748c2ecf20Sopenharmony_ci case WMI_CFG_RX_CHAIN_DONE_EVENTID: 5758c2ecf20Sopenharmony_ci return "WMI_CFG_RX_CHAIN_DONE_EVENT"; 5768c2ecf20Sopenharmony_ci case WMI_TEMP_SENSE_DONE_EVENTID: 5778c2ecf20Sopenharmony_ci return "WMI_TEMP_SENSE_DONE_EVENT"; 5788c2ecf20Sopenharmony_ci case WMI_RCP_ADDBA_RESP_SENT_EVENTID: 5798c2ecf20Sopenharmony_ci return "WMI_RCP_ADDBA_RESP_SENT_EVENT"; 5808c2ecf20Sopenharmony_ci case WMI_PS_DEV_PROFILE_CFG_EVENTID: 5818c2ecf20Sopenharmony_ci return "WMI_PS_DEV_PROFILE_CFG_EVENT"; 5828c2ecf20Sopenharmony_ci case WMI_SET_MGMT_RETRY_LIMIT_EVENTID: 5838c2ecf20Sopenharmony_ci return "WMI_SET_MGMT_RETRY_LIMIT_EVENT"; 5848c2ecf20Sopenharmony_ci case WMI_GET_MGMT_RETRY_LIMIT_EVENTID: 5858c2ecf20Sopenharmony_ci return "WMI_GET_MGMT_RETRY_LIMIT_EVENT"; 5868c2ecf20Sopenharmony_ci case WMI_SET_THERMAL_THROTTLING_CFG_EVENTID: 5878c2ecf20Sopenharmony_ci return "WMI_SET_THERMAL_THROTTLING_CFG_EVENT"; 5888c2ecf20Sopenharmony_ci case WMI_GET_THERMAL_THROTTLING_CFG_EVENTID: 5898c2ecf20Sopenharmony_ci return "WMI_GET_THERMAL_THROTTLING_CFG_EVENT"; 5908c2ecf20Sopenharmony_ci case WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID: 5918c2ecf20Sopenharmony_ci return "WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENT"; 5928c2ecf20Sopenharmony_ci case WMI_LO_POWER_CALIB_FROM_OTP_EVENTID: 5938c2ecf20Sopenharmony_ci return "WMI_LO_POWER_CALIB_FROM_OTP_EVENT"; 5948c2ecf20Sopenharmony_ci case WMI_START_SCHED_SCAN_EVENTID: 5958c2ecf20Sopenharmony_ci return "WMI_START_SCHED_SCAN_EVENT"; 5968c2ecf20Sopenharmony_ci case WMI_STOP_SCHED_SCAN_EVENTID: 5978c2ecf20Sopenharmony_ci return "WMI_STOP_SCHED_SCAN_EVENT"; 5988c2ecf20Sopenharmony_ci case WMI_SCHED_SCAN_RESULT_EVENTID: 5998c2ecf20Sopenharmony_ci return "WMI_SCHED_SCAN_RESULT_EVENT"; 6008c2ecf20Sopenharmony_ci case WMI_TX_STATUS_RING_CFG_DONE_EVENTID: 6018c2ecf20Sopenharmony_ci return "WMI_TX_STATUS_RING_CFG_DONE_EVENT"; 6028c2ecf20Sopenharmony_ci case WMI_RX_STATUS_RING_CFG_DONE_EVENTID: 6038c2ecf20Sopenharmony_ci return "WMI_RX_STATUS_RING_CFG_DONE_EVENT"; 6048c2ecf20Sopenharmony_ci case WMI_TX_DESC_RING_CFG_DONE_EVENTID: 6058c2ecf20Sopenharmony_ci return "WMI_TX_DESC_RING_CFG_DONE_EVENT"; 6068c2ecf20Sopenharmony_ci case WMI_RX_DESC_RING_CFG_DONE_EVENTID: 6078c2ecf20Sopenharmony_ci return "WMI_RX_DESC_RING_CFG_DONE_EVENT"; 6088c2ecf20Sopenharmony_ci case WMI_CFG_DEF_RX_OFFLOAD_DONE_EVENTID: 6098c2ecf20Sopenharmony_ci return "WMI_CFG_DEF_RX_OFFLOAD_DONE_EVENT"; 6108c2ecf20Sopenharmony_ci case WMI_LINK_STATS_CONFIG_DONE_EVENTID: 6118c2ecf20Sopenharmony_ci return "WMI_LINK_STATS_CONFIG_DONE_EVENT"; 6128c2ecf20Sopenharmony_ci case WMI_LINK_STATS_EVENTID: 6138c2ecf20Sopenharmony_ci return "WMI_LINK_STATS_EVENT"; 6148c2ecf20Sopenharmony_ci case WMI_COMMAND_NOT_SUPPORTED_EVENTID: 6158c2ecf20Sopenharmony_ci return "WMI_COMMAND_NOT_SUPPORTED_EVENT"; 6168c2ecf20Sopenharmony_ci case WMI_FT_AUTH_STATUS_EVENTID: 6178c2ecf20Sopenharmony_ci return "WMI_FT_AUTH_STATUS_EVENT"; 6188c2ecf20Sopenharmony_ci case WMI_FT_REASSOC_STATUS_EVENTID: 6198c2ecf20Sopenharmony_ci return "WMI_FT_REASSOC_STATUS_EVENT"; 6208c2ecf20Sopenharmony_ci case WMI_RBUFCAP_CFG_EVENTID: 6218c2ecf20Sopenharmony_ci return "WMI_RBUFCAP_CFG_EVENT"; 6228c2ecf20Sopenharmony_ci case WMI_TEMP_SENSE_ALL_DONE_EVENTID: 6238c2ecf20Sopenharmony_ci return "WMI_TEMP_SENSE_ALL_DONE_EVENTID"; 6248c2ecf20Sopenharmony_ci case WMI_SET_LINK_MONITOR_EVENTID: 6258c2ecf20Sopenharmony_ci return "WMI_SET_LINK_MONITOR_EVENT"; 6268c2ecf20Sopenharmony_ci case WMI_LINK_MONITOR_EVENTID: 6278c2ecf20Sopenharmony_ci return "WMI_LINK_MONITOR_EVENT"; 6288c2ecf20Sopenharmony_ci default: 6298c2ecf20Sopenharmony_ci return "Untracked EVENT"; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic int __wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, 6348c2ecf20Sopenharmony_ci void *buf, u16 len) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci struct { 6378c2ecf20Sopenharmony_ci struct wil6210_mbox_hdr hdr; 6388c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 6398c2ecf20Sopenharmony_ci } __packed cmd = { 6408c2ecf20Sopenharmony_ci .hdr = { 6418c2ecf20Sopenharmony_ci .type = WIL_MBOX_HDR_TYPE_WMI, 6428c2ecf20Sopenharmony_ci .flags = 0, 6438c2ecf20Sopenharmony_ci .len = cpu_to_le16(sizeof(cmd.wmi) + len), 6448c2ecf20Sopenharmony_ci }, 6458c2ecf20Sopenharmony_ci .wmi = { 6468c2ecf20Sopenharmony_ci .mid = mid, 6478c2ecf20Sopenharmony_ci .command_id = cpu_to_le16(cmdid), 6488c2ecf20Sopenharmony_ci }, 6498c2ecf20Sopenharmony_ci }; 6508c2ecf20Sopenharmony_ci struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx; 6518c2ecf20Sopenharmony_ci struct wil6210_mbox_ring_desc d_head; 6528c2ecf20Sopenharmony_ci u32 next_head; 6538c2ecf20Sopenharmony_ci void __iomem *dst; 6548c2ecf20Sopenharmony_ci void __iomem *head = wmi_addr(wil, r->head); 6558c2ecf20Sopenharmony_ci uint retry; 6568c2ecf20Sopenharmony_ci int rc = 0; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (len > r->entry_size - sizeof(cmd)) { 6598c2ecf20Sopenharmony_ci wil_err(wil, "WMI size too large: %d bytes, max is %d\n", 6608c2ecf20Sopenharmony_ci (int)(sizeof(cmd) + len), r->entry_size); 6618c2ecf20Sopenharmony_ci return -ERANGE; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci might_sleep(); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci if (!test_bit(wil_status_fwready, wil->status)) { 6678c2ecf20Sopenharmony_ci wil_err(wil, "WMI: cannot send command while FW not ready\n"); 6688c2ecf20Sopenharmony_ci return -EAGAIN; 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci /* Allow sending only suspend / resume commands during susepnd flow */ 6728c2ecf20Sopenharmony_ci if ((test_bit(wil_status_suspending, wil->status) || 6738c2ecf20Sopenharmony_ci test_bit(wil_status_suspended, wil->status) || 6748c2ecf20Sopenharmony_ci test_bit(wil_status_resuming, wil->status)) && 6758c2ecf20Sopenharmony_ci ((cmdid != WMI_TRAFFIC_SUSPEND_CMDID) && 6768c2ecf20Sopenharmony_ci (cmdid != WMI_TRAFFIC_RESUME_CMDID))) { 6778c2ecf20Sopenharmony_ci wil_err(wil, "WMI: reject send_command during suspend\n"); 6788c2ecf20Sopenharmony_ci return -EINVAL; 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (!head) { 6828c2ecf20Sopenharmony_ci wil_err(wil, "WMI head is garbage: 0x%08x\n", r->head); 6838c2ecf20Sopenharmony_ci return -EINVAL; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci wil_halp_vote(wil); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* read Tx head till it is not busy */ 6898c2ecf20Sopenharmony_ci for (retry = 5; retry > 0; retry--) { 6908c2ecf20Sopenharmony_ci wil_memcpy_fromio_32(&d_head, head, sizeof(d_head)); 6918c2ecf20Sopenharmony_ci if (d_head.sync == 0) 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci msleep(20); 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci if (d_head.sync != 0) { 6968c2ecf20Sopenharmony_ci wil_err(wil, "WMI head busy\n"); 6978c2ecf20Sopenharmony_ci rc = -EBUSY; 6988c2ecf20Sopenharmony_ci goto out; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci /* next head */ 7018c2ecf20Sopenharmony_ci next_head = r->base + ((r->head - r->base + sizeof(d_head)) % r->size); 7028c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head); 7038c2ecf20Sopenharmony_ci /* wait till FW finish with previous command */ 7048c2ecf20Sopenharmony_ci for (retry = 5; retry > 0; retry--) { 7058c2ecf20Sopenharmony_ci if (!test_bit(wil_status_fwready, wil->status)) { 7068c2ecf20Sopenharmony_ci wil_err(wil, "WMI: cannot send command while FW not ready\n"); 7078c2ecf20Sopenharmony_ci rc = -EAGAIN; 7088c2ecf20Sopenharmony_ci goto out; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci r->tail = wil_r(wil, RGF_MBOX + 7118c2ecf20Sopenharmony_ci offsetof(struct wil6210_mbox_ctl, tx.tail)); 7128c2ecf20Sopenharmony_ci if (next_head != r->tail) 7138c2ecf20Sopenharmony_ci break; 7148c2ecf20Sopenharmony_ci msleep(20); 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci if (next_head == r->tail) { 7178c2ecf20Sopenharmony_ci wil_err(wil, "WMI ring full\n"); 7188c2ecf20Sopenharmony_ci rc = -EBUSY; 7198c2ecf20Sopenharmony_ci goto out; 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci dst = wmi_buffer(wil, d_head.addr); 7228c2ecf20Sopenharmony_ci if (!dst) { 7238c2ecf20Sopenharmony_ci wil_err(wil, "invalid WMI buffer: 0x%08x\n", 7248c2ecf20Sopenharmony_ci le32_to_cpu(d_head.addr)); 7258c2ecf20Sopenharmony_ci rc = -EAGAIN; 7268c2ecf20Sopenharmony_ci goto out; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq); 7298c2ecf20Sopenharmony_ci /* set command */ 7308c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "sending %s (0x%04x) [%d] mid %d\n", 7318c2ecf20Sopenharmony_ci cmdid2name(cmdid), cmdid, len, mid); 7328c2ecf20Sopenharmony_ci wil_hex_dump_wmi("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd, 7338c2ecf20Sopenharmony_ci sizeof(cmd), true); 7348c2ecf20Sopenharmony_ci wil_hex_dump_wmi("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf, 7358c2ecf20Sopenharmony_ci len, true); 7368c2ecf20Sopenharmony_ci wil_memcpy_toio_32(dst, &cmd, sizeof(cmd)); 7378c2ecf20Sopenharmony_ci wil_memcpy_toio_32(dst + sizeof(cmd), buf, len); 7388c2ecf20Sopenharmony_ci /* mark entry as full */ 7398c2ecf20Sopenharmony_ci wil_w(wil, r->head + offsetof(struct wil6210_mbox_ring_desc, sync), 1); 7408c2ecf20Sopenharmony_ci /* advance next ptr */ 7418c2ecf20Sopenharmony_ci wil_w(wil, RGF_MBOX + offsetof(struct wil6210_mbox_ctl, tx.head), 7428c2ecf20Sopenharmony_ci r->head = next_head); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci trace_wil6210_wmi_cmd(&cmd.wmi, buf, len); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci /* interrupt to FW */ 7478c2ecf20Sopenharmony_ci wil_w(wil, RGF_USER_USER_ICR + offsetof(struct RGF_ICR, ICS), 7488c2ecf20Sopenharmony_ci SW_INT_MBOX); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ciout: 7518c2ecf20Sopenharmony_ci wil_halp_unvote(wil); 7528c2ecf20Sopenharmony_ci return rc; 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ciint wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci int rc; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci mutex_lock(&wil->wmi_mutex); 7608c2ecf20Sopenharmony_ci rc = __wmi_send(wil, cmdid, mid, buf, len); 7618c2ecf20Sopenharmony_ci mutex_unlock(&wil->wmi_mutex); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci return rc; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci/*=== Event handlers ===*/ 7678c2ecf20Sopenharmony_cistatic void wmi_evt_ready(struct wil6210_vif *vif, int id, void *d, int len) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 7708c2ecf20Sopenharmony_ci struct wiphy *wiphy = wil_to_wiphy(wil); 7718c2ecf20Sopenharmony_ci struct wmi_ready_event *evt = d; 7728c2ecf20Sopenharmony_ci u8 fw_max_assoc_sta; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci wil_info(wil, "FW ver. %s(SW %d); MAC %pM; %d MID's\n", 7758c2ecf20Sopenharmony_ci wil->fw_version, le32_to_cpu(evt->sw_version), 7768c2ecf20Sopenharmony_ci evt->mac, evt->numof_additional_mids); 7778c2ecf20Sopenharmony_ci if (evt->numof_additional_mids + 1 < wil->max_vifs) { 7788c2ecf20Sopenharmony_ci wil_err(wil, "FW does not support enough MIDs (need %d)", 7798c2ecf20Sopenharmony_ci wil->max_vifs - 1); 7808c2ecf20Sopenharmony_ci return; /* FW load will fail after timeout */ 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci /* ignore MAC address, we already have it from the boot loader */ 7838c2ecf20Sopenharmony_ci strlcpy(wiphy->fw_version, wil->fw_version, sizeof(wiphy->fw_version)); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (len > offsetof(struct wmi_ready_event, rfc_read_calib_result)) { 7868c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "rfc calibration result %d\n", 7878c2ecf20Sopenharmony_ci evt->rfc_read_calib_result); 7888c2ecf20Sopenharmony_ci wil->fw_calib_result = evt->rfc_read_calib_result; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci fw_max_assoc_sta = WIL6210_RX_DESC_MAX_CID; 7928c2ecf20Sopenharmony_ci if (len > offsetof(struct wmi_ready_event, max_assoc_sta) && 7938c2ecf20Sopenharmony_ci evt->max_assoc_sta > 0) { 7948c2ecf20Sopenharmony_ci fw_max_assoc_sta = evt->max_assoc_sta; 7958c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "fw reported max assoc sta %d\n", 7968c2ecf20Sopenharmony_ci fw_max_assoc_sta); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (fw_max_assoc_sta > WIL6210_MAX_CID) { 7998c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, 8008c2ecf20Sopenharmony_ci "fw max assoc sta %d exceeds max driver supported %d\n", 8018c2ecf20Sopenharmony_ci fw_max_assoc_sta, WIL6210_MAX_CID); 8028c2ecf20Sopenharmony_ci fw_max_assoc_sta = WIL6210_MAX_CID; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci wil->max_assoc_sta = min_t(uint, max_assoc_sta, fw_max_assoc_sta); 8078c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "setting max assoc sta to %d\n", wil->max_assoc_sta); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci wil_set_recovery_state(wil, fw_recovery_idle); 8108c2ecf20Sopenharmony_ci set_bit(wil_status_fwready, wil->status); 8118c2ecf20Sopenharmony_ci /* let the reset sequence continue */ 8128c2ecf20Sopenharmony_ci complete(&wil->wmi_ready); 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic void wmi_evt_rx_mgmt(struct wil6210_vif *vif, int id, void *d, int len) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 8188c2ecf20Sopenharmony_ci struct wmi_rx_mgmt_packet_event *data = d; 8198c2ecf20Sopenharmony_ci struct wiphy *wiphy = wil_to_wiphy(wil); 8208c2ecf20Sopenharmony_ci struct ieee80211_mgmt *rx_mgmt_frame = 8218c2ecf20Sopenharmony_ci (struct ieee80211_mgmt *)data->payload; 8228c2ecf20Sopenharmony_ci int flen = len - offsetof(struct wmi_rx_mgmt_packet_event, payload); 8238c2ecf20Sopenharmony_ci int ch_no; 8248c2ecf20Sopenharmony_ci u32 freq; 8258c2ecf20Sopenharmony_ci struct ieee80211_channel *channel; 8268c2ecf20Sopenharmony_ci s32 signal; 8278c2ecf20Sopenharmony_ci __le16 fc; 8288c2ecf20Sopenharmony_ci u32 d_len; 8298c2ecf20Sopenharmony_ci u16 d_status; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci if (flen < 0) { 8328c2ecf20Sopenharmony_ci wil_err(wil, "MGMT Rx: short event, len %d\n", len); 8338c2ecf20Sopenharmony_ci return; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci d_len = le32_to_cpu(data->info.len); 8378c2ecf20Sopenharmony_ci if (d_len != flen) { 8388c2ecf20Sopenharmony_ci wil_err(wil, 8398c2ecf20Sopenharmony_ci "MGMT Rx: length mismatch, d_len %d should be %d\n", 8408c2ecf20Sopenharmony_ci d_len, flen); 8418c2ecf20Sopenharmony_ci return; 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci ch_no = data->info.channel + 1; 8458c2ecf20Sopenharmony_ci freq = ieee80211_channel_to_frequency(ch_no, NL80211_BAND_60GHZ); 8468c2ecf20Sopenharmony_ci channel = ieee80211_get_channel(wiphy, freq); 8478c2ecf20Sopenharmony_ci if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities)) 8488c2ecf20Sopenharmony_ci signal = 100 * data->info.rssi; 8498c2ecf20Sopenharmony_ci else 8508c2ecf20Sopenharmony_ci signal = data->info.sqi; 8518c2ecf20Sopenharmony_ci d_status = le16_to_cpu(data->info.status); 8528c2ecf20Sopenharmony_ci fc = rx_mgmt_frame->frame_control; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "MGMT Rx: channel %d MCS %d RSSI %d SQI %d%%\n", 8558c2ecf20Sopenharmony_ci data->info.channel, data->info.mcs, data->info.rssi, 8568c2ecf20Sopenharmony_ci data->info.sqi); 8578c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "status 0x%04x len %d fc 0x%04x\n", d_status, d_len, 8588c2ecf20Sopenharmony_ci le16_to_cpu(fc)); 8598c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "qid %d mid %d cid %d\n", 8608c2ecf20Sopenharmony_ci data->info.qid, data->info.mid, data->info.cid); 8618c2ecf20Sopenharmony_ci wil_hex_dump_wmi("MGMT Rx ", DUMP_PREFIX_OFFSET, 16, 1, rx_mgmt_frame, 8628c2ecf20Sopenharmony_ci d_len, true); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (!channel) { 8658c2ecf20Sopenharmony_ci wil_err(wil, "Frame on unsupported channel\n"); 8668c2ecf20Sopenharmony_ci return; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) { 8708c2ecf20Sopenharmony_ci struct cfg80211_bss *bss; 8718c2ecf20Sopenharmony_ci struct cfg80211_inform_bss bss_data = { 8728c2ecf20Sopenharmony_ci .chan = channel, 8738c2ecf20Sopenharmony_ci .scan_width = NL80211_BSS_CHAN_WIDTH_20, 8748c2ecf20Sopenharmony_ci .signal = signal, 8758c2ecf20Sopenharmony_ci .boottime_ns = ktime_to_ns(ktime_get_boottime()), 8768c2ecf20Sopenharmony_ci }; 8778c2ecf20Sopenharmony_ci u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp); 8788c2ecf20Sopenharmony_ci u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info); 8798c2ecf20Sopenharmony_ci u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int); 8808c2ecf20Sopenharmony_ci const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable; 8818c2ecf20Sopenharmony_ci size_t ie_len = d_len - offsetof(struct ieee80211_mgmt, 8828c2ecf20Sopenharmony_ci u.beacon.variable); 8838c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap); 8848c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "TSF : 0x%016llx\n", tsf); 8858c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "Beacon interval : %d\n", bi); 8868c2ecf20Sopenharmony_ci wil_hex_dump_wmi("IE ", DUMP_PREFIX_OFFSET, 16, 1, ie_buf, 8878c2ecf20Sopenharmony_ci ie_len, true); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci bss = cfg80211_inform_bss_frame_data(wiphy, &bss_data, 8928c2ecf20Sopenharmony_ci rx_mgmt_frame, 8938c2ecf20Sopenharmony_ci d_len, GFP_KERNEL); 8948c2ecf20Sopenharmony_ci if (bss) { 8958c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "Added BSS %pM\n", 8968c2ecf20Sopenharmony_ci rx_mgmt_frame->bssid); 8978c2ecf20Sopenharmony_ci cfg80211_put_bss(wiphy, bss); 8988c2ecf20Sopenharmony_ci } else { 8998c2ecf20Sopenharmony_ci wil_err(wil, "cfg80211_inform_bss_frame() failed\n"); 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci } else { 9028c2ecf20Sopenharmony_ci mutex_lock(&wil->vif_mutex); 9038c2ecf20Sopenharmony_ci cfg80211_rx_mgmt(vif_to_radio_wdev(wil, vif), freq, signal, 9048c2ecf20Sopenharmony_ci (void *)rx_mgmt_frame, d_len, 0); 9058c2ecf20Sopenharmony_ci mutex_unlock(&wil->vif_mutex); 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci} 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cistatic void wmi_evt_tx_mgmt(struct wil6210_vif *vif, int id, void *d, int len) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci struct wmi_tx_mgmt_packet_event *data = d; 9128c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt_frame = 9138c2ecf20Sopenharmony_ci (struct ieee80211_mgmt *)data->payload; 9148c2ecf20Sopenharmony_ci int flen = len - offsetof(struct wmi_tx_mgmt_packet_event, payload); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci wil_hex_dump_wmi("MGMT Tx ", DUMP_PREFIX_OFFSET, 16, 1, mgmt_frame, 9178c2ecf20Sopenharmony_ci flen, true); 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_cistatic void wmi_evt_scan_complete(struct wil6210_vif *vif, int id, 9218c2ecf20Sopenharmony_ci void *d, int len) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci mutex_lock(&wil->vif_mutex); 9268c2ecf20Sopenharmony_ci if (vif->scan_request) { 9278c2ecf20Sopenharmony_ci struct wmi_scan_complete_event *data = d; 9288c2ecf20Sopenharmony_ci int status = le32_to_cpu(data->status); 9298c2ecf20Sopenharmony_ci struct cfg80211_scan_info info = { 9308c2ecf20Sopenharmony_ci .aborted = ((status != WMI_SCAN_SUCCESS) && 9318c2ecf20Sopenharmony_ci (status != WMI_SCAN_ABORT_REJECTED)), 9328c2ecf20Sopenharmony_ci }; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", status); 9358c2ecf20Sopenharmony_ci wil_dbg_misc(wil, "Complete scan_request 0x%p aborted %d\n", 9368c2ecf20Sopenharmony_ci vif->scan_request, info.aborted); 9378c2ecf20Sopenharmony_ci del_timer_sync(&vif->scan_timer); 9388c2ecf20Sopenharmony_ci cfg80211_scan_done(vif->scan_request, &info); 9398c2ecf20Sopenharmony_ci if (vif->mid == 0) 9408c2ecf20Sopenharmony_ci wil->radio_wdev = wil->main_ndev->ieee80211_ptr; 9418c2ecf20Sopenharmony_ci vif->scan_request = NULL; 9428c2ecf20Sopenharmony_ci wake_up_interruptible(&wil->wq); 9438c2ecf20Sopenharmony_ci if (vif->p2p.pending_listen_wdev) { 9448c2ecf20Sopenharmony_ci wil_dbg_misc(wil, "Scheduling delayed listen\n"); 9458c2ecf20Sopenharmony_ci schedule_work(&vif->p2p.delayed_listen_work); 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci } else { 9488c2ecf20Sopenharmony_ci wil_err(wil, "SCAN_COMPLETE while not scanning\n"); 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci mutex_unlock(&wil->vif_mutex); 9518c2ecf20Sopenharmony_ci} 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_cistatic void wmi_evt_connect(struct wil6210_vif *vif, int id, void *d, int len) 9548c2ecf20Sopenharmony_ci{ 9558c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 9568c2ecf20Sopenharmony_ci struct net_device *ndev = vif_to_ndev(vif); 9578c2ecf20Sopenharmony_ci struct wireless_dev *wdev = vif_to_wdev(vif); 9588c2ecf20Sopenharmony_ci struct wmi_connect_event *evt = d; 9598c2ecf20Sopenharmony_ci int ch; /* channel number */ 9608c2ecf20Sopenharmony_ci struct station_info *sinfo; 9618c2ecf20Sopenharmony_ci u8 *assoc_req_ie, *assoc_resp_ie; 9628c2ecf20Sopenharmony_ci size_t assoc_req_ielen, assoc_resp_ielen; 9638c2ecf20Sopenharmony_ci /* capinfo(u16) + listen_interval(u16) + IEs */ 9648c2ecf20Sopenharmony_ci const size_t assoc_req_ie_offset = sizeof(u16) * 2; 9658c2ecf20Sopenharmony_ci /* capinfo(u16) + status_code(u16) + associd(u16) + IEs */ 9668c2ecf20Sopenharmony_ci const size_t assoc_resp_ie_offset = sizeof(u16) * 3; 9678c2ecf20Sopenharmony_ci int rc; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci if (len < sizeof(*evt)) { 9708c2ecf20Sopenharmony_ci wil_err(wil, "Connect event too short : %d bytes\n", len); 9718c2ecf20Sopenharmony_ci return; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci if (len != sizeof(*evt) + evt->beacon_ie_len + evt->assoc_req_len + 9748c2ecf20Sopenharmony_ci evt->assoc_resp_len) { 9758c2ecf20Sopenharmony_ci wil_err(wil, 9768c2ecf20Sopenharmony_ci "Connect event corrupted : %d != %d + %d + %d + %d\n", 9778c2ecf20Sopenharmony_ci len, (int)sizeof(*evt), evt->beacon_ie_len, 9788c2ecf20Sopenharmony_ci evt->assoc_req_len, evt->assoc_resp_len); 9798c2ecf20Sopenharmony_ci return; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci if (evt->cid >= wil->max_assoc_sta) { 9828c2ecf20Sopenharmony_ci wil_err(wil, "Connect CID invalid : %d\n", evt->cid); 9838c2ecf20Sopenharmony_ci return; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci ch = evt->channel + 1; 9878c2ecf20Sopenharmony_ci wil_info(wil, "Connect %pM channel [%d] cid %d aid %d\n", 9888c2ecf20Sopenharmony_ci evt->bssid, ch, evt->cid, evt->aid); 9898c2ecf20Sopenharmony_ci wil_hex_dump_wmi("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1, 9908c2ecf20Sopenharmony_ci evt->assoc_info, len - sizeof(*evt), true); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci /* figure out IE's */ 9938c2ecf20Sopenharmony_ci assoc_req_ie = &evt->assoc_info[evt->beacon_ie_len + 9948c2ecf20Sopenharmony_ci assoc_req_ie_offset]; 9958c2ecf20Sopenharmony_ci assoc_req_ielen = evt->assoc_req_len - assoc_req_ie_offset; 9968c2ecf20Sopenharmony_ci if (evt->assoc_req_len <= assoc_req_ie_offset) { 9978c2ecf20Sopenharmony_ci assoc_req_ie = NULL; 9988c2ecf20Sopenharmony_ci assoc_req_ielen = 0; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci assoc_resp_ie = &evt->assoc_info[evt->beacon_ie_len + 10028c2ecf20Sopenharmony_ci evt->assoc_req_len + 10038c2ecf20Sopenharmony_ci assoc_resp_ie_offset]; 10048c2ecf20Sopenharmony_ci assoc_resp_ielen = evt->assoc_resp_len - assoc_resp_ie_offset; 10058c2ecf20Sopenharmony_ci if (evt->assoc_resp_len <= assoc_resp_ie_offset) { 10068c2ecf20Sopenharmony_ci assoc_resp_ie = NULL; 10078c2ecf20Sopenharmony_ci assoc_resp_ielen = 0; 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if (test_bit(wil_status_resetting, wil->status) || 10118c2ecf20Sopenharmony_ci !test_bit(wil_status_fwready, wil->status)) { 10128c2ecf20Sopenharmony_ci wil_err(wil, "status_resetting, cancel connect event, CID %d\n", 10138c2ecf20Sopenharmony_ci evt->cid); 10148c2ecf20Sopenharmony_ci /* no need for cleanup, wil_reset will do that */ 10158c2ecf20Sopenharmony_ci return; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci mutex_lock(&wil->mutex); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci if ((wdev->iftype == NL80211_IFTYPE_STATION) || 10218c2ecf20Sopenharmony_ci (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { 10228c2ecf20Sopenharmony_ci if (!test_bit(wil_vif_fwconnecting, vif->status)) { 10238c2ecf20Sopenharmony_ci wil_err(wil, "Not in connecting state\n"); 10248c2ecf20Sopenharmony_ci mutex_unlock(&wil->mutex); 10258c2ecf20Sopenharmony_ci return; 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci del_timer_sync(&vif->connect_timer); 10288c2ecf20Sopenharmony_ci } else if ((wdev->iftype == NL80211_IFTYPE_AP) || 10298c2ecf20Sopenharmony_ci (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { 10308c2ecf20Sopenharmony_ci if (wil->sta[evt->cid].status != wil_sta_unused) { 10318c2ecf20Sopenharmony_ci wil_err(wil, "AP: Invalid status %d for CID %d\n", 10328c2ecf20Sopenharmony_ci wil->sta[evt->cid].status, evt->cid); 10338c2ecf20Sopenharmony_ci mutex_unlock(&wil->mutex); 10348c2ecf20Sopenharmony_ci return; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci ether_addr_copy(wil->sta[evt->cid].addr, evt->bssid); 10398c2ecf20Sopenharmony_ci wil->sta[evt->cid].mid = vif->mid; 10408c2ecf20Sopenharmony_ci wil->sta[evt->cid].status = wil_sta_conn_pending; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci rc = wil_ring_init_tx(vif, evt->cid); 10438c2ecf20Sopenharmony_ci if (rc) { 10448c2ecf20Sopenharmony_ci wil_err(wil, "config tx vring failed for CID %d, rc (%d)\n", 10458c2ecf20Sopenharmony_ci evt->cid, rc); 10468c2ecf20Sopenharmony_ci wmi_disconnect_sta(vif, wil->sta[evt->cid].addr, 10478c2ecf20Sopenharmony_ci WLAN_REASON_UNSPECIFIED, false); 10488c2ecf20Sopenharmony_ci } else { 10498c2ecf20Sopenharmony_ci wil_info(wil, "successful connection to CID %d\n", evt->cid); 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if ((wdev->iftype == NL80211_IFTYPE_STATION) || 10538c2ecf20Sopenharmony_ci (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { 10548c2ecf20Sopenharmony_ci if (rc) { 10558c2ecf20Sopenharmony_ci netif_carrier_off(ndev); 10568c2ecf20Sopenharmony_ci wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); 10578c2ecf20Sopenharmony_ci wil_err(wil, "cfg80211_connect_result with failure\n"); 10588c2ecf20Sopenharmony_ci cfg80211_connect_result(ndev, evt->bssid, NULL, 0, 10598c2ecf20Sopenharmony_ci NULL, 0, 10608c2ecf20Sopenharmony_ci WLAN_STATUS_UNSPECIFIED_FAILURE, 10618c2ecf20Sopenharmony_ci GFP_KERNEL); 10628c2ecf20Sopenharmony_ci goto out; 10638c2ecf20Sopenharmony_ci } else { 10648c2ecf20Sopenharmony_ci struct wiphy *wiphy = wil_to_wiphy(wil); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci cfg80211_ref_bss(wiphy, vif->bss); 10678c2ecf20Sopenharmony_ci cfg80211_connect_bss(ndev, evt->bssid, vif->bss, 10688c2ecf20Sopenharmony_ci assoc_req_ie, assoc_req_ielen, 10698c2ecf20Sopenharmony_ci assoc_resp_ie, assoc_resp_ielen, 10708c2ecf20Sopenharmony_ci WLAN_STATUS_SUCCESS, GFP_KERNEL, 10718c2ecf20Sopenharmony_ci NL80211_TIMEOUT_UNSPECIFIED); 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci vif->bss = NULL; 10748c2ecf20Sopenharmony_ci } else if ((wdev->iftype == NL80211_IFTYPE_AP) || 10758c2ecf20Sopenharmony_ci (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci if (rc) { 10788c2ecf20Sopenharmony_ci if (disable_ap_sme) 10798c2ecf20Sopenharmony_ci /* notify new_sta has failed */ 10808c2ecf20Sopenharmony_ci cfg80211_del_sta(ndev, evt->bssid, GFP_KERNEL); 10818c2ecf20Sopenharmony_ci goto out; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); 10858c2ecf20Sopenharmony_ci if (!sinfo) { 10868c2ecf20Sopenharmony_ci rc = -ENOMEM; 10878c2ecf20Sopenharmony_ci goto out; 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci sinfo->generation = wil->sinfo_gen++; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (assoc_req_ie) { 10938c2ecf20Sopenharmony_ci sinfo->assoc_req_ies = assoc_req_ie; 10948c2ecf20Sopenharmony_ci sinfo->assoc_req_ies_len = assoc_req_ielen; 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci cfg80211_new_sta(ndev, evt->bssid, sinfo, GFP_KERNEL); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci kfree(sinfo); 11008c2ecf20Sopenharmony_ci } else { 11018c2ecf20Sopenharmony_ci wil_err(wil, "unhandled iftype %d for CID %d\n", wdev->iftype, 11028c2ecf20Sopenharmony_ci evt->cid); 11038c2ecf20Sopenharmony_ci goto out; 11048c2ecf20Sopenharmony_ci } 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci wil->sta[evt->cid].status = wil_sta_connected; 11078c2ecf20Sopenharmony_ci wil->sta[evt->cid].aid = evt->aid; 11088c2ecf20Sopenharmony_ci if (!test_and_set_bit(wil_vif_fwconnected, vif->status)) 11098c2ecf20Sopenharmony_ci atomic_inc(&wil->connected_vifs); 11108c2ecf20Sopenharmony_ci wil_update_net_queues_bh(wil, vif, NULL, false); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ciout: 11138c2ecf20Sopenharmony_ci if (rc) { 11148c2ecf20Sopenharmony_ci wil->sta[evt->cid].status = wil_sta_unused; 11158c2ecf20Sopenharmony_ci wil->sta[evt->cid].mid = U8_MAX; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci clear_bit(wil_vif_fwconnecting, vif->status); 11188c2ecf20Sopenharmony_ci mutex_unlock(&wil->mutex); 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cistatic void wmi_evt_disconnect(struct wil6210_vif *vif, int id, 11228c2ecf20Sopenharmony_ci void *d, int len) 11238c2ecf20Sopenharmony_ci{ 11248c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 11258c2ecf20Sopenharmony_ci struct wmi_disconnect_event *evt = d; 11268c2ecf20Sopenharmony_ci u16 reason_code = le16_to_cpu(evt->protocol_reason_status); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci wil_info(wil, "Disconnect %pM reason [proto %d wmi %d]\n", 11298c2ecf20Sopenharmony_ci evt->bssid, reason_code, evt->disconnect_reason); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci wil->sinfo_gen++; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci if (test_bit(wil_status_resetting, wil->status) || 11348c2ecf20Sopenharmony_ci !test_bit(wil_status_fwready, wil->status)) { 11358c2ecf20Sopenharmony_ci wil_err(wil, "status_resetting, cancel disconnect event\n"); 11368c2ecf20Sopenharmony_ci /* no need for cleanup, wil_reset will do that */ 11378c2ecf20Sopenharmony_ci return; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci mutex_lock(&wil->mutex); 11418c2ecf20Sopenharmony_ci wil6210_disconnect_complete(vif, evt->bssid, reason_code); 11428c2ecf20Sopenharmony_ci if (disable_ap_sme) { 11438c2ecf20Sopenharmony_ci struct wireless_dev *wdev = vif_to_wdev(vif); 11448c2ecf20Sopenharmony_ci struct net_device *ndev = vif_to_ndev(vif); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci /* disconnect event in disable_ap_sme mode means link loss */ 11478c2ecf20Sopenharmony_ci switch (wdev->iftype) { 11488c2ecf20Sopenharmony_ci /* AP-like interface */ 11498c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 11508c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_GO: 11518c2ecf20Sopenharmony_ci /* notify hostapd about link loss */ 11528c2ecf20Sopenharmony_ci cfg80211_cqm_pktloss_notify(ndev, evt->bssid, 0, 11538c2ecf20Sopenharmony_ci GFP_KERNEL); 11548c2ecf20Sopenharmony_ci break; 11558c2ecf20Sopenharmony_ci default: 11568c2ecf20Sopenharmony_ci break; 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci mutex_unlock(&wil->mutex); 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci/* 11638c2ecf20Sopenharmony_ci * Firmware reports EAPOL frame using WME event. 11648c2ecf20Sopenharmony_ci * Reconstruct Ethernet frame and deliver it via normal Rx 11658c2ecf20Sopenharmony_ci */ 11668c2ecf20Sopenharmony_cistatic void wmi_evt_eapol_rx(struct wil6210_vif *vif, int id, void *d, int len) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 11698c2ecf20Sopenharmony_ci struct net_device *ndev = vif_to_ndev(vif); 11708c2ecf20Sopenharmony_ci struct wmi_eapol_rx_event *evt = d; 11718c2ecf20Sopenharmony_ci u16 eapol_len = le16_to_cpu(evt->eapol_len); 11728c2ecf20Sopenharmony_ci int sz = eapol_len + ETH_HLEN; 11738c2ecf20Sopenharmony_ci struct sk_buff *skb; 11748c2ecf20Sopenharmony_ci struct ethhdr *eth; 11758c2ecf20Sopenharmony_ci int cid; 11768c2ecf20Sopenharmony_ci struct wil_net_stats *stats = NULL; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "EAPOL len %d from %pM MID %d\n", eapol_len, 11798c2ecf20Sopenharmony_ci evt->src_mac, vif->mid); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci cid = wil_find_cid(wil, vif->mid, evt->src_mac); 11828c2ecf20Sopenharmony_ci if (cid >= 0) 11838c2ecf20Sopenharmony_ci stats = &wil->sta[cid].stats; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci if (eapol_len > 196) { /* TODO: revisit size limit */ 11868c2ecf20Sopenharmony_ci wil_err(wil, "EAPOL too large\n"); 11878c2ecf20Sopenharmony_ci return; 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci skb = alloc_skb(sz, GFP_KERNEL); 11918c2ecf20Sopenharmony_ci if (!skb) { 11928c2ecf20Sopenharmony_ci wil_err(wil, "Failed to allocate skb\n"); 11938c2ecf20Sopenharmony_ci return; 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci eth = skb_put(skb, ETH_HLEN); 11978c2ecf20Sopenharmony_ci ether_addr_copy(eth->h_dest, ndev->dev_addr); 11988c2ecf20Sopenharmony_ci ether_addr_copy(eth->h_source, evt->src_mac); 11998c2ecf20Sopenharmony_ci eth->h_proto = cpu_to_be16(ETH_P_PAE); 12008c2ecf20Sopenharmony_ci skb_put_data(skb, evt->eapol, eapol_len); 12018c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 12028c2ecf20Sopenharmony_ci if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) { 12038c2ecf20Sopenharmony_ci ndev->stats.rx_packets++; 12048c2ecf20Sopenharmony_ci ndev->stats.rx_bytes += sz; 12058c2ecf20Sopenharmony_ci if (stats) { 12068c2ecf20Sopenharmony_ci stats->rx_packets++; 12078c2ecf20Sopenharmony_ci stats->rx_bytes += sz; 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci } else { 12108c2ecf20Sopenharmony_ci ndev->stats.rx_dropped++; 12118c2ecf20Sopenharmony_ci if (stats) 12128c2ecf20Sopenharmony_ci stats->rx_dropped++; 12138c2ecf20Sopenharmony_ci } 12148c2ecf20Sopenharmony_ci} 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_cistatic void wmi_evt_ring_en(struct wil6210_vif *vif, int id, void *d, int len) 12178c2ecf20Sopenharmony_ci{ 12188c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 12198c2ecf20Sopenharmony_ci struct wmi_ring_en_event *evt = d; 12208c2ecf20Sopenharmony_ci u8 vri = evt->ring_index; 12218c2ecf20Sopenharmony_ci struct wireless_dev *wdev = vif_to_wdev(vif); 12228c2ecf20Sopenharmony_ci struct wil_sta_info *sta; 12238c2ecf20Sopenharmony_ci u8 cid; 12248c2ecf20Sopenharmony_ci struct key_params params; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "Enable vring %d MID %d\n", vri, vif->mid); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci if (vri >= ARRAY_SIZE(wil->ring_tx)) { 12298c2ecf20Sopenharmony_ci wil_err(wil, "Enable for invalid vring %d\n", vri); 12308c2ecf20Sopenharmony_ci return; 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci if (wdev->iftype != NL80211_IFTYPE_AP || !disable_ap_sme || 12348c2ecf20Sopenharmony_ci test_bit(wil_vif_ft_roam, vif->status)) 12358c2ecf20Sopenharmony_ci /* in AP mode with disable_ap_sme that is not FT, 12368c2ecf20Sopenharmony_ci * this is done by wil_cfg80211_change_station() 12378c2ecf20Sopenharmony_ci */ 12388c2ecf20Sopenharmony_ci wil->ring_tx_data[vri].dot1x_open = true; 12398c2ecf20Sopenharmony_ci if (vri == vif->bcast_ring) /* no BA for bcast */ 12408c2ecf20Sopenharmony_ci return; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci cid = wil->ring2cid_tid[vri][0]; 12438c2ecf20Sopenharmony_ci if (!wil_cid_valid(wil, cid)) { 12448c2ecf20Sopenharmony_ci wil_err(wil, "invalid cid %d for vring %d\n", cid, vri); 12458c2ecf20Sopenharmony_ci return; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci /* In FT mode we get key but not store it as it is received 12498c2ecf20Sopenharmony_ci * before WMI_CONNECT_EVENT received from FW. 12508c2ecf20Sopenharmony_ci * wil_set_crypto_rx is called here to reset the security PN 12518c2ecf20Sopenharmony_ci */ 12528c2ecf20Sopenharmony_ci sta = &wil->sta[cid]; 12538c2ecf20Sopenharmony_ci if (test_bit(wil_vif_ft_roam, vif->status)) { 12548c2ecf20Sopenharmony_ci memset(¶ms, 0, sizeof(params)); 12558c2ecf20Sopenharmony_ci wil_set_crypto_rx(0, WMI_KEY_USE_PAIRWISE, sta, ¶ms); 12568c2ecf20Sopenharmony_ci if (wdev->iftype != NL80211_IFTYPE_AP) 12578c2ecf20Sopenharmony_ci clear_bit(wil_vif_ft_roam, vif->status); 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci if (agg_wsize >= 0) 12618c2ecf20Sopenharmony_ci wil_addba_tx_request(wil, vri, agg_wsize); 12628c2ecf20Sopenharmony_ci} 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_cistatic void wmi_evt_ba_status(struct wil6210_vif *vif, int id, 12658c2ecf20Sopenharmony_ci void *d, int len) 12668c2ecf20Sopenharmony_ci{ 12678c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 12688c2ecf20Sopenharmony_ci struct wmi_ba_status_event *evt = d; 12698c2ecf20Sopenharmony_ci struct wil_ring_tx_data *txdata; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "BACK[%d] %s {%d} timeout %d AMSDU%s\n", 12728c2ecf20Sopenharmony_ci evt->ringid, 12738c2ecf20Sopenharmony_ci evt->status == WMI_BA_AGREED ? "OK" : "N/A", 12748c2ecf20Sopenharmony_ci evt->agg_wsize, __le16_to_cpu(evt->ba_timeout), 12758c2ecf20Sopenharmony_ci evt->amsdu ? "+" : "-"); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci if (evt->ringid >= WIL6210_MAX_TX_RINGS) { 12788c2ecf20Sopenharmony_ci wil_err(wil, "invalid ring id %d\n", evt->ringid); 12798c2ecf20Sopenharmony_ci return; 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci if (evt->status != WMI_BA_AGREED) { 12838c2ecf20Sopenharmony_ci evt->ba_timeout = 0; 12848c2ecf20Sopenharmony_ci evt->agg_wsize = 0; 12858c2ecf20Sopenharmony_ci evt->amsdu = 0; 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci txdata = &wil->ring_tx_data[evt->ringid]; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci txdata->agg_timeout = le16_to_cpu(evt->ba_timeout); 12918c2ecf20Sopenharmony_ci txdata->agg_wsize = evt->agg_wsize; 12928c2ecf20Sopenharmony_ci txdata->agg_amsdu = evt->amsdu; 12938c2ecf20Sopenharmony_ci txdata->addba_in_progress = false; 12948c2ecf20Sopenharmony_ci} 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_cistatic void wmi_evt_addba_rx_req(struct wil6210_vif *vif, int id, 12978c2ecf20Sopenharmony_ci void *d, int len) 12988c2ecf20Sopenharmony_ci{ 12998c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 13008c2ecf20Sopenharmony_ci u8 cid, tid; 13018c2ecf20Sopenharmony_ci struct wmi_rcp_addba_req_event *evt = d; 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci if (evt->cidxtid != CIDXTID_EXTENDED_CID_TID) { 13048c2ecf20Sopenharmony_ci parse_cidxtid(evt->cidxtid, &cid, &tid); 13058c2ecf20Sopenharmony_ci } else { 13068c2ecf20Sopenharmony_ci cid = evt->cid; 13078c2ecf20Sopenharmony_ci tid = evt->tid; 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci wil_addba_rx_request(wil, vif->mid, cid, tid, evt->dialog_token, 13108c2ecf20Sopenharmony_ci evt->ba_param_set, evt->ba_timeout, 13118c2ecf20Sopenharmony_ci evt->ba_seq_ctrl); 13128c2ecf20Sopenharmony_ci} 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_cistatic void wmi_evt_delba(struct wil6210_vif *vif, int id, void *d, int len) 13158c2ecf20Sopenharmony_ci__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) 13168c2ecf20Sopenharmony_ci{ 13178c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 13188c2ecf20Sopenharmony_ci struct wmi_delba_event *evt = d; 13198c2ecf20Sopenharmony_ci u8 cid, tid; 13208c2ecf20Sopenharmony_ci u16 reason = __le16_to_cpu(evt->reason); 13218c2ecf20Sopenharmony_ci struct wil_sta_info *sta; 13228c2ecf20Sopenharmony_ci struct wil_tid_ampdu_rx *r; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci might_sleep(); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci if (evt->cidxtid != CIDXTID_EXTENDED_CID_TID) { 13278c2ecf20Sopenharmony_ci parse_cidxtid(evt->cidxtid, &cid, &tid); 13288c2ecf20Sopenharmony_ci } else { 13298c2ecf20Sopenharmony_ci cid = evt->cid; 13308c2ecf20Sopenharmony_ci tid = evt->tid; 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci if (!wil_cid_valid(wil, cid)) { 13348c2ecf20Sopenharmony_ci wil_err(wil, "DELBA: Invalid CID %d\n", cid); 13358c2ecf20Sopenharmony_ci return; 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "DELBA MID %d CID %d TID %d from %s reason %d\n", 13398c2ecf20Sopenharmony_ci vif->mid, cid, tid, 13408c2ecf20Sopenharmony_ci evt->from_initiator ? "originator" : "recipient", 13418c2ecf20Sopenharmony_ci reason); 13428c2ecf20Sopenharmony_ci if (!evt->from_initiator) { 13438c2ecf20Sopenharmony_ci int i; 13448c2ecf20Sopenharmony_ci /* find Tx vring it belongs to */ 13458c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wil->ring2cid_tid); i++) { 13468c2ecf20Sopenharmony_ci if (wil->ring2cid_tid[i][0] == cid && 13478c2ecf20Sopenharmony_ci wil->ring2cid_tid[i][1] == tid) { 13488c2ecf20Sopenharmony_ci struct wil_ring_tx_data *txdata = 13498c2ecf20Sopenharmony_ci &wil->ring_tx_data[i]; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "DELBA Tx vring %d\n", i); 13528c2ecf20Sopenharmony_ci txdata->agg_timeout = 0; 13538c2ecf20Sopenharmony_ci txdata->agg_wsize = 0; 13548c2ecf20Sopenharmony_ci txdata->addba_in_progress = false; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci break; /* max. 1 matching ring */ 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_ci if (i >= ARRAY_SIZE(wil->ring2cid_tid)) 13608c2ecf20Sopenharmony_ci wil_err(wil, "DELBA: unable to find Tx vring\n"); 13618c2ecf20Sopenharmony_ci return; 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci sta = &wil->sta[cid]; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci spin_lock_bh(&sta->tid_rx_lock); 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci r = sta->tid_rx[tid]; 13698c2ecf20Sopenharmony_ci sta->tid_rx[tid] = NULL; 13708c2ecf20Sopenharmony_ci wil_tid_ampdu_rx_free(wil, r); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci spin_unlock_bh(&sta->tid_rx_lock); 13738c2ecf20Sopenharmony_ci} 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_cistatic void 13768c2ecf20Sopenharmony_ciwmi_evt_sched_scan_result(struct wil6210_vif *vif, int id, void *d, int len) 13778c2ecf20Sopenharmony_ci{ 13788c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 13798c2ecf20Sopenharmony_ci struct wmi_sched_scan_result_event *data = d; 13808c2ecf20Sopenharmony_ci struct wiphy *wiphy = wil_to_wiphy(wil); 13818c2ecf20Sopenharmony_ci struct ieee80211_mgmt *rx_mgmt_frame = 13828c2ecf20Sopenharmony_ci (struct ieee80211_mgmt *)data->payload; 13838c2ecf20Sopenharmony_ci int flen = len - offsetof(struct wmi_sched_scan_result_event, payload); 13848c2ecf20Sopenharmony_ci int ch_no; 13858c2ecf20Sopenharmony_ci u32 freq; 13868c2ecf20Sopenharmony_ci struct ieee80211_channel *channel; 13878c2ecf20Sopenharmony_ci s32 signal; 13888c2ecf20Sopenharmony_ci __le16 fc; 13898c2ecf20Sopenharmony_ci u32 d_len; 13908c2ecf20Sopenharmony_ci struct cfg80211_bss *bss; 13918c2ecf20Sopenharmony_ci struct cfg80211_inform_bss bss_data = { 13928c2ecf20Sopenharmony_ci .scan_width = NL80211_BSS_CHAN_WIDTH_20, 13938c2ecf20Sopenharmony_ci .boottime_ns = ktime_to_ns(ktime_get_boottime()), 13948c2ecf20Sopenharmony_ci }; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci if (flen < 0) { 13978c2ecf20Sopenharmony_ci wil_err(wil, "sched scan result event too short, len %d\n", 13988c2ecf20Sopenharmony_ci len); 13998c2ecf20Sopenharmony_ci return; 14008c2ecf20Sopenharmony_ci } 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci d_len = le32_to_cpu(data->info.len); 14038c2ecf20Sopenharmony_ci if (d_len != flen) { 14048c2ecf20Sopenharmony_ci wil_err(wil, 14058c2ecf20Sopenharmony_ci "sched scan result length mismatch, d_len %d should be %d\n", 14068c2ecf20Sopenharmony_ci d_len, flen); 14078c2ecf20Sopenharmony_ci return; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci fc = rx_mgmt_frame->frame_control; 14118c2ecf20Sopenharmony_ci if (!ieee80211_is_probe_resp(fc)) { 14128c2ecf20Sopenharmony_ci wil_err(wil, "sched scan result invalid frame, fc 0x%04x\n", 14138c2ecf20Sopenharmony_ci fc); 14148c2ecf20Sopenharmony_ci return; 14158c2ecf20Sopenharmony_ci } 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci ch_no = data->info.channel + 1; 14188c2ecf20Sopenharmony_ci freq = ieee80211_channel_to_frequency(ch_no, NL80211_BAND_60GHZ); 14198c2ecf20Sopenharmony_ci channel = ieee80211_get_channel(wiphy, freq); 14208c2ecf20Sopenharmony_ci if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities)) 14218c2ecf20Sopenharmony_ci signal = 100 * data->info.rssi; 14228c2ecf20Sopenharmony_ci else 14238c2ecf20Sopenharmony_ci signal = data->info.sqi; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "sched scan result: channel %d MCS %d RSSI %d\n", 14268c2ecf20Sopenharmony_ci data->info.channel, data->info.mcs, data->info.rssi); 14278c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "len %d qid %d mid %d cid %d\n", 14288c2ecf20Sopenharmony_ci d_len, data->info.qid, data->info.mid, data->info.cid); 14298c2ecf20Sopenharmony_ci wil_hex_dump_wmi("PROBE ", DUMP_PREFIX_OFFSET, 16, 1, rx_mgmt_frame, 14308c2ecf20Sopenharmony_ci d_len, true); 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci if (!channel) { 14338c2ecf20Sopenharmony_ci wil_err(wil, "Frame on unsupported channel\n"); 14348c2ecf20Sopenharmony_ci return; 14358c2ecf20Sopenharmony_ci } 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci bss_data.signal = signal; 14388c2ecf20Sopenharmony_ci bss_data.chan = channel; 14398c2ecf20Sopenharmony_ci bss = cfg80211_inform_bss_frame_data(wiphy, &bss_data, rx_mgmt_frame, 14408c2ecf20Sopenharmony_ci d_len, GFP_KERNEL); 14418c2ecf20Sopenharmony_ci if (bss) { 14428c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "Added BSS %pM\n", rx_mgmt_frame->bssid); 14438c2ecf20Sopenharmony_ci cfg80211_put_bss(wiphy, bss); 14448c2ecf20Sopenharmony_ci } else { 14458c2ecf20Sopenharmony_ci wil_err(wil, "cfg80211_inform_bss_frame() failed\n"); 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci cfg80211_sched_scan_results(wiphy, 0); 14498c2ecf20Sopenharmony_ci} 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_cistatic void wil_link_stats_store_basic(struct wil6210_vif *vif, 14528c2ecf20Sopenharmony_ci struct wmi_link_stats_basic *basic) 14538c2ecf20Sopenharmony_ci{ 14548c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 14558c2ecf20Sopenharmony_ci u8 cid = basic->cid; 14568c2ecf20Sopenharmony_ci struct wil_sta_info *sta; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci if (cid < 0 || cid >= wil->max_assoc_sta) { 14598c2ecf20Sopenharmony_ci wil_err(wil, "invalid cid %d\n", cid); 14608c2ecf20Sopenharmony_ci return; 14618c2ecf20Sopenharmony_ci } 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci sta = &wil->sta[cid]; 14648c2ecf20Sopenharmony_ci sta->fw_stats_basic = *basic; 14658c2ecf20Sopenharmony_ci} 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_cistatic void wil_link_stats_store_global(struct wil6210_vif *vif, 14688c2ecf20Sopenharmony_ci struct wmi_link_stats_global *global) 14698c2ecf20Sopenharmony_ci{ 14708c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci wil->fw_stats_global.stats = *global; 14738c2ecf20Sopenharmony_ci} 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_cistatic void wmi_link_stats_parse(struct wil6210_vif *vif, u64 tsf, 14768c2ecf20Sopenharmony_ci bool has_next, void *payload, 14778c2ecf20Sopenharmony_ci size_t payload_size) 14788c2ecf20Sopenharmony_ci{ 14798c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 14808c2ecf20Sopenharmony_ci size_t hdr_size = sizeof(struct wmi_link_stats_record); 14818c2ecf20Sopenharmony_ci size_t stats_size, record_size, expected_size; 14828c2ecf20Sopenharmony_ci struct wmi_link_stats_record *hdr; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci if (payload_size < hdr_size) { 14858c2ecf20Sopenharmony_ci wil_err(wil, "link stats wrong event size %zu\n", payload_size); 14868c2ecf20Sopenharmony_ci return; 14878c2ecf20Sopenharmony_ci } 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci while (payload_size >= hdr_size) { 14908c2ecf20Sopenharmony_ci hdr = payload; 14918c2ecf20Sopenharmony_ci stats_size = le16_to_cpu(hdr->record_size); 14928c2ecf20Sopenharmony_ci record_size = hdr_size + stats_size; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci if (payload_size < record_size) { 14958c2ecf20Sopenharmony_ci wil_err(wil, "link stats payload ended unexpectedly, size %zu < %zu\n", 14968c2ecf20Sopenharmony_ci payload_size, record_size); 14978c2ecf20Sopenharmony_ci return; 14988c2ecf20Sopenharmony_ci } 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci switch (hdr->record_type_id) { 15018c2ecf20Sopenharmony_ci case WMI_LINK_STATS_TYPE_BASIC: 15028c2ecf20Sopenharmony_ci expected_size = sizeof(struct wmi_link_stats_basic); 15038c2ecf20Sopenharmony_ci if (stats_size < expected_size) { 15048c2ecf20Sopenharmony_ci wil_err(wil, "link stats invalid basic record size %zu < %zu\n", 15058c2ecf20Sopenharmony_ci stats_size, expected_size); 15068c2ecf20Sopenharmony_ci return; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci if (vif->fw_stats_ready) { 15098c2ecf20Sopenharmony_ci /* clean old statistics */ 15108c2ecf20Sopenharmony_ci vif->fw_stats_tsf = 0; 15118c2ecf20Sopenharmony_ci vif->fw_stats_ready = false; 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci wil_link_stats_store_basic(vif, payload + hdr_size); 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci if (!has_next) { 15178c2ecf20Sopenharmony_ci vif->fw_stats_tsf = tsf; 15188c2ecf20Sopenharmony_ci vif->fw_stats_ready = true; 15198c2ecf20Sopenharmony_ci } 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci break; 15228c2ecf20Sopenharmony_ci case WMI_LINK_STATS_TYPE_GLOBAL: 15238c2ecf20Sopenharmony_ci expected_size = sizeof(struct wmi_link_stats_global); 15248c2ecf20Sopenharmony_ci if (stats_size < sizeof(struct wmi_link_stats_global)) { 15258c2ecf20Sopenharmony_ci wil_err(wil, "link stats invalid global record size %zu < %zu\n", 15268c2ecf20Sopenharmony_ci stats_size, expected_size); 15278c2ecf20Sopenharmony_ci return; 15288c2ecf20Sopenharmony_ci } 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci if (wil->fw_stats_global.ready) { 15318c2ecf20Sopenharmony_ci /* clean old statistics */ 15328c2ecf20Sopenharmony_ci wil->fw_stats_global.tsf = 0; 15338c2ecf20Sopenharmony_ci wil->fw_stats_global.ready = false; 15348c2ecf20Sopenharmony_ci } 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci wil_link_stats_store_global(vif, payload + hdr_size); 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci if (!has_next) { 15398c2ecf20Sopenharmony_ci wil->fw_stats_global.tsf = tsf; 15408c2ecf20Sopenharmony_ci wil->fw_stats_global.ready = true; 15418c2ecf20Sopenharmony_ci } 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci break; 15448c2ecf20Sopenharmony_ci default: 15458c2ecf20Sopenharmony_ci break; 15468c2ecf20Sopenharmony_ci } 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci /* skip to next record */ 15498c2ecf20Sopenharmony_ci payload += record_size; 15508c2ecf20Sopenharmony_ci payload_size -= record_size; 15518c2ecf20Sopenharmony_ci } 15528c2ecf20Sopenharmony_ci} 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_cistatic void 15558c2ecf20Sopenharmony_ciwmi_evt_link_stats(struct wil6210_vif *vif, int id, void *d, int len) 15568c2ecf20Sopenharmony_ci{ 15578c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 15588c2ecf20Sopenharmony_ci struct wmi_link_stats_event *evt = d; 15598c2ecf20Sopenharmony_ci size_t payload_size; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci if (len < offsetof(struct wmi_link_stats_event, payload)) { 15628c2ecf20Sopenharmony_ci wil_err(wil, "stats event way too short %d\n", len); 15638c2ecf20Sopenharmony_ci return; 15648c2ecf20Sopenharmony_ci } 15658c2ecf20Sopenharmony_ci payload_size = le16_to_cpu(evt->payload_size); 15668c2ecf20Sopenharmony_ci if (len < sizeof(struct wmi_link_stats_event) + payload_size) { 15678c2ecf20Sopenharmony_ci wil_err(wil, "stats event too short %d\n", len); 15688c2ecf20Sopenharmony_ci return; 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci wmi_link_stats_parse(vif, le64_to_cpu(evt->tsf), evt->has_next, 15728c2ecf20Sopenharmony_ci evt->payload, payload_size); 15738c2ecf20Sopenharmony_ci} 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci/* find cid and ringid for the station vif 15768c2ecf20Sopenharmony_ci * 15778c2ecf20Sopenharmony_ci * return error, if other interfaces are used or ring was not found 15788c2ecf20Sopenharmony_ci */ 15798c2ecf20Sopenharmony_cistatic int wil_find_cid_ringid_sta(struct wil6210_priv *wil, 15808c2ecf20Sopenharmony_ci struct wil6210_vif *vif, 15818c2ecf20Sopenharmony_ci int *cid, 15828c2ecf20Sopenharmony_ci int *ringid) 15838c2ecf20Sopenharmony_ci{ 15848c2ecf20Sopenharmony_ci struct wil_ring *ring; 15858c2ecf20Sopenharmony_ci struct wil_ring_tx_data *txdata; 15868c2ecf20Sopenharmony_ci int min_ring_id = wil_get_min_tx_ring_id(wil); 15878c2ecf20Sopenharmony_ci int i; 15888c2ecf20Sopenharmony_ci u8 lcid; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci if (!(vif->wdev.iftype == NL80211_IFTYPE_STATION || 15918c2ecf20Sopenharmony_ci vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) { 15928c2ecf20Sopenharmony_ci wil_err(wil, "invalid interface type %d\n", vif->wdev.iftype); 15938c2ecf20Sopenharmony_ci return -EINVAL; 15948c2ecf20Sopenharmony_ci } 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci /* In the STA mode, it is expected to have only one ring 15978c2ecf20Sopenharmony_ci * for the AP we are connected to. 15988c2ecf20Sopenharmony_ci * find it and return the cid associated with it. 15998c2ecf20Sopenharmony_ci */ 16008c2ecf20Sopenharmony_ci for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { 16018c2ecf20Sopenharmony_ci ring = &wil->ring_tx[i]; 16028c2ecf20Sopenharmony_ci txdata = &wil->ring_tx_data[i]; 16038c2ecf20Sopenharmony_ci if (!ring->va || !txdata->enabled || txdata->mid != vif->mid) 16048c2ecf20Sopenharmony_ci continue; 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci lcid = wil->ring2cid_tid[i][0]; 16078c2ecf20Sopenharmony_ci if (lcid >= wil->max_assoc_sta) /* skip BCAST */ 16088c2ecf20Sopenharmony_ci continue; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "find sta -> ringid %d cid %d\n", i, lcid); 16118c2ecf20Sopenharmony_ci *cid = lcid; 16128c2ecf20Sopenharmony_ci *ringid = i; 16138c2ecf20Sopenharmony_ci return 0; 16148c2ecf20Sopenharmony_ci } 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "find sta cid while no rings active?\n"); 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci return -ENOENT; 16198c2ecf20Sopenharmony_ci} 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_cistatic void 16228c2ecf20Sopenharmony_ciwmi_evt_auth_status(struct wil6210_vif *vif, int id, void *d, int len) 16238c2ecf20Sopenharmony_ci{ 16248c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 16258c2ecf20Sopenharmony_ci struct net_device *ndev = vif_to_ndev(vif); 16268c2ecf20Sopenharmony_ci struct wmi_ft_auth_status_event *data = d; 16278c2ecf20Sopenharmony_ci int ie_len = len - offsetof(struct wmi_ft_auth_status_event, ie_info); 16288c2ecf20Sopenharmony_ci int rc, cid = 0, ringid = 0; 16298c2ecf20Sopenharmony_ci struct cfg80211_ft_event_params ft; 16308c2ecf20Sopenharmony_ci u16 d_len; 16318c2ecf20Sopenharmony_ci /* auth_alg(u16) + auth_transaction(u16) + status_code(u16) */ 16328c2ecf20Sopenharmony_ci const size_t auth_ie_offset = sizeof(u16) * 3; 16338c2ecf20Sopenharmony_ci struct auth_no_hdr *auth = (struct auth_no_hdr *)data->ie_info; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci /* check the status */ 16368c2ecf20Sopenharmony_ci if (ie_len >= 0 && data->status != WMI_FW_STATUS_SUCCESS) { 16378c2ecf20Sopenharmony_ci wil_err(wil, "FT: auth failed. status %d\n", data->status); 16388c2ecf20Sopenharmony_ci goto fail; 16398c2ecf20Sopenharmony_ci } 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci if (ie_len < auth_ie_offset) { 16428c2ecf20Sopenharmony_ci wil_err(wil, "FT: auth event too short, len %d\n", len); 16438c2ecf20Sopenharmony_ci goto fail; 16448c2ecf20Sopenharmony_ci } 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci d_len = le16_to_cpu(data->ie_len); 16478c2ecf20Sopenharmony_ci if (d_len != ie_len) { 16488c2ecf20Sopenharmony_ci wil_err(wil, 16498c2ecf20Sopenharmony_ci "FT: auth ie length mismatch, d_len %d should be %d\n", 16508c2ecf20Sopenharmony_ci d_len, ie_len); 16518c2ecf20Sopenharmony_ci goto fail; 16528c2ecf20Sopenharmony_ci } 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci if (!test_bit(wil_vif_ft_roam, wil->status)) { 16558c2ecf20Sopenharmony_ci wil_err(wil, "FT: Not in roaming state\n"); 16568c2ecf20Sopenharmony_ci goto fail; 16578c2ecf20Sopenharmony_ci } 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci if (le16_to_cpu(auth->auth_transaction) != 2) { 16608c2ecf20Sopenharmony_ci wil_err(wil, "FT: auth error. auth_transaction %d\n", 16618c2ecf20Sopenharmony_ci le16_to_cpu(auth->auth_transaction)); 16628c2ecf20Sopenharmony_ci goto fail; 16638c2ecf20Sopenharmony_ci } 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci if (le16_to_cpu(auth->auth_alg) != WLAN_AUTH_FT) { 16668c2ecf20Sopenharmony_ci wil_err(wil, "FT: auth error. auth_alg %d\n", 16678c2ecf20Sopenharmony_ci le16_to_cpu(auth->auth_alg)); 16688c2ecf20Sopenharmony_ci goto fail; 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "FT: Auth to %pM successfully\n", data->mac_addr); 16728c2ecf20Sopenharmony_ci wil_hex_dump_wmi("FT Auth ies : ", DUMP_PREFIX_OFFSET, 16, 1, 16738c2ecf20Sopenharmony_ci data->ie_info, d_len, true); 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci /* find cid and ringid */ 16768c2ecf20Sopenharmony_ci rc = wil_find_cid_ringid_sta(wil, vif, &cid, &ringid); 16778c2ecf20Sopenharmony_ci if (rc) { 16788c2ecf20Sopenharmony_ci wil_err(wil, "No valid cid found\n"); 16798c2ecf20Sopenharmony_ci goto fail; 16808c2ecf20Sopenharmony_ci } 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci if (vif->privacy) { 16838c2ecf20Sopenharmony_ci /* For secure assoc, remove old keys */ 16848c2ecf20Sopenharmony_ci rc = wmi_del_cipher_key(vif, 0, wil->sta[cid].addr, 16858c2ecf20Sopenharmony_ci WMI_KEY_USE_PAIRWISE); 16868c2ecf20Sopenharmony_ci if (rc) { 16878c2ecf20Sopenharmony_ci wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(PTK) failed\n"); 16888c2ecf20Sopenharmony_ci goto fail; 16898c2ecf20Sopenharmony_ci } 16908c2ecf20Sopenharmony_ci rc = wmi_del_cipher_key(vif, 0, wil->sta[cid].addr, 16918c2ecf20Sopenharmony_ci WMI_KEY_USE_RX_GROUP); 16928c2ecf20Sopenharmony_ci if (rc) { 16938c2ecf20Sopenharmony_ci wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(GTK) failed\n"); 16948c2ecf20Sopenharmony_ci goto fail; 16958c2ecf20Sopenharmony_ci } 16968c2ecf20Sopenharmony_ci } 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci memset(&ft, 0, sizeof(ft)); 16998c2ecf20Sopenharmony_ci ft.ies = data->ie_info + auth_ie_offset; 17008c2ecf20Sopenharmony_ci ft.ies_len = d_len - auth_ie_offset; 17018c2ecf20Sopenharmony_ci ft.target_ap = data->mac_addr; 17028c2ecf20Sopenharmony_ci cfg80211_ft_event(ndev, &ft); 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci return; 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_cifail: 17078c2ecf20Sopenharmony_ci wil6210_disconnect(vif, NULL, WLAN_REASON_PREV_AUTH_NOT_VALID); 17088c2ecf20Sopenharmony_ci} 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_cistatic void 17118c2ecf20Sopenharmony_ciwmi_evt_reassoc_status(struct wil6210_vif *vif, int id, void *d, int len) 17128c2ecf20Sopenharmony_ci{ 17138c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 17148c2ecf20Sopenharmony_ci struct net_device *ndev = vif_to_ndev(vif); 17158c2ecf20Sopenharmony_ci struct wiphy *wiphy = wil_to_wiphy(wil); 17168c2ecf20Sopenharmony_ci struct wmi_ft_reassoc_status_event *data = d; 17178c2ecf20Sopenharmony_ci int ies_len = len - offsetof(struct wmi_ft_reassoc_status_event, 17188c2ecf20Sopenharmony_ci ie_info); 17198c2ecf20Sopenharmony_ci int rc = -ENOENT, cid = 0, ringid = 0; 17208c2ecf20Sopenharmony_ci int ch; /* channel number (primary) */ 17218c2ecf20Sopenharmony_ci size_t assoc_req_ie_len = 0, assoc_resp_ie_len = 0; 17228c2ecf20Sopenharmony_ci u8 *assoc_req_ie = NULL, *assoc_resp_ie = NULL; 17238c2ecf20Sopenharmony_ci /* capinfo(u16) + listen_interval(u16) + current_ap mac addr + IEs */ 17248c2ecf20Sopenharmony_ci const size_t assoc_req_ie_offset = sizeof(u16) * 2 + ETH_ALEN; 17258c2ecf20Sopenharmony_ci /* capinfo(u16) + status_code(u16) + associd(u16) + IEs */ 17268c2ecf20Sopenharmony_ci const size_t assoc_resp_ie_offset = sizeof(u16) * 3; 17278c2ecf20Sopenharmony_ci u16 d_len; 17288c2ecf20Sopenharmony_ci int freq; 17298c2ecf20Sopenharmony_ci struct cfg80211_roam_info info; 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci if (ies_len < 0) { 17328c2ecf20Sopenharmony_ci wil_err(wil, "ft reassoc event too short, len %d\n", len); 17338c2ecf20Sopenharmony_ci goto fail; 17348c2ecf20Sopenharmony_ci } 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "Reasoc Status event: status=%d, aid=%d", 17378c2ecf20Sopenharmony_ci data->status, data->aid); 17388c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, " mac_addr=%pM, beacon_ie_len=%d", 17398c2ecf20Sopenharmony_ci data->mac_addr, data->beacon_ie_len); 17408c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, " reassoc_req_ie_len=%d, reassoc_resp_ie_len=%d", 17418c2ecf20Sopenharmony_ci le16_to_cpu(data->reassoc_req_ie_len), 17428c2ecf20Sopenharmony_ci le16_to_cpu(data->reassoc_resp_ie_len)); 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci d_len = le16_to_cpu(data->beacon_ie_len) + 17458c2ecf20Sopenharmony_ci le16_to_cpu(data->reassoc_req_ie_len) + 17468c2ecf20Sopenharmony_ci le16_to_cpu(data->reassoc_resp_ie_len); 17478c2ecf20Sopenharmony_ci if (d_len != ies_len) { 17488c2ecf20Sopenharmony_ci wil_err(wil, 17498c2ecf20Sopenharmony_ci "ft reassoc ie length mismatch, d_len %d should be %d\n", 17508c2ecf20Sopenharmony_ci d_len, ies_len); 17518c2ecf20Sopenharmony_ci goto fail; 17528c2ecf20Sopenharmony_ci } 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci /* check the status */ 17558c2ecf20Sopenharmony_ci if (data->status != WMI_FW_STATUS_SUCCESS) { 17568c2ecf20Sopenharmony_ci wil_err(wil, "ft reassoc failed. status %d\n", data->status); 17578c2ecf20Sopenharmony_ci goto fail; 17588c2ecf20Sopenharmony_ci } 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci /* find cid and ringid */ 17618c2ecf20Sopenharmony_ci rc = wil_find_cid_ringid_sta(wil, vif, &cid, &ringid); 17628c2ecf20Sopenharmony_ci if (rc) { 17638c2ecf20Sopenharmony_ci wil_err(wil, "No valid cid found\n"); 17648c2ecf20Sopenharmony_ci goto fail; 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci ch = data->channel + 1; 17688c2ecf20Sopenharmony_ci wil_info(wil, "FT: Roam %pM channel [%d] cid %d aid %d\n", 17698c2ecf20Sopenharmony_ci data->mac_addr, ch, cid, data->aid); 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci wil_hex_dump_wmi("reassoc AI : ", DUMP_PREFIX_OFFSET, 16, 1, 17728c2ecf20Sopenharmony_ci data->ie_info, len - sizeof(*data), true); 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci /* figure out IE's */ 17758c2ecf20Sopenharmony_ci if (le16_to_cpu(data->reassoc_req_ie_len) > assoc_req_ie_offset) { 17768c2ecf20Sopenharmony_ci assoc_req_ie = &data->ie_info[assoc_req_ie_offset]; 17778c2ecf20Sopenharmony_ci assoc_req_ie_len = le16_to_cpu(data->reassoc_req_ie_len) - 17788c2ecf20Sopenharmony_ci assoc_req_ie_offset; 17798c2ecf20Sopenharmony_ci } 17808c2ecf20Sopenharmony_ci if (le16_to_cpu(data->reassoc_resp_ie_len) <= assoc_resp_ie_offset) { 17818c2ecf20Sopenharmony_ci wil_err(wil, "FT: reassoc resp ie len is too short, len %d\n", 17828c2ecf20Sopenharmony_ci le16_to_cpu(data->reassoc_resp_ie_len)); 17838c2ecf20Sopenharmony_ci goto fail; 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci assoc_resp_ie = &data->ie_info[le16_to_cpu(data->reassoc_req_ie_len) + 17878c2ecf20Sopenharmony_ci assoc_resp_ie_offset]; 17888c2ecf20Sopenharmony_ci assoc_resp_ie_len = le16_to_cpu(data->reassoc_resp_ie_len) - 17898c2ecf20Sopenharmony_ci assoc_resp_ie_offset; 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci if (test_bit(wil_status_resetting, wil->status) || 17928c2ecf20Sopenharmony_ci !test_bit(wil_status_fwready, wil->status)) { 17938c2ecf20Sopenharmony_ci wil_err(wil, "FT: status_resetting, cancel reassoc event\n"); 17948c2ecf20Sopenharmony_ci /* no need for cleanup, wil_reset will do that */ 17958c2ecf20Sopenharmony_ci return; 17968c2ecf20Sopenharmony_ci } 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci mutex_lock(&wil->mutex); 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci /* ring modify to set the ring for the roamed AP settings */ 18018c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, 18028c2ecf20Sopenharmony_ci "ft modify tx config for connection CID %d ring %d\n", 18038c2ecf20Sopenharmony_ci cid, ringid); 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci rc = wil->txrx_ops.tx_ring_modify(vif, ringid, cid, 0); 18068c2ecf20Sopenharmony_ci if (rc) { 18078c2ecf20Sopenharmony_ci wil_err(wil, "modify TX for CID %d MID %d ring %d failed (%d)\n", 18088c2ecf20Sopenharmony_ci cid, vif->mid, ringid, rc); 18098c2ecf20Sopenharmony_ci mutex_unlock(&wil->mutex); 18108c2ecf20Sopenharmony_ci goto fail; 18118c2ecf20Sopenharmony_ci } 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci /* Update the driver STA members with the new bss */ 18148c2ecf20Sopenharmony_ci wil->sta[cid].aid = data->aid; 18158c2ecf20Sopenharmony_ci wil->sta[cid].stats.ft_roams++; 18168c2ecf20Sopenharmony_ci ether_addr_copy(wil->sta[cid].addr, vif->bss->bssid); 18178c2ecf20Sopenharmony_ci mutex_unlock(&wil->mutex); 18188c2ecf20Sopenharmony_ci del_timer_sync(&vif->connect_timer); 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci cfg80211_ref_bss(wiphy, vif->bss); 18218c2ecf20Sopenharmony_ci freq = ieee80211_channel_to_frequency(ch, NL80211_BAND_60GHZ); 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci memset(&info, 0, sizeof(info)); 18248c2ecf20Sopenharmony_ci info.channel = ieee80211_get_channel(wiphy, freq); 18258c2ecf20Sopenharmony_ci info.bss = vif->bss; 18268c2ecf20Sopenharmony_ci info.req_ie = assoc_req_ie; 18278c2ecf20Sopenharmony_ci info.req_ie_len = assoc_req_ie_len; 18288c2ecf20Sopenharmony_ci info.resp_ie = assoc_resp_ie; 18298c2ecf20Sopenharmony_ci info.resp_ie_len = assoc_resp_ie_len; 18308c2ecf20Sopenharmony_ci cfg80211_roamed(ndev, &info, GFP_KERNEL); 18318c2ecf20Sopenharmony_ci vif->bss = NULL; 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci return; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_cifail: 18368c2ecf20Sopenharmony_ci wil6210_disconnect(vif, NULL, WLAN_REASON_PREV_AUTH_NOT_VALID); 18378c2ecf20Sopenharmony_ci} 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_cistatic void 18408c2ecf20Sopenharmony_ciwmi_evt_link_monitor(struct wil6210_vif *vif, int id, void *d, int len) 18418c2ecf20Sopenharmony_ci{ 18428c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 18438c2ecf20Sopenharmony_ci struct net_device *ndev = vif_to_ndev(vif); 18448c2ecf20Sopenharmony_ci struct wmi_link_monitor_event *evt = d; 18458c2ecf20Sopenharmony_ci enum nl80211_cqm_rssi_threshold_event event_type; 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci if (len < sizeof(*evt)) { 18488c2ecf20Sopenharmony_ci wil_err(wil, "link monitor event too short %d\n", len); 18498c2ecf20Sopenharmony_ci return; 18508c2ecf20Sopenharmony_ci } 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "link monitor event, type %d rssi %d (stored %d)\n", 18538c2ecf20Sopenharmony_ci evt->type, evt->rssi_level, wil->cqm_rssi_thold); 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci if (evt->type != WMI_LINK_MONITOR_NOTIF_RSSI_THRESHOLD_EVT) 18568c2ecf20Sopenharmony_ci /* ignore */ 18578c2ecf20Sopenharmony_ci return; 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci event_type = (evt->rssi_level > wil->cqm_rssi_thold ? 18608c2ecf20Sopenharmony_ci NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH : 18618c2ecf20Sopenharmony_ci NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW); 18628c2ecf20Sopenharmony_ci cfg80211_cqm_rssi_notify(ndev, event_type, evt->rssi_level, GFP_KERNEL); 18638c2ecf20Sopenharmony_ci} 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci/* Some events are ignored for purpose; and need not be interpreted as 18668c2ecf20Sopenharmony_ci * "unhandled events" 18678c2ecf20Sopenharmony_ci */ 18688c2ecf20Sopenharmony_cistatic void wmi_evt_ignore(struct wil6210_vif *vif, int id, void *d, int len) 18698c2ecf20Sopenharmony_ci{ 18708c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "Ignore event 0x%04x len %d\n", id, len); 18738c2ecf20Sopenharmony_ci} 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_cistatic const struct { 18768c2ecf20Sopenharmony_ci int eventid; 18778c2ecf20Sopenharmony_ci void (*handler)(struct wil6210_vif *vif, 18788c2ecf20Sopenharmony_ci int eventid, void *data, int data_len); 18798c2ecf20Sopenharmony_ci} wmi_evt_handlers[] = { 18808c2ecf20Sopenharmony_ci {WMI_READY_EVENTID, wmi_evt_ready}, 18818c2ecf20Sopenharmony_ci {WMI_FW_READY_EVENTID, wmi_evt_ignore}, 18828c2ecf20Sopenharmony_ci {WMI_RX_MGMT_PACKET_EVENTID, wmi_evt_rx_mgmt}, 18838c2ecf20Sopenharmony_ci {WMI_TX_MGMT_PACKET_EVENTID, wmi_evt_tx_mgmt}, 18848c2ecf20Sopenharmony_ci {WMI_SCAN_COMPLETE_EVENTID, wmi_evt_scan_complete}, 18858c2ecf20Sopenharmony_ci {WMI_CONNECT_EVENTID, wmi_evt_connect}, 18868c2ecf20Sopenharmony_ci {WMI_DISCONNECT_EVENTID, wmi_evt_disconnect}, 18878c2ecf20Sopenharmony_ci {WMI_EAPOL_RX_EVENTID, wmi_evt_eapol_rx}, 18888c2ecf20Sopenharmony_ci {WMI_BA_STATUS_EVENTID, wmi_evt_ba_status}, 18898c2ecf20Sopenharmony_ci {WMI_RCP_ADDBA_REQ_EVENTID, wmi_evt_addba_rx_req}, 18908c2ecf20Sopenharmony_ci {WMI_DELBA_EVENTID, wmi_evt_delba}, 18918c2ecf20Sopenharmony_ci {WMI_RING_EN_EVENTID, wmi_evt_ring_en}, 18928c2ecf20Sopenharmony_ci {WMI_DATA_PORT_OPEN_EVENTID, wmi_evt_ignore}, 18938c2ecf20Sopenharmony_ci {WMI_SCHED_SCAN_RESULT_EVENTID, wmi_evt_sched_scan_result}, 18948c2ecf20Sopenharmony_ci {WMI_LINK_STATS_EVENTID, wmi_evt_link_stats}, 18958c2ecf20Sopenharmony_ci {WMI_FT_AUTH_STATUS_EVENTID, wmi_evt_auth_status}, 18968c2ecf20Sopenharmony_ci {WMI_FT_REASSOC_STATUS_EVENTID, wmi_evt_reassoc_status}, 18978c2ecf20Sopenharmony_ci {WMI_LINK_MONITOR_EVENTID, wmi_evt_link_monitor}, 18988c2ecf20Sopenharmony_ci}; 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci/* 19018c2ecf20Sopenharmony_ci * Run in IRQ context 19028c2ecf20Sopenharmony_ci * Extract WMI command from mailbox. Queue it to the @wil->pending_wmi_ev 19038c2ecf20Sopenharmony_ci * that will be eventually handled by the @wmi_event_worker in the thread 19048c2ecf20Sopenharmony_ci * context of thread "wil6210_wmi" 19058c2ecf20Sopenharmony_ci */ 19068c2ecf20Sopenharmony_civoid wmi_recv_cmd(struct wil6210_priv *wil) 19078c2ecf20Sopenharmony_ci{ 19088c2ecf20Sopenharmony_ci struct wil6210_mbox_ring_desc d_tail; 19098c2ecf20Sopenharmony_ci struct wil6210_mbox_hdr hdr; 19108c2ecf20Sopenharmony_ci struct wil6210_mbox_ring *r = &wil->mbox_ctl.rx; 19118c2ecf20Sopenharmony_ci struct pending_wmi_event *evt; 19128c2ecf20Sopenharmony_ci u8 *cmd; 19138c2ecf20Sopenharmony_ci void __iomem *src; 19148c2ecf20Sopenharmony_ci ulong flags; 19158c2ecf20Sopenharmony_ci unsigned n; 19168c2ecf20Sopenharmony_ci unsigned int num_immed_reply = 0; 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci if (!test_bit(wil_status_mbox_ready, wil->status)) { 19198c2ecf20Sopenharmony_ci wil_err(wil, "Reset in progress. Cannot handle WMI event\n"); 19208c2ecf20Sopenharmony_ci return; 19218c2ecf20Sopenharmony_ci } 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci if (test_bit(wil_status_suspended, wil->status)) { 19248c2ecf20Sopenharmony_ci wil_err(wil, "suspended. cannot handle WMI event\n"); 19258c2ecf20Sopenharmony_ci return; 19268c2ecf20Sopenharmony_ci } 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci for (n = 0;; n++) { 19298c2ecf20Sopenharmony_ci u16 len; 19308c2ecf20Sopenharmony_ci bool q; 19318c2ecf20Sopenharmony_ci bool immed_reply = false; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci r->head = wil_r(wil, RGF_MBOX + 19348c2ecf20Sopenharmony_ci offsetof(struct wil6210_mbox_ctl, rx.head)); 19358c2ecf20Sopenharmony_ci if (r->tail == r->head) 19368c2ecf20Sopenharmony_ci break; 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "Mbox head %08x tail %08x\n", 19398c2ecf20Sopenharmony_ci r->head, r->tail); 19408c2ecf20Sopenharmony_ci /* read cmd descriptor from tail */ 19418c2ecf20Sopenharmony_ci wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail), 19428c2ecf20Sopenharmony_ci sizeof(struct wil6210_mbox_ring_desc)); 19438c2ecf20Sopenharmony_ci if (d_tail.sync == 0) { 19448c2ecf20Sopenharmony_ci wil_err(wil, "Mbox evt not owned by FW?\n"); 19458c2ecf20Sopenharmony_ci break; 19468c2ecf20Sopenharmony_ci } 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci /* read cmd header from descriptor */ 19498c2ecf20Sopenharmony_ci if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) { 19508c2ecf20Sopenharmony_ci wil_err(wil, "Mbox evt at 0x%08x?\n", 19518c2ecf20Sopenharmony_ci le32_to_cpu(d_tail.addr)); 19528c2ecf20Sopenharmony_ci break; 19538c2ecf20Sopenharmony_ci } 19548c2ecf20Sopenharmony_ci len = le16_to_cpu(hdr.len); 19558c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n", 19568c2ecf20Sopenharmony_ci le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type), 19578c2ecf20Sopenharmony_ci hdr.flags); 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci /* read cmd buffer from descriptor */ 19608c2ecf20Sopenharmony_ci src = wmi_buffer(wil, d_tail.addr) + 19618c2ecf20Sopenharmony_ci sizeof(struct wil6210_mbox_hdr); 19628c2ecf20Sopenharmony_ci evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event, 19638c2ecf20Sopenharmony_ci event.wmi) + len, 4), 19648c2ecf20Sopenharmony_ci GFP_KERNEL); 19658c2ecf20Sopenharmony_ci if (!evt) 19668c2ecf20Sopenharmony_ci break; 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci evt->event.hdr = hdr; 19698c2ecf20Sopenharmony_ci cmd = (void *)&evt->event.wmi; 19708c2ecf20Sopenharmony_ci wil_memcpy_fromio_32(cmd, src, len); 19718c2ecf20Sopenharmony_ci /* mark entry as empty */ 19728c2ecf20Sopenharmony_ci wil_w(wil, r->tail + 19738c2ecf20Sopenharmony_ci offsetof(struct wil6210_mbox_ring_desc, sync), 0); 19748c2ecf20Sopenharmony_ci /* indicate */ 19758c2ecf20Sopenharmony_ci if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && 19768c2ecf20Sopenharmony_ci (len >= sizeof(struct wmi_cmd_hdr))) { 19778c2ecf20Sopenharmony_ci struct wmi_cmd_hdr *wmi = &evt->event.wmi; 19788c2ecf20Sopenharmony_ci u16 id = le16_to_cpu(wmi->command_id); 19798c2ecf20Sopenharmony_ci u8 mid = wmi->mid; 19808c2ecf20Sopenharmony_ci u32 tstamp = le32_to_cpu(wmi->fw_timestamp); 19818c2ecf20Sopenharmony_ci if (test_bit(wil_status_resuming, wil->status)) { 19828c2ecf20Sopenharmony_ci if (id == WMI_TRAFFIC_RESUME_EVENTID) 19838c2ecf20Sopenharmony_ci clear_bit(wil_status_resuming, 19848c2ecf20Sopenharmony_ci wil->status); 19858c2ecf20Sopenharmony_ci else 19868c2ecf20Sopenharmony_ci wil_err(wil, 19878c2ecf20Sopenharmony_ci "WMI evt %d while resuming\n", 19888c2ecf20Sopenharmony_ci id); 19898c2ecf20Sopenharmony_ci } 19908c2ecf20Sopenharmony_ci spin_lock_irqsave(&wil->wmi_ev_lock, flags); 19918c2ecf20Sopenharmony_ci if (wil->reply_id && wil->reply_id == id && 19928c2ecf20Sopenharmony_ci wil->reply_mid == mid) { 19938c2ecf20Sopenharmony_ci if (wil->reply_buf) { 19948c2ecf20Sopenharmony_ci memcpy(wil->reply_buf, wmi, 19958c2ecf20Sopenharmony_ci min(len, wil->reply_size)); 19968c2ecf20Sopenharmony_ci immed_reply = true; 19978c2ecf20Sopenharmony_ci } 19988c2ecf20Sopenharmony_ci if (id == WMI_TRAFFIC_SUSPEND_EVENTID) { 19998c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, 20008c2ecf20Sopenharmony_ci "set suspend_resp_rcvd\n"); 20018c2ecf20Sopenharmony_ci wil->suspend_resp_rcvd = true; 20028c2ecf20Sopenharmony_ci } 20038c2ecf20Sopenharmony_ci } 20048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "recv %s (0x%04x) MID %d @%d msec\n", 20078c2ecf20Sopenharmony_ci eventid2name(id), id, wmi->mid, tstamp); 20088c2ecf20Sopenharmony_ci trace_wil6210_wmi_event(wmi, &wmi[1], 20098c2ecf20Sopenharmony_ci len - sizeof(*wmi)); 20108c2ecf20Sopenharmony_ci } 20118c2ecf20Sopenharmony_ci wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1, 20128c2ecf20Sopenharmony_ci &evt->event.hdr, sizeof(hdr) + len, true); 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci /* advance tail */ 20158c2ecf20Sopenharmony_ci r->tail = r->base + ((r->tail - r->base + 20168c2ecf20Sopenharmony_ci sizeof(struct wil6210_mbox_ring_desc)) % r->size); 20178c2ecf20Sopenharmony_ci wil_w(wil, RGF_MBOX + 20188c2ecf20Sopenharmony_ci offsetof(struct wil6210_mbox_ctl, rx.tail), r->tail); 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci if (immed_reply) { 20218c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "recv_cmd: Complete WMI 0x%04x\n", 20228c2ecf20Sopenharmony_ci wil->reply_id); 20238c2ecf20Sopenharmony_ci kfree(evt); 20248c2ecf20Sopenharmony_ci num_immed_reply++; 20258c2ecf20Sopenharmony_ci complete(&wil->wmi_call); 20268c2ecf20Sopenharmony_ci } else { 20278c2ecf20Sopenharmony_ci /* add to the pending list */ 20288c2ecf20Sopenharmony_ci spin_lock_irqsave(&wil->wmi_ev_lock, flags); 20298c2ecf20Sopenharmony_ci list_add_tail(&evt->list, &wil->pending_wmi_ev); 20308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); 20318c2ecf20Sopenharmony_ci q = queue_work(wil->wmi_wq, &wil->wmi_event_worker); 20328c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "queue_work -> %d\n", q); 20338c2ecf20Sopenharmony_ci } 20348c2ecf20Sopenharmony_ci } 20358c2ecf20Sopenharmony_ci /* normally, 1 event per IRQ should be processed */ 20368c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "recv_cmd: -> %d events queued, %d completed\n", 20378c2ecf20Sopenharmony_ci n - num_immed_reply, num_immed_reply); 20388c2ecf20Sopenharmony_ci} 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ciint wmi_call(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len, 20418c2ecf20Sopenharmony_ci u16 reply_id, void *reply, u16 reply_size, int to_msec) 20428c2ecf20Sopenharmony_ci{ 20438c2ecf20Sopenharmony_ci int rc; 20448c2ecf20Sopenharmony_ci unsigned long remain; 20458c2ecf20Sopenharmony_ci ulong flags; 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci mutex_lock(&wil->wmi_mutex); 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci spin_lock_irqsave(&wil->wmi_ev_lock, flags); 20508c2ecf20Sopenharmony_ci wil->reply_id = reply_id; 20518c2ecf20Sopenharmony_ci wil->reply_mid = mid; 20528c2ecf20Sopenharmony_ci wil->reply_buf = reply; 20538c2ecf20Sopenharmony_ci wil->reply_size = reply_size; 20548c2ecf20Sopenharmony_ci reinit_completion(&wil->wmi_call); 20558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci rc = __wmi_send(wil, cmdid, mid, buf, len); 20588c2ecf20Sopenharmony_ci if (rc) 20598c2ecf20Sopenharmony_ci goto out; 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci remain = wait_for_completion_timeout(&wil->wmi_call, 20628c2ecf20Sopenharmony_ci msecs_to_jiffies(to_msec)); 20638c2ecf20Sopenharmony_ci if (0 == remain) { 20648c2ecf20Sopenharmony_ci wil_err(wil, "wmi_call(0x%04x->0x%04x) timeout %d msec\n", 20658c2ecf20Sopenharmony_ci cmdid, reply_id, to_msec); 20668c2ecf20Sopenharmony_ci rc = -ETIME; 20678c2ecf20Sopenharmony_ci } else { 20688c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, 20698c2ecf20Sopenharmony_ci "wmi_call(0x%04x->0x%04x) completed in %d msec\n", 20708c2ecf20Sopenharmony_ci cmdid, reply_id, 20718c2ecf20Sopenharmony_ci to_msec - jiffies_to_msecs(remain)); 20728c2ecf20Sopenharmony_ci } 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ciout: 20758c2ecf20Sopenharmony_ci spin_lock_irqsave(&wil->wmi_ev_lock, flags); 20768c2ecf20Sopenharmony_ci wil->reply_id = 0; 20778c2ecf20Sopenharmony_ci wil->reply_mid = U8_MAX; 20788c2ecf20Sopenharmony_ci wil->reply_buf = NULL; 20798c2ecf20Sopenharmony_ci wil->reply_size = 0; 20808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci mutex_unlock(&wil->wmi_mutex); 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci return rc; 20858c2ecf20Sopenharmony_ci} 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ciint wmi_echo(struct wil6210_priv *wil) 20888c2ecf20Sopenharmony_ci{ 20898c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 20908c2ecf20Sopenharmony_ci struct wmi_echo_cmd cmd = { 20918c2ecf20Sopenharmony_ci .value = cpu_to_le32(0x12345678), 20928c2ecf20Sopenharmony_ci }; 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci return wmi_call(wil, WMI_ECHO_CMDID, vif->mid, &cmd, sizeof(cmd), 20958c2ecf20Sopenharmony_ci WMI_ECHO_RSP_EVENTID, NULL, 0, 20968c2ecf20Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 20978c2ecf20Sopenharmony_ci} 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ciint wmi_set_mac_address(struct wil6210_priv *wil, void *addr) 21008c2ecf20Sopenharmony_ci{ 21018c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 21028c2ecf20Sopenharmony_ci struct wmi_set_mac_address_cmd cmd; 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci ether_addr_copy(cmd.mac, addr); 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "Set MAC %pM\n", addr); 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, vif->mid, 21098c2ecf20Sopenharmony_ci &cmd, sizeof(cmd)); 21108c2ecf20Sopenharmony_ci} 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ciint wmi_led_cfg(struct wil6210_priv *wil, bool enable) 21138c2ecf20Sopenharmony_ci{ 21148c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 21158c2ecf20Sopenharmony_ci int rc = 0; 21168c2ecf20Sopenharmony_ci struct wmi_led_cfg_cmd cmd = { 21178c2ecf20Sopenharmony_ci .led_mode = enable, 21188c2ecf20Sopenharmony_ci .id = led_id, 21198c2ecf20Sopenharmony_ci .slow_blink_cfg.blink_on = 21208c2ecf20Sopenharmony_ci cpu_to_le32(led_blink_time[WIL_LED_TIME_SLOW].on_ms), 21218c2ecf20Sopenharmony_ci .slow_blink_cfg.blink_off = 21228c2ecf20Sopenharmony_ci cpu_to_le32(led_blink_time[WIL_LED_TIME_SLOW].off_ms), 21238c2ecf20Sopenharmony_ci .medium_blink_cfg.blink_on = 21248c2ecf20Sopenharmony_ci cpu_to_le32(led_blink_time[WIL_LED_TIME_MED].on_ms), 21258c2ecf20Sopenharmony_ci .medium_blink_cfg.blink_off = 21268c2ecf20Sopenharmony_ci cpu_to_le32(led_blink_time[WIL_LED_TIME_MED].off_ms), 21278c2ecf20Sopenharmony_ci .fast_blink_cfg.blink_on = 21288c2ecf20Sopenharmony_ci cpu_to_le32(led_blink_time[WIL_LED_TIME_FAST].on_ms), 21298c2ecf20Sopenharmony_ci .fast_blink_cfg.blink_off = 21308c2ecf20Sopenharmony_ci cpu_to_le32(led_blink_time[WIL_LED_TIME_FAST].off_ms), 21318c2ecf20Sopenharmony_ci .led_polarity = led_polarity, 21328c2ecf20Sopenharmony_ci }; 21338c2ecf20Sopenharmony_ci struct { 21348c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 21358c2ecf20Sopenharmony_ci struct wmi_led_cfg_done_event evt; 21368c2ecf20Sopenharmony_ci } __packed reply = { 21378c2ecf20Sopenharmony_ci .evt = {.status = cpu_to_le32(WMI_FW_STATUS_FAILURE)}, 21388c2ecf20Sopenharmony_ci }; 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_ci if (led_id == WIL_LED_INVALID_ID) 21418c2ecf20Sopenharmony_ci goto out; 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci if (led_id > WIL_LED_MAX_ID) { 21448c2ecf20Sopenharmony_ci wil_err(wil, "Invalid led id %d\n", led_id); 21458c2ecf20Sopenharmony_ci rc = -EINVAL; 21468c2ecf20Sopenharmony_ci goto out; 21478c2ecf20Sopenharmony_ci } 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, 21508c2ecf20Sopenharmony_ci "%s led %d\n", 21518c2ecf20Sopenharmony_ci enable ? "enabling" : "disabling", led_id); 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_LED_CFG_CMDID, vif->mid, &cmd, sizeof(cmd), 21548c2ecf20Sopenharmony_ci WMI_LED_CFG_DONE_EVENTID, &reply, sizeof(reply), 21558c2ecf20Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 21568c2ecf20Sopenharmony_ci if (rc) 21578c2ecf20Sopenharmony_ci goto out; 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci if (reply.evt.status) { 21608c2ecf20Sopenharmony_ci wil_err(wil, "led %d cfg failed with status %d\n", 21618c2ecf20Sopenharmony_ci led_id, le32_to_cpu(reply.evt.status)); 21628c2ecf20Sopenharmony_ci rc = -EINVAL; 21638c2ecf20Sopenharmony_ci } 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ciout: 21668c2ecf20Sopenharmony_ci return rc; 21678c2ecf20Sopenharmony_ci} 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ciint wmi_rbufcap_cfg(struct wil6210_priv *wil, bool enable, u16 threshold) 21708c2ecf20Sopenharmony_ci{ 21718c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 21728c2ecf20Sopenharmony_ci int rc; 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci struct wmi_rbufcap_cfg_cmd cmd = { 21758c2ecf20Sopenharmony_ci .enable = enable, 21768c2ecf20Sopenharmony_ci .rx_desc_threshold = cpu_to_le16(threshold), 21778c2ecf20Sopenharmony_ci }; 21788c2ecf20Sopenharmony_ci struct { 21798c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 21808c2ecf20Sopenharmony_ci struct wmi_rbufcap_cfg_event evt; 21818c2ecf20Sopenharmony_ci } __packed reply = { 21828c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 21838c2ecf20Sopenharmony_ci }; 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_RBUFCAP_CFG_CMDID, vif->mid, &cmd, sizeof(cmd), 21868c2ecf20Sopenharmony_ci WMI_RBUFCAP_CFG_EVENTID, &reply, sizeof(reply), 21878c2ecf20Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 21888c2ecf20Sopenharmony_ci if (rc) 21898c2ecf20Sopenharmony_ci return rc; 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { 21928c2ecf20Sopenharmony_ci wil_err(wil, "RBUFCAP_CFG failed. status %d\n", 21938c2ecf20Sopenharmony_ci reply.evt.status); 21948c2ecf20Sopenharmony_ci rc = -EINVAL; 21958c2ecf20Sopenharmony_ci } 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci return rc; 21988c2ecf20Sopenharmony_ci} 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ciint wmi_pcp_start(struct wil6210_vif *vif, int bi, u8 wmi_nettype, 22018c2ecf20Sopenharmony_ci u8 chan, u8 wmi_edmg_chan, u8 hidden_ssid, u8 is_go) 22028c2ecf20Sopenharmony_ci{ 22038c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 22048c2ecf20Sopenharmony_ci int rc; 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci struct wmi_pcp_start_cmd cmd = { 22078c2ecf20Sopenharmony_ci .bcon_interval = cpu_to_le16(bi), 22088c2ecf20Sopenharmony_ci .network_type = wmi_nettype, 22098c2ecf20Sopenharmony_ci .disable_sec_offload = 1, 22108c2ecf20Sopenharmony_ci .channel = chan - 1, 22118c2ecf20Sopenharmony_ci .edmg_channel = wmi_edmg_chan, 22128c2ecf20Sopenharmony_ci .pcp_max_assoc_sta = wil->max_assoc_sta, 22138c2ecf20Sopenharmony_ci .hidden_ssid = hidden_ssid, 22148c2ecf20Sopenharmony_ci .is_go = is_go, 22158c2ecf20Sopenharmony_ci .ap_sme_offload_mode = disable_ap_sme ? 22168c2ecf20Sopenharmony_ci WMI_AP_SME_OFFLOAD_PARTIAL : 22178c2ecf20Sopenharmony_ci WMI_AP_SME_OFFLOAD_FULL, 22188c2ecf20Sopenharmony_ci .abft_len = wil->abft_len, 22198c2ecf20Sopenharmony_ci }; 22208c2ecf20Sopenharmony_ci struct { 22218c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 22228c2ecf20Sopenharmony_ci struct wmi_pcp_started_event evt; 22238c2ecf20Sopenharmony_ci } __packed reply = { 22248c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 22258c2ecf20Sopenharmony_ci }; 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_ci if (!vif->privacy) 22288c2ecf20Sopenharmony_ci cmd.disable_sec = 1; 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci if ((cmd.pcp_max_assoc_sta > WIL6210_MAX_CID) || 22318c2ecf20Sopenharmony_ci (cmd.pcp_max_assoc_sta <= 0)) { 22328c2ecf20Sopenharmony_ci wil_err(wil, "unexpected max_assoc_sta %d\n", 22338c2ecf20Sopenharmony_ci cmd.pcp_max_assoc_sta); 22348c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 22358c2ecf20Sopenharmony_ci } 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci if (disable_ap_sme && 22388c2ecf20Sopenharmony_ci !test_bit(WMI_FW_CAPABILITY_AP_SME_OFFLOAD_PARTIAL, 22398c2ecf20Sopenharmony_ci wil->fw_capabilities)) { 22408c2ecf20Sopenharmony_ci wil_err(wil, "disable_ap_sme not supported by FW\n"); 22418c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 22428c2ecf20Sopenharmony_ci } 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci /* 22458c2ecf20Sopenharmony_ci * Processing time may be huge, in case of secure AP it takes about 22468c2ecf20Sopenharmony_ci * 3500ms for FW to start AP 22478c2ecf20Sopenharmony_ci */ 22488c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_PCP_START_CMDID, vif->mid, &cmd, sizeof(cmd), 22498c2ecf20Sopenharmony_ci WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 5000); 22508c2ecf20Sopenharmony_ci if (rc) 22518c2ecf20Sopenharmony_ci return rc; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci if (reply.evt.status != WMI_FW_STATUS_SUCCESS) 22548c2ecf20Sopenharmony_ci rc = -EINVAL; 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci if (wmi_nettype != WMI_NETTYPE_P2P) 22578c2ecf20Sopenharmony_ci /* Don't fail due to error in the led configuration */ 22588c2ecf20Sopenharmony_ci wmi_led_cfg(wil, true); 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_ci return rc; 22618c2ecf20Sopenharmony_ci} 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ciint wmi_pcp_stop(struct wil6210_vif *vif) 22648c2ecf20Sopenharmony_ci{ 22658c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 22668c2ecf20Sopenharmony_ci int rc; 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci rc = wmi_led_cfg(wil, false); 22698c2ecf20Sopenharmony_ci if (rc) 22708c2ecf20Sopenharmony_ci return rc; 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ci return wmi_call(wil, WMI_PCP_STOP_CMDID, vif->mid, NULL, 0, 22738c2ecf20Sopenharmony_ci WMI_PCP_STOPPED_EVENTID, NULL, 0, 22748c2ecf20Sopenharmony_ci WIL_WMI_PCP_STOP_TO_MS); 22758c2ecf20Sopenharmony_ci} 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ciint wmi_set_ssid(struct wil6210_vif *vif, u8 ssid_len, const void *ssid) 22788c2ecf20Sopenharmony_ci{ 22798c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 22808c2ecf20Sopenharmony_ci struct wmi_set_ssid_cmd cmd = { 22818c2ecf20Sopenharmony_ci .ssid_len = cpu_to_le32(ssid_len), 22828c2ecf20Sopenharmony_ci }; 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci if (ssid_len > sizeof(cmd.ssid)) 22858c2ecf20Sopenharmony_ci return -EINVAL; 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ci memcpy(cmd.ssid, ssid, ssid_len); 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ci return wmi_send(wil, WMI_SET_SSID_CMDID, vif->mid, &cmd, sizeof(cmd)); 22908c2ecf20Sopenharmony_ci} 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ciint wmi_get_ssid(struct wil6210_vif *vif, u8 *ssid_len, void *ssid) 22938c2ecf20Sopenharmony_ci{ 22948c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 22958c2ecf20Sopenharmony_ci int rc; 22968c2ecf20Sopenharmony_ci struct { 22978c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 22988c2ecf20Sopenharmony_ci struct wmi_set_ssid_cmd cmd; 22998c2ecf20Sopenharmony_ci } __packed reply; 23008c2ecf20Sopenharmony_ci int len; /* reply.cmd.ssid_len in CPU order */ 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci memset(&reply, 0, sizeof(reply)); 23038c2ecf20Sopenharmony_ci 23048c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_GET_SSID_CMDID, vif->mid, NULL, 0, 23058c2ecf20Sopenharmony_ci WMI_GET_SSID_EVENTID, &reply, sizeof(reply), 23068c2ecf20Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 23078c2ecf20Sopenharmony_ci if (rc) 23088c2ecf20Sopenharmony_ci return rc; 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci len = le32_to_cpu(reply.cmd.ssid_len); 23118c2ecf20Sopenharmony_ci if (len > sizeof(reply.cmd.ssid)) 23128c2ecf20Sopenharmony_ci return -EINVAL; 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci *ssid_len = len; 23158c2ecf20Sopenharmony_ci memcpy(ssid, reply.cmd.ssid, len); 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci return 0; 23188c2ecf20Sopenharmony_ci} 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ciint wmi_set_channel(struct wil6210_priv *wil, int channel) 23218c2ecf20Sopenharmony_ci{ 23228c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 23238c2ecf20Sopenharmony_ci struct wmi_set_pcp_channel_cmd cmd = { 23248c2ecf20Sopenharmony_ci .channel = channel - 1, 23258c2ecf20Sopenharmony_ci }; 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci return wmi_send(wil, WMI_SET_PCP_CHANNEL_CMDID, vif->mid, 23288c2ecf20Sopenharmony_ci &cmd, sizeof(cmd)); 23298c2ecf20Sopenharmony_ci} 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ciint wmi_get_channel(struct wil6210_priv *wil, int *channel) 23328c2ecf20Sopenharmony_ci{ 23338c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 23348c2ecf20Sopenharmony_ci int rc; 23358c2ecf20Sopenharmony_ci struct { 23368c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 23378c2ecf20Sopenharmony_ci struct wmi_set_pcp_channel_cmd cmd; 23388c2ecf20Sopenharmony_ci } __packed reply; 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci memset(&reply, 0, sizeof(reply)); 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_GET_PCP_CHANNEL_CMDID, vif->mid, NULL, 0, 23438c2ecf20Sopenharmony_ci WMI_GET_PCP_CHANNEL_EVENTID, &reply, sizeof(reply), 23448c2ecf20Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 23458c2ecf20Sopenharmony_ci if (rc) 23468c2ecf20Sopenharmony_ci return rc; 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci if (reply.cmd.channel > 3) 23498c2ecf20Sopenharmony_ci return -EINVAL; 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_ci *channel = reply.cmd.channel + 1; 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci return 0; 23548c2ecf20Sopenharmony_ci} 23558c2ecf20Sopenharmony_ci 23568c2ecf20Sopenharmony_ciint wmi_p2p_cfg(struct wil6210_vif *vif, int channel, int bi) 23578c2ecf20Sopenharmony_ci{ 23588c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 23598c2ecf20Sopenharmony_ci int rc; 23608c2ecf20Sopenharmony_ci struct wmi_p2p_cfg_cmd cmd = { 23618c2ecf20Sopenharmony_ci .discovery_mode = WMI_DISCOVERY_MODE_PEER2PEER, 23628c2ecf20Sopenharmony_ci .bcon_interval = cpu_to_le16(bi), 23638c2ecf20Sopenharmony_ci .channel = channel - 1, 23648c2ecf20Sopenharmony_ci }; 23658c2ecf20Sopenharmony_ci struct { 23668c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 23678c2ecf20Sopenharmony_ci struct wmi_p2p_cfg_done_event evt; 23688c2ecf20Sopenharmony_ci } __packed reply = { 23698c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 23708c2ecf20Sopenharmony_ci }; 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "sending WMI_P2P_CFG_CMDID\n"); 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_P2P_CFG_CMDID, vif->mid, &cmd, sizeof(cmd), 23758c2ecf20Sopenharmony_ci WMI_P2P_CFG_DONE_EVENTID, &reply, sizeof(reply), 300); 23768c2ecf20Sopenharmony_ci if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) { 23778c2ecf20Sopenharmony_ci wil_err(wil, "P2P_CFG failed. status %d\n", reply.evt.status); 23788c2ecf20Sopenharmony_ci rc = -EINVAL; 23798c2ecf20Sopenharmony_ci } 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci return rc; 23828c2ecf20Sopenharmony_ci} 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ciint wmi_start_listen(struct wil6210_vif *vif) 23858c2ecf20Sopenharmony_ci{ 23868c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 23878c2ecf20Sopenharmony_ci int rc; 23888c2ecf20Sopenharmony_ci struct { 23898c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 23908c2ecf20Sopenharmony_ci struct wmi_listen_started_event evt; 23918c2ecf20Sopenharmony_ci } __packed reply = { 23928c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 23938c2ecf20Sopenharmony_ci }; 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "sending WMI_START_LISTEN_CMDID\n"); 23968c2ecf20Sopenharmony_ci 23978c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_START_LISTEN_CMDID, vif->mid, NULL, 0, 23988c2ecf20Sopenharmony_ci WMI_LISTEN_STARTED_EVENTID, &reply, sizeof(reply), 300); 23998c2ecf20Sopenharmony_ci if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) { 24008c2ecf20Sopenharmony_ci wil_err(wil, "device failed to start listen. status %d\n", 24018c2ecf20Sopenharmony_ci reply.evt.status); 24028c2ecf20Sopenharmony_ci rc = -EINVAL; 24038c2ecf20Sopenharmony_ci } 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci return rc; 24068c2ecf20Sopenharmony_ci} 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ciint wmi_start_search(struct wil6210_vif *vif) 24098c2ecf20Sopenharmony_ci{ 24108c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 24118c2ecf20Sopenharmony_ci int rc; 24128c2ecf20Sopenharmony_ci struct { 24138c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 24148c2ecf20Sopenharmony_ci struct wmi_search_started_event evt; 24158c2ecf20Sopenharmony_ci } __packed reply = { 24168c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 24178c2ecf20Sopenharmony_ci }; 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "sending WMI_START_SEARCH_CMDID\n"); 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_START_SEARCH_CMDID, vif->mid, NULL, 0, 24228c2ecf20Sopenharmony_ci WMI_SEARCH_STARTED_EVENTID, &reply, sizeof(reply), 300); 24238c2ecf20Sopenharmony_ci if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) { 24248c2ecf20Sopenharmony_ci wil_err(wil, "device failed to start search. status %d\n", 24258c2ecf20Sopenharmony_ci reply.evt.status); 24268c2ecf20Sopenharmony_ci rc = -EINVAL; 24278c2ecf20Sopenharmony_ci } 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci return rc; 24308c2ecf20Sopenharmony_ci} 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ciint wmi_stop_discovery(struct wil6210_vif *vif) 24338c2ecf20Sopenharmony_ci{ 24348c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 24358c2ecf20Sopenharmony_ci int rc; 24368c2ecf20Sopenharmony_ci 24378c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "sending WMI_DISCOVERY_STOP_CMDID\n"); 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, vif->mid, NULL, 0, 24408c2ecf20Sopenharmony_ci WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 24418c2ecf20Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ci if (rc) 24448c2ecf20Sopenharmony_ci wil_err(wil, "Failed to stop discovery\n"); 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci return rc; 24478c2ecf20Sopenharmony_ci} 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_ciint wmi_del_cipher_key(struct wil6210_vif *vif, u8 key_index, 24508c2ecf20Sopenharmony_ci const void *mac_addr, int key_usage) 24518c2ecf20Sopenharmony_ci{ 24528c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 24538c2ecf20Sopenharmony_ci struct wmi_delete_cipher_key_cmd cmd = { 24548c2ecf20Sopenharmony_ci .key_index = key_index, 24558c2ecf20Sopenharmony_ci }; 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_ci if (mac_addr) 24588c2ecf20Sopenharmony_ci memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci return wmi_send(wil, WMI_DELETE_CIPHER_KEY_CMDID, vif->mid, 24618c2ecf20Sopenharmony_ci &cmd, sizeof(cmd)); 24628c2ecf20Sopenharmony_ci} 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_ciint wmi_add_cipher_key(struct wil6210_vif *vif, u8 key_index, 24658c2ecf20Sopenharmony_ci const void *mac_addr, int key_len, const void *key, 24668c2ecf20Sopenharmony_ci int key_usage) 24678c2ecf20Sopenharmony_ci{ 24688c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 24698c2ecf20Sopenharmony_ci struct wmi_add_cipher_key_cmd cmd = { 24708c2ecf20Sopenharmony_ci .key_index = key_index, 24718c2ecf20Sopenharmony_ci .key_usage = key_usage, 24728c2ecf20Sopenharmony_ci .key_len = key_len, 24738c2ecf20Sopenharmony_ci }; 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_ci if (key_len > sizeof(cmd.key)) 24768c2ecf20Sopenharmony_ci return -EINVAL; 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_ci /* key len = 0 is allowed only for usage of WMI_KEY_USE_APPLY */ 24798c2ecf20Sopenharmony_ci if ((key_len == 0 || !key) && 24808c2ecf20Sopenharmony_ci key_usage != WMI_KEY_USE_APPLY_PTK) 24818c2ecf20Sopenharmony_ci return -EINVAL; 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci if (key) 24848c2ecf20Sopenharmony_ci memcpy(cmd.key, key, key_len); 24858c2ecf20Sopenharmony_ci 24868c2ecf20Sopenharmony_ci if (mac_addr) 24878c2ecf20Sopenharmony_ci memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_ci return wmi_send(wil, WMI_ADD_CIPHER_KEY_CMDID, vif->mid, 24908c2ecf20Sopenharmony_ci &cmd, sizeof(cmd)); 24918c2ecf20Sopenharmony_ci} 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ciint wmi_set_ie(struct wil6210_vif *vif, u8 type, u16 ie_len, const void *ie) 24948c2ecf20Sopenharmony_ci{ 24958c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 24968c2ecf20Sopenharmony_ci static const char *const names[] = { 24978c2ecf20Sopenharmony_ci [WMI_FRAME_BEACON] = "BEACON", 24988c2ecf20Sopenharmony_ci [WMI_FRAME_PROBE_REQ] = "PROBE_REQ", 24998c2ecf20Sopenharmony_ci [WMI_FRAME_PROBE_RESP] = "WMI_FRAME_PROBE_RESP", 25008c2ecf20Sopenharmony_ci [WMI_FRAME_ASSOC_REQ] = "WMI_FRAME_ASSOC_REQ", 25018c2ecf20Sopenharmony_ci [WMI_FRAME_ASSOC_RESP] = "WMI_FRAME_ASSOC_RESP", 25028c2ecf20Sopenharmony_ci }; 25038c2ecf20Sopenharmony_ci int rc; 25048c2ecf20Sopenharmony_ci u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len; 25058c2ecf20Sopenharmony_ci struct wmi_set_appie_cmd *cmd; 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_ci if (len < ie_len) { 25088c2ecf20Sopenharmony_ci rc = -EINVAL; 25098c2ecf20Sopenharmony_ci goto out; 25108c2ecf20Sopenharmony_ci } 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci cmd = kzalloc(len, GFP_KERNEL); 25138c2ecf20Sopenharmony_ci if (!cmd) { 25148c2ecf20Sopenharmony_ci rc = -ENOMEM; 25158c2ecf20Sopenharmony_ci goto out; 25168c2ecf20Sopenharmony_ci } 25178c2ecf20Sopenharmony_ci if (!ie) 25188c2ecf20Sopenharmony_ci ie_len = 0; 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci cmd->mgmt_frm_type = type; 25218c2ecf20Sopenharmony_ci /* BUG: FW API define ieLen as u8. Will fix FW */ 25228c2ecf20Sopenharmony_ci cmd->ie_len = cpu_to_le16(ie_len); 25238c2ecf20Sopenharmony_ci if (ie_len) 25248c2ecf20Sopenharmony_ci memcpy(cmd->ie_info, ie, ie_len); 25258c2ecf20Sopenharmony_ci rc = wmi_send(wil, WMI_SET_APPIE_CMDID, vif->mid, cmd, len); 25268c2ecf20Sopenharmony_ci kfree(cmd); 25278c2ecf20Sopenharmony_ciout: 25288c2ecf20Sopenharmony_ci if (rc) { 25298c2ecf20Sopenharmony_ci const char *name = type < ARRAY_SIZE(names) ? 25308c2ecf20Sopenharmony_ci names[type] : "??"; 25318c2ecf20Sopenharmony_ci wil_err(wil, "set_ie(%d %s) failed : %d\n", type, name, rc); 25328c2ecf20Sopenharmony_ci } 25338c2ecf20Sopenharmony_ci 25348c2ecf20Sopenharmony_ci return rc; 25358c2ecf20Sopenharmony_ci} 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ciint wmi_update_ft_ies(struct wil6210_vif *vif, u16 ie_len, const void *ie) 25388c2ecf20Sopenharmony_ci{ 25398c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 25408c2ecf20Sopenharmony_ci u16 len; 25418c2ecf20Sopenharmony_ci struct wmi_update_ft_ies_cmd *cmd; 25428c2ecf20Sopenharmony_ci int rc; 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci if (!ie) 25458c2ecf20Sopenharmony_ci ie_len = 0; 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci len = sizeof(struct wmi_update_ft_ies_cmd) + ie_len; 25488c2ecf20Sopenharmony_ci if (len < ie_len) { 25498c2ecf20Sopenharmony_ci wil_err(wil, "wraparound. ie len %d\n", ie_len); 25508c2ecf20Sopenharmony_ci return -EINVAL; 25518c2ecf20Sopenharmony_ci } 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci cmd = kzalloc(len, GFP_KERNEL); 25548c2ecf20Sopenharmony_ci if (!cmd) { 25558c2ecf20Sopenharmony_ci rc = -ENOMEM; 25568c2ecf20Sopenharmony_ci goto out; 25578c2ecf20Sopenharmony_ci } 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci cmd->ie_len = cpu_to_le16(ie_len); 25608c2ecf20Sopenharmony_ci if (ie_len) 25618c2ecf20Sopenharmony_ci memcpy(cmd->ie_info, ie, ie_len); 25628c2ecf20Sopenharmony_ci rc = wmi_send(wil, WMI_UPDATE_FT_IES_CMDID, vif->mid, cmd, len); 25638c2ecf20Sopenharmony_ci kfree(cmd); 25648c2ecf20Sopenharmony_ci 25658c2ecf20Sopenharmony_ciout: 25668c2ecf20Sopenharmony_ci if (rc) 25678c2ecf20Sopenharmony_ci wil_err(wil, "update ft ies failed : %d\n", rc); 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci return rc; 25708c2ecf20Sopenharmony_ci} 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci/** 25738c2ecf20Sopenharmony_ci * wmi_rxon - turn radio on/off 25748c2ecf20Sopenharmony_ci * @wil: driver data 25758c2ecf20Sopenharmony_ci * @on: turn on if true, off otherwise 25768c2ecf20Sopenharmony_ci * 25778c2ecf20Sopenharmony_ci * Only switch radio. Channel should be set separately. 25788c2ecf20Sopenharmony_ci * No timeout for rxon - radio turned on forever unless some other call 25798c2ecf20Sopenharmony_ci * turns it off 25808c2ecf20Sopenharmony_ci */ 25818c2ecf20Sopenharmony_ciint wmi_rxon(struct wil6210_priv *wil, bool on) 25828c2ecf20Sopenharmony_ci{ 25838c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 25848c2ecf20Sopenharmony_ci int rc; 25858c2ecf20Sopenharmony_ci struct { 25868c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 25878c2ecf20Sopenharmony_ci struct wmi_listen_started_event evt; 25888c2ecf20Sopenharmony_ci } __packed reply = { 25898c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 25908c2ecf20Sopenharmony_ci }; 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci wil_info(wil, "(%s)\n", on ? "on" : "off"); 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_ci if (on) { 25958c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_START_LISTEN_CMDID, vif->mid, NULL, 0, 25968c2ecf20Sopenharmony_ci WMI_LISTEN_STARTED_EVENTID, 25978c2ecf20Sopenharmony_ci &reply, sizeof(reply), 25988c2ecf20Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 25998c2ecf20Sopenharmony_ci if ((rc == 0) && (reply.evt.status != WMI_FW_STATUS_SUCCESS)) 26008c2ecf20Sopenharmony_ci rc = -EINVAL; 26018c2ecf20Sopenharmony_ci } else { 26028c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, vif->mid, NULL, 0, 26038c2ecf20Sopenharmony_ci WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 26048c2ecf20Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 26058c2ecf20Sopenharmony_ci } 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_ci return rc; 26088c2ecf20Sopenharmony_ci} 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ciint wmi_rx_chain_add(struct wil6210_priv *wil, struct wil_ring *vring) 26118c2ecf20Sopenharmony_ci{ 26128c2ecf20Sopenharmony_ci struct net_device *ndev = wil->main_ndev; 26138c2ecf20Sopenharmony_ci struct wireless_dev *wdev = ndev->ieee80211_ptr; 26148c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(ndev); 26158c2ecf20Sopenharmony_ci struct wmi_cfg_rx_chain_cmd cmd = { 26168c2ecf20Sopenharmony_ci .action = WMI_RX_CHAIN_ADD, 26178c2ecf20Sopenharmony_ci .rx_sw_ring = { 26188c2ecf20Sopenharmony_ci .max_mpdu_size = cpu_to_le16( 26198c2ecf20Sopenharmony_ci wil_mtu2macbuf(wil->rx_buf_len)), 26208c2ecf20Sopenharmony_ci .ring_mem_base = cpu_to_le64(vring->pa), 26218c2ecf20Sopenharmony_ci .ring_size = cpu_to_le16(vring->size), 26228c2ecf20Sopenharmony_ci }, 26238c2ecf20Sopenharmony_ci .mid = 0, /* TODO - what is it? */ 26248c2ecf20Sopenharmony_ci .decap_trans_type = WMI_DECAP_TYPE_802_3, 26258c2ecf20Sopenharmony_ci .reorder_type = WMI_RX_SW_REORDER, 26268c2ecf20Sopenharmony_ci .host_thrsh = cpu_to_le16(rx_ring_overflow_thrsh), 26278c2ecf20Sopenharmony_ci }; 26288c2ecf20Sopenharmony_ci struct { 26298c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 26308c2ecf20Sopenharmony_ci struct wmi_cfg_rx_chain_done_event evt; 26318c2ecf20Sopenharmony_ci } __packed evt; 26328c2ecf20Sopenharmony_ci int rc; 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_ci memset(&evt, 0, sizeof(evt)); 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ci if (wdev->iftype == NL80211_IFTYPE_MONITOR) { 26378c2ecf20Sopenharmony_ci struct ieee80211_channel *ch = wil->monitor_chandef.chan; 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_ci cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON); 26408c2ecf20Sopenharmony_ci if (ch) 26418c2ecf20Sopenharmony_ci cmd.sniffer_cfg.channel = ch->hw_value - 1; 26428c2ecf20Sopenharmony_ci cmd.sniffer_cfg.phy_info_mode = 26438c2ecf20Sopenharmony_ci cpu_to_le32(WMI_SNIFFER_PHY_INFO_DISABLED); 26448c2ecf20Sopenharmony_ci cmd.sniffer_cfg.phy_support = 26458c2ecf20Sopenharmony_ci cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL) 26468c2ecf20Sopenharmony_ci ? WMI_SNIFFER_CP : WMI_SNIFFER_BOTH_PHYS); 26478c2ecf20Sopenharmony_ci } else { 26488c2ecf20Sopenharmony_ci /* Initialize offload (in non-sniffer mode). 26498c2ecf20Sopenharmony_ci * Linux IP stack always calculates IP checksum 26508c2ecf20Sopenharmony_ci * HW always calculate TCP/UDP checksum 26518c2ecf20Sopenharmony_ci */ 26528c2ecf20Sopenharmony_ci cmd.l3_l4_ctrl |= (1 << L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS); 26538c2ecf20Sopenharmony_ci } 26548c2ecf20Sopenharmony_ci 26558c2ecf20Sopenharmony_ci if (rx_align_2) 26568c2ecf20Sopenharmony_ci cmd.l2_802_3_offload_ctrl |= 26578c2ecf20Sopenharmony_ci L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_MSK; 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_ci /* typical time for secure PCP is 840ms */ 26608c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, vif->mid, &cmd, sizeof(cmd), 26618c2ecf20Sopenharmony_ci WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000); 26628c2ecf20Sopenharmony_ci if (rc) 26638c2ecf20Sopenharmony_ci return rc; 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ci if (le32_to_cpu(evt.evt.status) != WMI_CFG_RX_CHAIN_SUCCESS) 26668c2ecf20Sopenharmony_ci rc = -EINVAL; 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_ci vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr); 26698c2ecf20Sopenharmony_ci 26708c2ecf20Sopenharmony_ci wil_dbg_misc(wil, "Rx init: status %d tail 0x%08x\n", 26718c2ecf20Sopenharmony_ci le32_to_cpu(evt.evt.status), vring->hwtail); 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci return rc; 26748c2ecf20Sopenharmony_ci} 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_ciint wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf) 26778c2ecf20Sopenharmony_ci{ 26788c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 26798c2ecf20Sopenharmony_ci int rc; 26808c2ecf20Sopenharmony_ci struct wmi_temp_sense_cmd cmd = { 26818c2ecf20Sopenharmony_ci .measure_baseband_en = cpu_to_le32(!!t_bb), 26828c2ecf20Sopenharmony_ci .measure_rf_en = cpu_to_le32(!!t_rf), 26838c2ecf20Sopenharmony_ci .measure_mode = cpu_to_le32(TEMPERATURE_MEASURE_NOW), 26848c2ecf20Sopenharmony_ci }; 26858c2ecf20Sopenharmony_ci struct { 26868c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 26878c2ecf20Sopenharmony_ci struct wmi_temp_sense_done_event evt; 26888c2ecf20Sopenharmony_ci } __packed reply; 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci memset(&reply, 0, sizeof(reply)); 26918c2ecf20Sopenharmony_ci 26928c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_TEMP_SENSE_CMDID, vif->mid, &cmd, sizeof(cmd), 26938c2ecf20Sopenharmony_ci WMI_TEMP_SENSE_DONE_EVENTID, &reply, sizeof(reply), 26948c2ecf20Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 26958c2ecf20Sopenharmony_ci if (rc) 26968c2ecf20Sopenharmony_ci return rc; 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci if (t_bb) 26998c2ecf20Sopenharmony_ci *t_bb = le32_to_cpu(reply.evt.baseband_t1000); 27008c2ecf20Sopenharmony_ci if (t_rf) 27018c2ecf20Sopenharmony_ci *t_rf = le32_to_cpu(reply.evt.rf_t1000); 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_ci return 0; 27048c2ecf20Sopenharmony_ci} 27058c2ecf20Sopenharmony_ci 27068c2ecf20Sopenharmony_ciint wmi_get_all_temperatures(struct wil6210_priv *wil, 27078c2ecf20Sopenharmony_ci struct wmi_temp_sense_all_done_event 27088c2ecf20Sopenharmony_ci *sense_all_evt) 27098c2ecf20Sopenharmony_ci{ 27108c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 27118c2ecf20Sopenharmony_ci int rc; 27128c2ecf20Sopenharmony_ci struct wmi_temp_sense_all_cmd cmd = { 27138c2ecf20Sopenharmony_ci .measure_baseband_en = true, 27148c2ecf20Sopenharmony_ci .measure_rf_en = true, 27158c2ecf20Sopenharmony_ci .measure_mode = TEMPERATURE_MEASURE_NOW, 27168c2ecf20Sopenharmony_ci }; 27178c2ecf20Sopenharmony_ci struct { 27188c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 27198c2ecf20Sopenharmony_ci struct wmi_temp_sense_all_done_event evt; 27208c2ecf20Sopenharmony_ci } __packed reply; 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci if (!sense_all_evt) { 27238c2ecf20Sopenharmony_ci wil_err(wil, "Invalid sense_all_evt value\n"); 27248c2ecf20Sopenharmony_ci return -EINVAL; 27258c2ecf20Sopenharmony_ci } 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_ci memset(&reply, 0, sizeof(reply)); 27288c2ecf20Sopenharmony_ci reply.evt.status = WMI_FW_STATUS_FAILURE; 27298c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_TEMP_SENSE_ALL_CMDID, vif->mid, &cmd, 27308c2ecf20Sopenharmony_ci sizeof(cmd), WMI_TEMP_SENSE_ALL_DONE_EVENTID, 27318c2ecf20Sopenharmony_ci &reply, sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); 27328c2ecf20Sopenharmony_ci if (rc) 27338c2ecf20Sopenharmony_ci return rc; 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci if (reply.evt.status == WMI_FW_STATUS_FAILURE) { 27368c2ecf20Sopenharmony_ci wil_err(wil, "Failed getting TEMP_SENSE_ALL\n"); 27378c2ecf20Sopenharmony_ci return -EINVAL; 27388c2ecf20Sopenharmony_ci } 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci memcpy(sense_all_evt, &reply.evt, sizeof(reply.evt)); 27418c2ecf20Sopenharmony_ci return 0; 27428c2ecf20Sopenharmony_ci} 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_ciint wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac, u16 reason, 27458c2ecf20Sopenharmony_ci bool del_sta) 27468c2ecf20Sopenharmony_ci{ 27478c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 27488c2ecf20Sopenharmony_ci int rc; 27498c2ecf20Sopenharmony_ci struct wmi_disconnect_sta_cmd disc_sta_cmd = { 27508c2ecf20Sopenharmony_ci .disconnect_reason = cpu_to_le16(reason), 27518c2ecf20Sopenharmony_ci }; 27528c2ecf20Sopenharmony_ci struct wmi_del_sta_cmd del_sta_cmd = { 27538c2ecf20Sopenharmony_ci .disconnect_reason = cpu_to_le16(reason), 27548c2ecf20Sopenharmony_ci }; 27558c2ecf20Sopenharmony_ci struct { 27568c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 27578c2ecf20Sopenharmony_ci struct wmi_disconnect_event evt; 27588c2ecf20Sopenharmony_ci } __packed reply; 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "disconnect_sta: (%pM, reason %d)\n", mac, reason); 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_ci memset(&reply, 0, sizeof(reply)); 27638c2ecf20Sopenharmony_ci vif->locally_generated_disc = true; 27648c2ecf20Sopenharmony_ci if (del_sta) { 27658c2ecf20Sopenharmony_ci ether_addr_copy(del_sta_cmd.dst_mac, mac); 27668c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_DEL_STA_CMDID, vif->mid, &del_sta_cmd, 27678c2ecf20Sopenharmony_ci sizeof(del_sta_cmd), WMI_DISCONNECT_EVENTID, 27688c2ecf20Sopenharmony_ci &reply, sizeof(reply), 1000); 27698c2ecf20Sopenharmony_ci } else { 27708c2ecf20Sopenharmony_ci ether_addr_copy(disc_sta_cmd.dst_mac, mac); 27718c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_DISCONNECT_STA_CMDID, vif->mid, 27728c2ecf20Sopenharmony_ci &disc_sta_cmd, sizeof(disc_sta_cmd), 27738c2ecf20Sopenharmony_ci WMI_DISCONNECT_EVENTID, 27748c2ecf20Sopenharmony_ci &reply, sizeof(reply), 1000); 27758c2ecf20Sopenharmony_ci } 27768c2ecf20Sopenharmony_ci /* failure to disconnect in reasonable time treated as FW error */ 27778c2ecf20Sopenharmony_ci if (rc) { 27788c2ecf20Sopenharmony_ci wil_fw_error_recovery(wil); 27798c2ecf20Sopenharmony_ci return rc; 27808c2ecf20Sopenharmony_ci } 27818c2ecf20Sopenharmony_ci wil->sinfo_gen++; 27828c2ecf20Sopenharmony_ci 27838c2ecf20Sopenharmony_ci return 0; 27848c2ecf20Sopenharmony_ci} 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ciint wmi_addba(struct wil6210_priv *wil, u8 mid, 27878c2ecf20Sopenharmony_ci u8 ringid, u8 size, u16 timeout) 27888c2ecf20Sopenharmony_ci{ 27898c2ecf20Sopenharmony_ci u8 amsdu = wil->use_enhanced_dma_hw && wil->use_rx_hw_reordering && 27908c2ecf20Sopenharmony_ci test_bit(WMI_FW_CAPABILITY_AMSDU, wil->fw_capabilities) && 27918c2ecf20Sopenharmony_ci wil->amsdu_en; 27928c2ecf20Sopenharmony_ci struct wmi_ring_ba_en_cmd cmd = { 27938c2ecf20Sopenharmony_ci .ring_id = ringid, 27948c2ecf20Sopenharmony_ci .agg_max_wsize = size, 27958c2ecf20Sopenharmony_ci .ba_timeout = cpu_to_le16(timeout), 27968c2ecf20Sopenharmony_ci .amsdu = amsdu, 27978c2ecf20Sopenharmony_ci }; 27988c2ecf20Sopenharmony_ci 27998c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "addba: (ring %d size %d timeout %d amsdu %d)\n", 28008c2ecf20Sopenharmony_ci ringid, size, timeout, amsdu); 28018c2ecf20Sopenharmony_ci 28028c2ecf20Sopenharmony_ci return wmi_send(wil, WMI_RING_BA_EN_CMDID, mid, &cmd, sizeof(cmd)); 28038c2ecf20Sopenharmony_ci} 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_ciint wmi_delba_tx(struct wil6210_priv *wil, u8 mid, u8 ringid, u16 reason) 28068c2ecf20Sopenharmony_ci{ 28078c2ecf20Sopenharmony_ci struct wmi_ring_ba_dis_cmd cmd = { 28088c2ecf20Sopenharmony_ci .ring_id = ringid, 28098c2ecf20Sopenharmony_ci .reason = cpu_to_le16(reason), 28108c2ecf20Sopenharmony_ci }; 28118c2ecf20Sopenharmony_ci 28128c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "delba_tx: (ring %d reason %d)\n", ringid, reason); 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_ci return wmi_send(wil, WMI_RING_BA_DIS_CMDID, mid, &cmd, sizeof(cmd)); 28158c2ecf20Sopenharmony_ci} 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_ciint wmi_delba_rx(struct wil6210_priv *wil, u8 mid, u8 cid, u8 tid, u16 reason) 28188c2ecf20Sopenharmony_ci{ 28198c2ecf20Sopenharmony_ci struct wmi_rcp_delba_cmd cmd = { 28208c2ecf20Sopenharmony_ci .reason = cpu_to_le16(reason), 28218c2ecf20Sopenharmony_ci }; 28228c2ecf20Sopenharmony_ci 28238c2ecf20Sopenharmony_ci if (cid >= WIL6210_RX_DESC_MAX_CID) { 28248c2ecf20Sopenharmony_ci cmd.cidxtid = CIDXTID_EXTENDED_CID_TID; 28258c2ecf20Sopenharmony_ci cmd.cid = cid; 28268c2ecf20Sopenharmony_ci cmd.tid = tid; 28278c2ecf20Sopenharmony_ci } else { 28288c2ecf20Sopenharmony_ci cmd.cidxtid = mk_cidxtid(cid, tid); 28298c2ecf20Sopenharmony_ci } 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "delba_rx: (CID %d TID %d reason %d)\n", cid, 28328c2ecf20Sopenharmony_ci tid, reason); 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_ci return wmi_send(wil, WMI_RCP_DELBA_CMDID, mid, &cmd, sizeof(cmd)); 28358c2ecf20Sopenharmony_ci} 28368c2ecf20Sopenharmony_ci 28378c2ecf20Sopenharmony_ciint wmi_addba_rx_resp(struct wil6210_priv *wil, 28388c2ecf20Sopenharmony_ci u8 mid, u8 cid, u8 tid, u8 token, 28398c2ecf20Sopenharmony_ci u16 status, bool amsdu, u16 agg_wsize, u16 timeout) 28408c2ecf20Sopenharmony_ci{ 28418c2ecf20Sopenharmony_ci int rc; 28428c2ecf20Sopenharmony_ci struct wmi_rcp_addba_resp_cmd cmd = { 28438c2ecf20Sopenharmony_ci .dialog_token = token, 28448c2ecf20Sopenharmony_ci .status_code = cpu_to_le16(status), 28458c2ecf20Sopenharmony_ci /* bit 0: A-MSDU supported 28468c2ecf20Sopenharmony_ci * bit 1: policy (controlled by FW) 28478c2ecf20Sopenharmony_ci * bits 2..5: TID 28488c2ecf20Sopenharmony_ci * bits 6..15: buffer size 28498c2ecf20Sopenharmony_ci */ 28508c2ecf20Sopenharmony_ci .ba_param_set = cpu_to_le16((amsdu ? 1 : 0) | (tid << 2) | 28518c2ecf20Sopenharmony_ci (agg_wsize << 6)), 28528c2ecf20Sopenharmony_ci .ba_timeout = cpu_to_le16(timeout), 28538c2ecf20Sopenharmony_ci }; 28548c2ecf20Sopenharmony_ci struct { 28558c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 28568c2ecf20Sopenharmony_ci struct wmi_rcp_addba_resp_sent_event evt; 28578c2ecf20Sopenharmony_ci } __packed reply = { 28588c2ecf20Sopenharmony_ci .evt = {.status = cpu_to_le16(WMI_FW_STATUS_FAILURE)}, 28598c2ecf20Sopenharmony_ci }; 28608c2ecf20Sopenharmony_ci 28618c2ecf20Sopenharmony_ci if (cid >= WIL6210_RX_DESC_MAX_CID) { 28628c2ecf20Sopenharmony_ci cmd.cidxtid = CIDXTID_EXTENDED_CID_TID; 28638c2ecf20Sopenharmony_ci cmd.cid = cid; 28648c2ecf20Sopenharmony_ci cmd.tid = tid; 28658c2ecf20Sopenharmony_ci } else { 28668c2ecf20Sopenharmony_ci cmd.cidxtid = mk_cidxtid(cid, tid); 28678c2ecf20Sopenharmony_ci } 28688c2ecf20Sopenharmony_ci 28698c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, 28708c2ecf20Sopenharmony_ci "ADDBA response for MID %d CID %d TID %d size %d timeout %d status %d AMSDU%s\n", 28718c2ecf20Sopenharmony_ci mid, cid, tid, agg_wsize, 28728c2ecf20Sopenharmony_ci timeout, status, amsdu ? "+" : "-"); 28738c2ecf20Sopenharmony_ci 28748c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_RCP_ADDBA_RESP_CMDID, mid, &cmd, sizeof(cmd), 28758c2ecf20Sopenharmony_ci WMI_RCP_ADDBA_RESP_SENT_EVENTID, &reply, sizeof(reply), 28768c2ecf20Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 28778c2ecf20Sopenharmony_ci if (rc) 28788c2ecf20Sopenharmony_ci return rc; 28798c2ecf20Sopenharmony_ci 28808c2ecf20Sopenharmony_ci if (reply.evt.status) { 28818c2ecf20Sopenharmony_ci wil_err(wil, "ADDBA response failed with status %d\n", 28828c2ecf20Sopenharmony_ci le16_to_cpu(reply.evt.status)); 28838c2ecf20Sopenharmony_ci rc = -EINVAL; 28848c2ecf20Sopenharmony_ci } 28858c2ecf20Sopenharmony_ci 28868c2ecf20Sopenharmony_ci return rc; 28878c2ecf20Sopenharmony_ci} 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_ciint wmi_addba_rx_resp_edma(struct wil6210_priv *wil, u8 mid, u8 cid, u8 tid, 28908c2ecf20Sopenharmony_ci u8 token, u16 status, bool amsdu, u16 agg_wsize, 28918c2ecf20Sopenharmony_ci u16 timeout) 28928c2ecf20Sopenharmony_ci{ 28938c2ecf20Sopenharmony_ci int rc; 28948c2ecf20Sopenharmony_ci struct wmi_rcp_addba_resp_edma_cmd cmd = { 28958c2ecf20Sopenharmony_ci .cid = cid, 28968c2ecf20Sopenharmony_ci .tid = tid, 28978c2ecf20Sopenharmony_ci .dialog_token = token, 28988c2ecf20Sopenharmony_ci .status_code = cpu_to_le16(status), 28998c2ecf20Sopenharmony_ci /* bit 0: A-MSDU supported 29008c2ecf20Sopenharmony_ci * bit 1: policy (controlled by FW) 29018c2ecf20Sopenharmony_ci * bits 2..5: TID 29028c2ecf20Sopenharmony_ci * bits 6..15: buffer size 29038c2ecf20Sopenharmony_ci */ 29048c2ecf20Sopenharmony_ci .ba_param_set = cpu_to_le16((amsdu ? 1 : 0) | (tid << 2) | 29058c2ecf20Sopenharmony_ci (agg_wsize << 6)), 29068c2ecf20Sopenharmony_ci .ba_timeout = cpu_to_le16(timeout), 29078c2ecf20Sopenharmony_ci /* route all the connections to status ring 0 */ 29088c2ecf20Sopenharmony_ci .status_ring_id = WIL_DEFAULT_RX_STATUS_RING_ID, 29098c2ecf20Sopenharmony_ci }; 29108c2ecf20Sopenharmony_ci struct { 29118c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 29128c2ecf20Sopenharmony_ci struct wmi_rcp_addba_resp_sent_event evt; 29138c2ecf20Sopenharmony_ci } __packed reply = { 29148c2ecf20Sopenharmony_ci .evt = {.status = cpu_to_le16(WMI_FW_STATUS_FAILURE)}, 29158c2ecf20Sopenharmony_ci }; 29168c2ecf20Sopenharmony_ci 29178c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, 29188c2ecf20Sopenharmony_ci "ADDBA response for CID %d TID %d size %d timeout %d status %d AMSDU%s, sring_id %d\n", 29198c2ecf20Sopenharmony_ci cid, tid, agg_wsize, timeout, status, amsdu ? "+" : "-", 29208c2ecf20Sopenharmony_ci WIL_DEFAULT_RX_STATUS_RING_ID); 29218c2ecf20Sopenharmony_ci 29228c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_RCP_ADDBA_RESP_EDMA_CMDID, mid, &cmd, 29238c2ecf20Sopenharmony_ci sizeof(cmd), WMI_RCP_ADDBA_RESP_SENT_EVENTID, &reply, 29248c2ecf20Sopenharmony_ci sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); 29258c2ecf20Sopenharmony_ci if (rc) 29268c2ecf20Sopenharmony_ci return rc; 29278c2ecf20Sopenharmony_ci 29288c2ecf20Sopenharmony_ci if (reply.evt.status) { 29298c2ecf20Sopenharmony_ci wil_err(wil, "ADDBA response failed with status %d\n", 29308c2ecf20Sopenharmony_ci le16_to_cpu(reply.evt.status)); 29318c2ecf20Sopenharmony_ci rc = -EINVAL; 29328c2ecf20Sopenharmony_ci } 29338c2ecf20Sopenharmony_ci 29348c2ecf20Sopenharmony_ci return rc; 29358c2ecf20Sopenharmony_ci} 29368c2ecf20Sopenharmony_ci 29378c2ecf20Sopenharmony_ciint wmi_ps_dev_profile_cfg(struct wil6210_priv *wil, 29388c2ecf20Sopenharmony_ci enum wmi_ps_profile_type ps_profile) 29398c2ecf20Sopenharmony_ci{ 29408c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 29418c2ecf20Sopenharmony_ci int rc; 29428c2ecf20Sopenharmony_ci struct wmi_ps_dev_profile_cfg_cmd cmd = { 29438c2ecf20Sopenharmony_ci .ps_profile = ps_profile, 29448c2ecf20Sopenharmony_ci }; 29458c2ecf20Sopenharmony_ci struct { 29468c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 29478c2ecf20Sopenharmony_ci struct wmi_ps_dev_profile_cfg_event evt; 29488c2ecf20Sopenharmony_ci } __packed reply = { 29498c2ecf20Sopenharmony_ci .evt = {.status = cpu_to_le32(WMI_PS_CFG_CMD_STATUS_ERROR)}, 29508c2ecf20Sopenharmony_ci }; 29518c2ecf20Sopenharmony_ci u32 status; 29528c2ecf20Sopenharmony_ci 29538c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "Setting ps dev profile %d\n", ps_profile); 29548c2ecf20Sopenharmony_ci 29558c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_PS_DEV_PROFILE_CFG_CMDID, vif->mid, 29568c2ecf20Sopenharmony_ci &cmd, sizeof(cmd), 29578c2ecf20Sopenharmony_ci WMI_PS_DEV_PROFILE_CFG_EVENTID, &reply, sizeof(reply), 29588c2ecf20Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 29598c2ecf20Sopenharmony_ci if (rc) 29608c2ecf20Sopenharmony_ci return rc; 29618c2ecf20Sopenharmony_ci 29628c2ecf20Sopenharmony_ci status = le32_to_cpu(reply.evt.status); 29638c2ecf20Sopenharmony_ci 29648c2ecf20Sopenharmony_ci if (status != WMI_PS_CFG_CMD_STATUS_SUCCESS) { 29658c2ecf20Sopenharmony_ci wil_err(wil, "ps dev profile cfg failed with status %d\n", 29668c2ecf20Sopenharmony_ci status); 29678c2ecf20Sopenharmony_ci rc = -EINVAL; 29688c2ecf20Sopenharmony_ci } 29698c2ecf20Sopenharmony_ci 29708c2ecf20Sopenharmony_ci return rc; 29718c2ecf20Sopenharmony_ci} 29728c2ecf20Sopenharmony_ci 29738c2ecf20Sopenharmony_ciint wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short) 29748c2ecf20Sopenharmony_ci{ 29758c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 29768c2ecf20Sopenharmony_ci int rc; 29778c2ecf20Sopenharmony_ci struct wmi_set_mgmt_retry_limit_cmd cmd = { 29788c2ecf20Sopenharmony_ci .mgmt_retry_limit = retry_short, 29798c2ecf20Sopenharmony_ci }; 29808c2ecf20Sopenharmony_ci struct { 29818c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 29828c2ecf20Sopenharmony_ci struct wmi_set_mgmt_retry_limit_event evt; 29838c2ecf20Sopenharmony_ci } __packed reply = { 29848c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 29858c2ecf20Sopenharmony_ci }; 29868c2ecf20Sopenharmony_ci 29878c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "Setting mgmt retry short %d\n", retry_short); 29888c2ecf20Sopenharmony_ci 29898c2ecf20Sopenharmony_ci if (!test_bit(WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT, wil->fw_capabilities)) 29908c2ecf20Sopenharmony_ci return -ENOTSUPP; 29918c2ecf20Sopenharmony_ci 29928c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_SET_MGMT_RETRY_LIMIT_CMDID, vif->mid, 29938c2ecf20Sopenharmony_ci &cmd, sizeof(cmd), 29948c2ecf20Sopenharmony_ci WMI_SET_MGMT_RETRY_LIMIT_EVENTID, &reply, sizeof(reply), 29958c2ecf20Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 29968c2ecf20Sopenharmony_ci if (rc) 29978c2ecf20Sopenharmony_ci return rc; 29988c2ecf20Sopenharmony_ci 29998c2ecf20Sopenharmony_ci if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { 30008c2ecf20Sopenharmony_ci wil_err(wil, "set mgmt retry limit failed with status %d\n", 30018c2ecf20Sopenharmony_ci reply.evt.status); 30028c2ecf20Sopenharmony_ci rc = -EINVAL; 30038c2ecf20Sopenharmony_ci } 30048c2ecf20Sopenharmony_ci 30058c2ecf20Sopenharmony_ci return rc; 30068c2ecf20Sopenharmony_ci} 30078c2ecf20Sopenharmony_ci 30088c2ecf20Sopenharmony_ciint wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short) 30098c2ecf20Sopenharmony_ci{ 30108c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 30118c2ecf20Sopenharmony_ci int rc; 30128c2ecf20Sopenharmony_ci struct { 30138c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 30148c2ecf20Sopenharmony_ci struct wmi_get_mgmt_retry_limit_event evt; 30158c2ecf20Sopenharmony_ci } __packed reply; 30168c2ecf20Sopenharmony_ci 30178c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "getting mgmt retry short\n"); 30188c2ecf20Sopenharmony_ci 30198c2ecf20Sopenharmony_ci if (!test_bit(WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT, wil->fw_capabilities)) 30208c2ecf20Sopenharmony_ci return -ENOTSUPP; 30218c2ecf20Sopenharmony_ci 30228c2ecf20Sopenharmony_ci memset(&reply, 0, sizeof(reply)); 30238c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_GET_MGMT_RETRY_LIMIT_CMDID, vif->mid, NULL, 0, 30248c2ecf20Sopenharmony_ci WMI_GET_MGMT_RETRY_LIMIT_EVENTID, &reply, sizeof(reply), 30258c2ecf20Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 30268c2ecf20Sopenharmony_ci if (rc) 30278c2ecf20Sopenharmony_ci return rc; 30288c2ecf20Sopenharmony_ci 30298c2ecf20Sopenharmony_ci if (retry_short) 30308c2ecf20Sopenharmony_ci *retry_short = reply.evt.mgmt_retry_limit; 30318c2ecf20Sopenharmony_ci 30328c2ecf20Sopenharmony_ci return 0; 30338c2ecf20Sopenharmony_ci} 30348c2ecf20Sopenharmony_ci 30358c2ecf20Sopenharmony_ciint wmi_abort_scan(struct wil6210_vif *vif) 30368c2ecf20Sopenharmony_ci{ 30378c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 30388c2ecf20Sopenharmony_ci int rc; 30398c2ecf20Sopenharmony_ci 30408c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "sending WMI_ABORT_SCAN_CMDID\n"); 30418c2ecf20Sopenharmony_ci 30428c2ecf20Sopenharmony_ci rc = wmi_send(wil, WMI_ABORT_SCAN_CMDID, vif->mid, NULL, 0); 30438c2ecf20Sopenharmony_ci if (rc) 30448c2ecf20Sopenharmony_ci wil_err(wil, "Failed to abort scan (%d)\n", rc); 30458c2ecf20Sopenharmony_ci 30468c2ecf20Sopenharmony_ci return rc; 30478c2ecf20Sopenharmony_ci} 30488c2ecf20Sopenharmony_ci 30498c2ecf20Sopenharmony_ciint wmi_new_sta(struct wil6210_vif *vif, const u8 *mac, u8 aid) 30508c2ecf20Sopenharmony_ci{ 30518c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 30528c2ecf20Sopenharmony_ci int rc; 30538c2ecf20Sopenharmony_ci struct wmi_new_sta_cmd cmd = { 30548c2ecf20Sopenharmony_ci .aid = aid, 30558c2ecf20Sopenharmony_ci }; 30568c2ecf20Sopenharmony_ci 30578c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "new sta %pM, aid %d\n", mac, aid); 30588c2ecf20Sopenharmony_ci 30598c2ecf20Sopenharmony_ci ether_addr_copy(cmd.dst_mac, mac); 30608c2ecf20Sopenharmony_ci 30618c2ecf20Sopenharmony_ci rc = wmi_send(wil, WMI_NEW_STA_CMDID, vif->mid, &cmd, sizeof(cmd)); 30628c2ecf20Sopenharmony_ci if (rc) 30638c2ecf20Sopenharmony_ci wil_err(wil, "Failed to send new sta (%d)\n", rc); 30648c2ecf20Sopenharmony_ci 30658c2ecf20Sopenharmony_ci return rc; 30668c2ecf20Sopenharmony_ci} 30678c2ecf20Sopenharmony_ci 30688c2ecf20Sopenharmony_civoid wmi_event_flush(struct wil6210_priv *wil) 30698c2ecf20Sopenharmony_ci{ 30708c2ecf20Sopenharmony_ci ulong flags; 30718c2ecf20Sopenharmony_ci struct pending_wmi_event *evt, *t; 30728c2ecf20Sopenharmony_ci 30738c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "event_flush\n"); 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_ci spin_lock_irqsave(&wil->wmi_ev_lock, flags); 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) { 30788c2ecf20Sopenharmony_ci list_del(&evt->list); 30798c2ecf20Sopenharmony_ci kfree(evt); 30808c2ecf20Sopenharmony_ci } 30818c2ecf20Sopenharmony_ci 30828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); 30838c2ecf20Sopenharmony_ci} 30848c2ecf20Sopenharmony_ci 30858c2ecf20Sopenharmony_cistatic const char *suspend_status2name(u8 status) 30868c2ecf20Sopenharmony_ci{ 30878c2ecf20Sopenharmony_ci switch (status) { 30888c2ecf20Sopenharmony_ci case WMI_TRAFFIC_SUSPEND_REJECTED_LINK_NOT_IDLE: 30898c2ecf20Sopenharmony_ci return "LINK_NOT_IDLE"; 30908c2ecf20Sopenharmony_ci case WMI_TRAFFIC_SUSPEND_REJECTED_DISCONNECT: 30918c2ecf20Sopenharmony_ci return "DISCONNECT"; 30928c2ecf20Sopenharmony_ci case WMI_TRAFFIC_SUSPEND_REJECTED_OTHER: 30938c2ecf20Sopenharmony_ci return "OTHER"; 30948c2ecf20Sopenharmony_ci default: 30958c2ecf20Sopenharmony_ci return "Untracked status"; 30968c2ecf20Sopenharmony_ci } 30978c2ecf20Sopenharmony_ci} 30988c2ecf20Sopenharmony_ci 30998c2ecf20Sopenharmony_ciint wmi_suspend(struct wil6210_priv *wil) 31008c2ecf20Sopenharmony_ci{ 31018c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 31028c2ecf20Sopenharmony_ci int rc; 31038c2ecf20Sopenharmony_ci struct wmi_traffic_suspend_cmd cmd = { 31048c2ecf20Sopenharmony_ci .wakeup_trigger = wil->wakeup_trigger, 31058c2ecf20Sopenharmony_ci }; 31068c2ecf20Sopenharmony_ci struct { 31078c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 31088c2ecf20Sopenharmony_ci struct wmi_traffic_suspend_event evt; 31098c2ecf20Sopenharmony_ci } __packed reply = { 31108c2ecf20Sopenharmony_ci .evt = {.status = WMI_TRAFFIC_SUSPEND_REJECTED_LINK_NOT_IDLE}, 31118c2ecf20Sopenharmony_ci }; 31128c2ecf20Sopenharmony_ci 31138c2ecf20Sopenharmony_ci u32 suspend_to = WIL_WAIT_FOR_SUSPEND_RESUME_COMP; 31148c2ecf20Sopenharmony_ci 31158c2ecf20Sopenharmony_ci wil->suspend_resp_rcvd = false; 31168c2ecf20Sopenharmony_ci wil->suspend_resp_comp = false; 31178c2ecf20Sopenharmony_ci 31188c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_TRAFFIC_SUSPEND_CMDID, vif->mid, 31198c2ecf20Sopenharmony_ci &cmd, sizeof(cmd), 31208c2ecf20Sopenharmony_ci WMI_TRAFFIC_SUSPEND_EVENTID, &reply, sizeof(reply), 31218c2ecf20Sopenharmony_ci suspend_to); 31228c2ecf20Sopenharmony_ci if (rc) { 31238c2ecf20Sopenharmony_ci wil_err(wil, "wmi_call for suspend req failed, rc=%d\n", rc); 31248c2ecf20Sopenharmony_ci if (rc == -ETIME) 31258c2ecf20Sopenharmony_ci /* wmi_call TO */ 31268c2ecf20Sopenharmony_ci wil->suspend_stats.rejected_by_device++; 31278c2ecf20Sopenharmony_ci else 31288c2ecf20Sopenharmony_ci wil->suspend_stats.rejected_by_host++; 31298c2ecf20Sopenharmony_ci goto out; 31308c2ecf20Sopenharmony_ci } 31318c2ecf20Sopenharmony_ci 31328c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "waiting for suspend_response_completed\n"); 31338c2ecf20Sopenharmony_ci 31348c2ecf20Sopenharmony_ci rc = wait_event_interruptible_timeout(wil->wq, 31358c2ecf20Sopenharmony_ci wil->suspend_resp_comp, 31368c2ecf20Sopenharmony_ci msecs_to_jiffies(suspend_to)); 31378c2ecf20Sopenharmony_ci if (rc == 0) { 31388c2ecf20Sopenharmony_ci wil_err(wil, "TO waiting for suspend_response_completed\n"); 31398c2ecf20Sopenharmony_ci if (wil->suspend_resp_rcvd) 31408c2ecf20Sopenharmony_ci /* Device responded but we TO due to another reason */ 31418c2ecf20Sopenharmony_ci wil->suspend_stats.rejected_by_host++; 31428c2ecf20Sopenharmony_ci else 31438c2ecf20Sopenharmony_ci wil->suspend_stats.rejected_by_device++; 31448c2ecf20Sopenharmony_ci rc = -EBUSY; 31458c2ecf20Sopenharmony_ci goto out; 31468c2ecf20Sopenharmony_ci } 31478c2ecf20Sopenharmony_ci 31488c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "suspend_response_completed rcvd\n"); 31498c2ecf20Sopenharmony_ci if (reply.evt.status != WMI_TRAFFIC_SUSPEND_APPROVED) { 31508c2ecf20Sopenharmony_ci wil_dbg_pm(wil, "device rejected the suspend, %s\n", 31518c2ecf20Sopenharmony_ci suspend_status2name(reply.evt.status)); 31528c2ecf20Sopenharmony_ci wil->suspend_stats.rejected_by_device++; 31538c2ecf20Sopenharmony_ci } 31548c2ecf20Sopenharmony_ci rc = reply.evt.status; 31558c2ecf20Sopenharmony_ci 31568c2ecf20Sopenharmony_ciout: 31578c2ecf20Sopenharmony_ci wil->suspend_resp_rcvd = false; 31588c2ecf20Sopenharmony_ci wil->suspend_resp_comp = false; 31598c2ecf20Sopenharmony_ci 31608c2ecf20Sopenharmony_ci return rc; 31618c2ecf20Sopenharmony_ci} 31628c2ecf20Sopenharmony_ci 31638c2ecf20Sopenharmony_cistatic void resume_triggers2string(u32 triggers, char *string, int str_size) 31648c2ecf20Sopenharmony_ci{ 31658c2ecf20Sopenharmony_ci string[0] = '\0'; 31668c2ecf20Sopenharmony_ci 31678c2ecf20Sopenharmony_ci if (!triggers) { 31688c2ecf20Sopenharmony_ci strlcat(string, " UNKNOWN", str_size); 31698c2ecf20Sopenharmony_ci return; 31708c2ecf20Sopenharmony_ci } 31718c2ecf20Sopenharmony_ci 31728c2ecf20Sopenharmony_ci if (triggers & WMI_RESUME_TRIGGER_HOST) 31738c2ecf20Sopenharmony_ci strlcat(string, " HOST", str_size); 31748c2ecf20Sopenharmony_ci 31758c2ecf20Sopenharmony_ci if (triggers & WMI_RESUME_TRIGGER_UCAST_RX) 31768c2ecf20Sopenharmony_ci strlcat(string, " UCAST_RX", str_size); 31778c2ecf20Sopenharmony_ci 31788c2ecf20Sopenharmony_ci if (triggers & WMI_RESUME_TRIGGER_BCAST_RX) 31798c2ecf20Sopenharmony_ci strlcat(string, " BCAST_RX", str_size); 31808c2ecf20Sopenharmony_ci 31818c2ecf20Sopenharmony_ci if (triggers & WMI_RESUME_TRIGGER_WMI_EVT) 31828c2ecf20Sopenharmony_ci strlcat(string, " WMI_EVT", str_size); 31838c2ecf20Sopenharmony_ci 31848c2ecf20Sopenharmony_ci if (triggers & WMI_RESUME_TRIGGER_DISCONNECT) 31858c2ecf20Sopenharmony_ci strlcat(string, " DISCONNECT", str_size); 31868c2ecf20Sopenharmony_ci} 31878c2ecf20Sopenharmony_ci 31888c2ecf20Sopenharmony_ciint wmi_resume(struct wil6210_priv *wil) 31898c2ecf20Sopenharmony_ci{ 31908c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 31918c2ecf20Sopenharmony_ci int rc; 31928c2ecf20Sopenharmony_ci char string[100]; 31938c2ecf20Sopenharmony_ci struct { 31948c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 31958c2ecf20Sopenharmony_ci struct wmi_traffic_resume_event evt; 31968c2ecf20Sopenharmony_ci } __packed reply = { 31978c2ecf20Sopenharmony_ci .evt = {.status = WMI_TRAFFIC_RESUME_FAILED, 31988c2ecf20Sopenharmony_ci .resume_triggers = 31998c2ecf20Sopenharmony_ci cpu_to_le32(WMI_RESUME_TRIGGER_UNKNOWN)}, 32008c2ecf20Sopenharmony_ci }; 32018c2ecf20Sopenharmony_ci 32028c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_TRAFFIC_RESUME_CMDID, vif->mid, NULL, 0, 32038c2ecf20Sopenharmony_ci WMI_TRAFFIC_RESUME_EVENTID, &reply, sizeof(reply), 32048c2ecf20Sopenharmony_ci WIL_WAIT_FOR_SUSPEND_RESUME_COMP); 32058c2ecf20Sopenharmony_ci if (rc) 32068c2ecf20Sopenharmony_ci return rc; 32078c2ecf20Sopenharmony_ci resume_triggers2string(le32_to_cpu(reply.evt.resume_triggers), string, 32088c2ecf20Sopenharmony_ci sizeof(string)); 32098c2ecf20Sopenharmony_ci wil_dbg_pm(wil, "device resume %s, resume triggers:%s (0x%x)\n", 32108c2ecf20Sopenharmony_ci reply.evt.status ? "failed" : "passed", string, 32118c2ecf20Sopenharmony_ci le32_to_cpu(reply.evt.resume_triggers)); 32128c2ecf20Sopenharmony_ci 32138c2ecf20Sopenharmony_ci return reply.evt.status; 32148c2ecf20Sopenharmony_ci} 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ciint wmi_port_allocate(struct wil6210_priv *wil, u8 mid, 32178c2ecf20Sopenharmony_ci const u8 *mac, enum nl80211_iftype iftype) 32188c2ecf20Sopenharmony_ci{ 32198c2ecf20Sopenharmony_ci int rc; 32208c2ecf20Sopenharmony_ci struct wmi_port_allocate_cmd cmd = { 32218c2ecf20Sopenharmony_ci .mid = mid, 32228c2ecf20Sopenharmony_ci }; 32238c2ecf20Sopenharmony_ci struct { 32248c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 32258c2ecf20Sopenharmony_ci struct wmi_port_allocated_event evt; 32268c2ecf20Sopenharmony_ci } __packed reply = { 32278c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 32288c2ecf20Sopenharmony_ci }; 32298c2ecf20Sopenharmony_ci 32308c2ecf20Sopenharmony_ci wil_dbg_misc(wil, "port allocate, mid %d iftype %d, mac %pM\n", 32318c2ecf20Sopenharmony_ci mid, iftype, mac); 32328c2ecf20Sopenharmony_ci 32338c2ecf20Sopenharmony_ci ether_addr_copy(cmd.mac, mac); 32348c2ecf20Sopenharmony_ci switch (iftype) { 32358c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 32368c2ecf20Sopenharmony_ci cmd.port_role = WMI_PORT_STA; 32378c2ecf20Sopenharmony_ci break; 32388c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 32398c2ecf20Sopenharmony_ci cmd.port_role = WMI_PORT_AP; 32408c2ecf20Sopenharmony_ci break; 32418c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_CLIENT: 32428c2ecf20Sopenharmony_ci cmd.port_role = WMI_PORT_P2P_CLIENT; 32438c2ecf20Sopenharmony_ci break; 32448c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_GO: 32458c2ecf20Sopenharmony_ci cmd.port_role = WMI_PORT_P2P_GO; 32468c2ecf20Sopenharmony_ci break; 32478c2ecf20Sopenharmony_ci /* what about monitor??? */ 32488c2ecf20Sopenharmony_ci default: 32498c2ecf20Sopenharmony_ci wil_err(wil, "unsupported iftype: %d\n", iftype); 32508c2ecf20Sopenharmony_ci return -EINVAL; 32518c2ecf20Sopenharmony_ci } 32528c2ecf20Sopenharmony_ci 32538c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_PORT_ALLOCATE_CMDID, mid, 32548c2ecf20Sopenharmony_ci &cmd, sizeof(cmd), 32558c2ecf20Sopenharmony_ci WMI_PORT_ALLOCATED_EVENTID, &reply, 32568c2ecf20Sopenharmony_ci sizeof(reply), 300); 32578c2ecf20Sopenharmony_ci if (rc) { 32588c2ecf20Sopenharmony_ci wil_err(wil, "failed to allocate port, status %d\n", rc); 32598c2ecf20Sopenharmony_ci return rc; 32608c2ecf20Sopenharmony_ci } 32618c2ecf20Sopenharmony_ci if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { 32628c2ecf20Sopenharmony_ci wil_err(wil, "WMI_PORT_ALLOCATE returned status %d\n", 32638c2ecf20Sopenharmony_ci reply.evt.status); 32648c2ecf20Sopenharmony_ci return -EINVAL; 32658c2ecf20Sopenharmony_ci } 32668c2ecf20Sopenharmony_ci 32678c2ecf20Sopenharmony_ci return 0; 32688c2ecf20Sopenharmony_ci} 32698c2ecf20Sopenharmony_ci 32708c2ecf20Sopenharmony_ciint wmi_port_delete(struct wil6210_priv *wil, u8 mid) 32718c2ecf20Sopenharmony_ci{ 32728c2ecf20Sopenharmony_ci int rc; 32738c2ecf20Sopenharmony_ci struct wmi_port_delete_cmd cmd = { 32748c2ecf20Sopenharmony_ci .mid = mid, 32758c2ecf20Sopenharmony_ci }; 32768c2ecf20Sopenharmony_ci struct { 32778c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 32788c2ecf20Sopenharmony_ci struct wmi_port_deleted_event evt; 32798c2ecf20Sopenharmony_ci } __packed reply = { 32808c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 32818c2ecf20Sopenharmony_ci }; 32828c2ecf20Sopenharmony_ci 32838c2ecf20Sopenharmony_ci wil_dbg_misc(wil, "port delete, mid %d\n", mid); 32848c2ecf20Sopenharmony_ci 32858c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_PORT_DELETE_CMDID, mid, 32868c2ecf20Sopenharmony_ci &cmd, sizeof(cmd), 32878c2ecf20Sopenharmony_ci WMI_PORT_DELETED_EVENTID, &reply, 32888c2ecf20Sopenharmony_ci sizeof(reply), 2000); 32898c2ecf20Sopenharmony_ci if (rc) { 32908c2ecf20Sopenharmony_ci wil_err(wil, "failed to delete port, status %d\n", rc); 32918c2ecf20Sopenharmony_ci return rc; 32928c2ecf20Sopenharmony_ci } 32938c2ecf20Sopenharmony_ci if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { 32948c2ecf20Sopenharmony_ci wil_err(wil, "WMI_PORT_DELETE returned status %d\n", 32958c2ecf20Sopenharmony_ci reply.evt.status); 32968c2ecf20Sopenharmony_ci return -EINVAL; 32978c2ecf20Sopenharmony_ci } 32988c2ecf20Sopenharmony_ci 32998c2ecf20Sopenharmony_ci return 0; 33008c2ecf20Sopenharmony_ci} 33018c2ecf20Sopenharmony_ci 33028c2ecf20Sopenharmony_cistatic bool wmi_evt_call_handler(struct wil6210_vif *vif, int id, 33038c2ecf20Sopenharmony_ci void *d, int len) 33048c2ecf20Sopenharmony_ci{ 33058c2ecf20Sopenharmony_ci uint i; 33068c2ecf20Sopenharmony_ci 33078c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wmi_evt_handlers); i++) { 33088c2ecf20Sopenharmony_ci if (wmi_evt_handlers[i].eventid == id) { 33098c2ecf20Sopenharmony_ci wmi_evt_handlers[i].handler(vif, id, d, len); 33108c2ecf20Sopenharmony_ci return true; 33118c2ecf20Sopenharmony_ci } 33128c2ecf20Sopenharmony_ci } 33138c2ecf20Sopenharmony_ci 33148c2ecf20Sopenharmony_ci return false; 33158c2ecf20Sopenharmony_ci} 33168c2ecf20Sopenharmony_ci 33178c2ecf20Sopenharmony_cistatic void wmi_event_handle(struct wil6210_priv *wil, 33188c2ecf20Sopenharmony_ci struct wil6210_mbox_hdr *hdr) 33198c2ecf20Sopenharmony_ci{ 33208c2ecf20Sopenharmony_ci u16 len = le16_to_cpu(hdr->len); 33218c2ecf20Sopenharmony_ci struct wil6210_vif *vif; 33228c2ecf20Sopenharmony_ci 33238c2ecf20Sopenharmony_ci if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) && 33248c2ecf20Sopenharmony_ci (len >= sizeof(struct wmi_cmd_hdr))) { 33258c2ecf20Sopenharmony_ci struct wmi_cmd_hdr *wmi = (void *)(&hdr[1]); 33268c2ecf20Sopenharmony_ci void *evt_data = (void *)(&wmi[1]); 33278c2ecf20Sopenharmony_ci u16 id = le16_to_cpu(wmi->command_id); 33288c2ecf20Sopenharmony_ci u8 mid = wmi->mid; 33298c2ecf20Sopenharmony_ci 33308c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "Handle %s (0x%04x) (reply_id 0x%04x,%d)\n", 33318c2ecf20Sopenharmony_ci eventid2name(id), id, wil->reply_id, 33328c2ecf20Sopenharmony_ci wil->reply_mid); 33338c2ecf20Sopenharmony_ci 33348c2ecf20Sopenharmony_ci if (mid == MID_BROADCAST) 33358c2ecf20Sopenharmony_ci mid = 0; 33368c2ecf20Sopenharmony_ci if (mid >= GET_MAX_VIFS(wil)) { 33378c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "invalid mid %d, event skipped\n", 33388c2ecf20Sopenharmony_ci mid); 33398c2ecf20Sopenharmony_ci return; 33408c2ecf20Sopenharmony_ci } 33418c2ecf20Sopenharmony_ci vif = wil->vifs[mid]; 33428c2ecf20Sopenharmony_ci if (!vif) { 33438c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "event for empty VIF(%d), skipped\n", 33448c2ecf20Sopenharmony_ci mid); 33458c2ecf20Sopenharmony_ci return; 33468c2ecf20Sopenharmony_ci } 33478c2ecf20Sopenharmony_ci 33488c2ecf20Sopenharmony_ci /* check if someone waits for this event */ 33498c2ecf20Sopenharmony_ci if (wil->reply_id && wil->reply_id == id && 33508c2ecf20Sopenharmony_ci wil->reply_mid == mid) { 33518c2ecf20Sopenharmony_ci if (wil->reply_buf) { 33528c2ecf20Sopenharmony_ci /* event received while wmi_call is waiting 33538c2ecf20Sopenharmony_ci * with a buffer. Such event should be handled 33548c2ecf20Sopenharmony_ci * in wmi_recv_cmd function. Handling the event 33558c2ecf20Sopenharmony_ci * here means a previous wmi_call was timeout. 33568c2ecf20Sopenharmony_ci * Drop the event and do not handle it. 33578c2ecf20Sopenharmony_ci */ 33588c2ecf20Sopenharmony_ci wil_err(wil, 33598c2ecf20Sopenharmony_ci "Old event (%d, %s) while wmi_call is waiting. Drop it and Continue waiting\n", 33608c2ecf20Sopenharmony_ci id, eventid2name(id)); 33618c2ecf20Sopenharmony_ci return; 33628c2ecf20Sopenharmony_ci } 33638c2ecf20Sopenharmony_ci 33648c2ecf20Sopenharmony_ci wmi_evt_call_handler(vif, id, evt_data, 33658c2ecf20Sopenharmony_ci len - sizeof(*wmi)); 33668c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "event_handle: Complete WMI 0x%04x\n", 33678c2ecf20Sopenharmony_ci id); 33688c2ecf20Sopenharmony_ci complete(&wil->wmi_call); 33698c2ecf20Sopenharmony_ci return; 33708c2ecf20Sopenharmony_ci } 33718c2ecf20Sopenharmony_ci /* unsolicited event */ 33728c2ecf20Sopenharmony_ci /* search for handler */ 33738c2ecf20Sopenharmony_ci if (!wmi_evt_call_handler(vif, id, evt_data, 33748c2ecf20Sopenharmony_ci len - sizeof(*wmi))) { 33758c2ecf20Sopenharmony_ci wil_info(wil, "Unhandled event 0x%04x\n", id); 33768c2ecf20Sopenharmony_ci } 33778c2ecf20Sopenharmony_ci } else { 33788c2ecf20Sopenharmony_ci wil_err(wil, "Unknown event type\n"); 33798c2ecf20Sopenharmony_ci print_hex_dump(KERN_ERR, "evt?? ", DUMP_PREFIX_OFFSET, 16, 1, 33808c2ecf20Sopenharmony_ci hdr, sizeof(*hdr) + len, true); 33818c2ecf20Sopenharmony_ci } 33828c2ecf20Sopenharmony_ci} 33838c2ecf20Sopenharmony_ci 33848c2ecf20Sopenharmony_ci/* 33858c2ecf20Sopenharmony_ci * Retrieve next WMI event from the pending list 33868c2ecf20Sopenharmony_ci */ 33878c2ecf20Sopenharmony_cistatic struct list_head *next_wmi_ev(struct wil6210_priv *wil) 33888c2ecf20Sopenharmony_ci{ 33898c2ecf20Sopenharmony_ci ulong flags; 33908c2ecf20Sopenharmony_ci struct list_head *ret = NULL; 33918c2ecf20Sopenharmony_ci 33928c2ecf20Sopenharmony_ci spin_lock_irqsave(&wil->wmi_ev_lock, flags); 33938c2ecf20Sopenharmony_ci 33948c2ecf20Sopenharmony_ci if (!list_empty(&wil->pending_wmi_ev)) { 33958c2ecf20Sopenharmony_ci ret = wil->pending_wmi_ev.next; 33968c2ecf20Sopenharmony_ci list_del(ret); 33978c2ecf20Sopenharmony_ci } 33988c2ecf20Sopenharmony_ci 33998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); 34008c2ecf20Sopenharmony_ci 34018c2ecf20Sopenharmony_ci return ret; 34028c2ecf20Sopenharmony_ci} 34038c2ecf20Sopenharmony_ci 34048c2ecf20Sopenharmony_ci/* 34058c2ecf20Sopenharmony_ci * Handler for the WMI events 34068c2ecf20Sopenharmony_ci */ 34078c2ecf20Sopenharmony_civoid wmi_event_worker(struct work_struct *work) 34088c2ecf20Sopenharmony_ci{ 34098c2ecf20Sopenharmony_ci struct wil6210_priv *wil = container_of(work, struct wil6210_priv, 34108c2ecf20Sopenharmony_ci wmi_event_worker); 34118c2ecf20Sopenharmony_ci struct pending_wmi_event *evt; 34128c2ecf20Sopenharmony_ci struct list_head *lh; 34138c2ecf20Sopenharmony_ci 34148c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "event_worker: Start\n"); 34158c2ecf20Sopenharmony_ci while ((lh = next_wmi_ev(wil)) != NULL) { 34168c2ecf20Sopenharmony_ci evt = list_entry(lh, struct pending_wmi_event, list); 34178c2ecf20Sopenharmony_ci wmi_event_handle(wil, &evt->event.hdr); 34188c2ecf20Sopenharmony_ci kfree(evt); 34198c2ecf20Sopenharmony_ci } 34208c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "event_worker: Finished\n"); 34218c2ecf20Sopenharmony_ci} 34228c2ecf20Sopenharmony_ci 34238c2ecf20Sopenharmony_cibool wil_is_wmi_idle(struct wil6210_priv *wil) 34248c2ecf20Sopenharmony_ci{ 34258c2ecf20Sopenharmony_ci ulong flags; 34268c2ecf20Sopenharmony_ci struct wil6210_mbox_ring *r = &wil->mbox_ctl.rx; 34278c2ecf20Sopenharmony_ci bool rc = false; 34288c2ecf20Sopenharmony_ci 34298c2ecf20Sopenharmony_ci spin_lock_irqsave(&wil->wmi_ev_lock, flags); 34308c2ecf20Sopenharmony_ci 34318c2ecf20Sopenharmony_ci /* Check if there are pending WMI events in the events queue */ 34328c2ecf20Sopenharmony_ci if (!list_empty(&wil->pending_wmi_ev)) { 34338c2ecf20Sopenharmony_ci wil_dbg_pm(wil, "Pending WMI events in queue\n"); 34348c2ecf20Sopenharmony_ci goto out; 34358c2ecf20Sopenharmony_ci } 34368c2ecf20Sopenharmony_ci 34378c2ecf20Sopenharmony_ci /* Check if there is a pending WMI call */ 34388c2ecf20Sopenharmony_ci if (wil->reply_id) { 34398c2ecf20Sopenharmony_ci wil_dbg_pm(wil, "Pending WMI call\n"); 34408c2ecf20Sopenharmony_ci goto out; 34418c2ecf20Sopenharmony_ci } 34428c2ecf20Sopenharmony_ci 34438c2ecf20Sopenharmony_ci /* Check if there are pending RX events in mbox */ 34448c2ecf20Sopenharmony_ci r->head = wil_r(wil, RGF_MBOX + 34458c2ecf20Sopenharmony_ci offsetof(struct wil6210_mbox_ctl, rx.head)); 34468c2ecf20Sopenharmony_ci if (r->tail != r->head) 34478c2ecf20Sopenharmony_ci wil_dbg_pm(wil, "Pending WMI mbox events\n"); 34488c2ecf20Sopenharmony_ci else 34498c2ecf20Sopenharmony_ci rc = true; 34508c2ecf20Sopenharmony_ci 34518c2ecf20Sopenharmony_ciout: 34528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); 34538c2ecf20Sopenharmony_ci return rc; 34548c2ecf20Sopenharmony_ci} 34558c2ecf20Sopenharmony_ci 34568c2ecf20Sopenharmony_cistatic void 34578c2ecf20Sopenharmony_ciwmi_sched_scan_set_ssids(struct wil6210_priv *wil, 34588c2ecf20Sopenharmony_ci struct wmi_start_sched_scan_cmd *cmd, 34598c2ecf20Sopenharmony_ci struct cfg80211_ssid *ssids, int n_ssids, 34608c2ecf20Sopenharmony_ci struct cfg80211_match_set *match_sets, 34618c2ecf20Sopenharmony_ci int n_match_sets) 34628c2ecf20Sopenharmony_ci{ 34638c2ecf20Sopenharmony_ci int i; 34648c2ecf20Sopenharmony_ci 34658c2ecf20Sopenharmony_ci if (n_match_sets > WMI_MAX_PNO_SSID_NUM) { 34668c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "too many match sets (%d), use first %d\n", 34678c2ecf20Sopenharmony_ci n_match_sets, WMI_MAX_PNO_SSID_NUM); 34688c2ecf20Sopenharmony_ci n_match_sets = WMI_MAX_PNO_SSID_NUM; 34698c2ecf20Sopenharmony_ci } 34708c2ecf20Sopenharmony_ci cmd->num_of_ssids = n_match_sets; 34718c2ecf20Sopenharmony_ci 34728c2ecf20Sopenharmony_ci for (i = 0; i < n_match_sets; i++) { 34738c2ecf20Sopenharmony_ci struct wmi_sched_scan_ssid_match *wmi_match = 34748c2ecf20Sopenharmony_ci &cmd->ssid_for_match[i]; 34758c2ecf20Sopenharmony_ci struct cfg80211_match_set *cfg_match = &match_sets[i]; 34768c2ecf20Sopenharmony_ci int j; 34778c2ecf20Sopenharmony_ci 34788c2ecf20Sopenharmony_ci wmi_match->ssid_len = cfg_match->ssid.ssid_len; 34798c2ecf20Sopenharmony_ci memcpy(wmi_match->ssid, cfg_match->ssid.ssid, 34808c2ecf20Sopenharmony_ci min_t(u8, wmi_match->ssid_len, WMI_MAX_SSID_LEN)); 34818c2ecf20Sopenharmony_ci wmi_match->rssi_threshold = S8_MIN; 34828c2ecf20Sopenharmony_ci if (cfg_match->rssi_thold >= S8_MIN && 34838c2ecf20Sopenharmony_ci cfg_match->rssi_thold <= S8_MAX) 34848c2ecf20Sopenharmony_ci wmi_match->rssi_threshold = cfg_match->rssi_thold; 34858c2ecf20Sopenharmony_ci 34868c2ecf20Sopenharmony_ci for (j = 0; j < n_ssids; j++) 34878c2ecf20Sopenharmony_ci if (wmi_match->ssid_len == ssids[j].ssid_len && 34888c2ecf20Sopenharmony_ci memcmp(wmi_match->ssid, ssids[j].ssid, 34898c2ecf20Sopenharmony_ci wmi_match->ssid_len) == 0) 34908c2ecf20Sopenharmony_ci wmi_match->add_ssid_to_probe = true; 34918c2ecf20Sopenharmony_ci } 34928c2ecf20Sopenharmony_ci} 34938c2ecf20Sopenharmony_ci 34948c2ecf20Sopenharmony_cistatic void 34958c2ecf20Sopenharmony_ciwmi_sched_scan_set_channels(struct wil6210_priv *wil, 34968c2ecf20Sopenharmony_ci struct wmi_start_sched_scan_cmd *cmd, 34978c2ecf20Sopenharmony_ci u32 n_channels, 34988c2ecf20Sopenharmony_ci struct ieee80211_channel **channels) 34998c2ecf20Sopenharmony_ci{ 35008c2ecf20Sopenharmony_ci int i; 35018c2ecf20Sopenharmony_ci 35028c2ecf20Sopenharmony_ci if (n_channels > WMI_MAX_CHANNEL_NUM) { 35038c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "too many channels (%d), use first %d\n", 35048c2ecf20Sopenharmony_ci n_channels, WMI_MAX_CHANNEL_NUM); 35058c2ecf20Sopenharmony_ci n_channels = WMI_MAX_CHANNEL_NUM; 35068c2ecf20Sopenharmony_ci } 35078c2ecf20Sopenharmony_ci cmd->num_of_channels = n_channels; 35088c2ecf20Sopenharmony_ci 35098c2ecf20Sopenharmony_ci for (i = 0; i < n_channels; i++) { 35108c2ecf20Sopenharmony_ci struct ieee80211_channel *cfg_chan = channels[i]; 35118c2ecf20Sopenharmony_ci 35128c2ecf20Sopenharmony_ci cmd->channel_list[i] = cfg_chan->hw_value - 1; 35138c2ecf20Sopenharmony_ci } 35148c2ecf20Sopenharmony_ci} 35158c2ecf20Sopenharmony_ci 35168c2ecf20Sopenharmony_cistatic void 35178c2ecf20Sopenharmony_ciwmi_sched_scan_set_plans(struct wil6210_priv *wil, 35188c2ecf20Sopenharmony_ci struct wmi_start_sched_scan_cmd *cmd, 35198c2ecf20Sopenharmony_ci struct cfg80211_sched_scan_plan *scan_plans, 35208c2ecf20Sopenharmony_ci int n_scan_plans) 35218c2ecf20Sopenharmony_ci{ 35228c2ecf20Sopenharmony_ci int i; 35238c2ecf20Sopenharmony_ci 35248c2ecf20Sopenharmony_ci if (n_scan_plans > WMI_MAX_PLANS_NUM) { 35258c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "too many plans (%d), use first %d\n", 35268c2ecf20Sopenharmony_ci n_scan_plans, WMI_MAX_PLANS_NUM); 35278c2ecf20Sopenharmony_ci n_scan_plans = WMI_MAX_PLANS_NUM; 35288c2ecf20Sopenharmony_ci } 35298c2ecf20Sopenharmony_ci 35308c2ecf20Sopenharmony_ci for (i = 0; i < n_scan_plans; i++) { 35318c2ecf20Sopenharmony_ci struct cfg80211_sched_scan_plan *cfg_plan = &scan_plans[i]; 35328c2ecf20Sopenharmony_ci 35338c2ecf20Sopenharmony_ci cmd->scan_plans[i].interval_sec = 35348c2ecf20Sopenharmony_ci cpu_to_le16(cfg_plan->interval); 35358c2ecf20Sopenharmony_ci cmd->scan_plans[i].num_of_iterations = 35368c2ecf20Sopenharmony_ci cpu_to_le16(cfg_plan->iterations); 35378c2ecf20Sopenharmony_ci } 35388c2ecf20Sopenharmony_ci} 35398c2ecf20Sopenharmony_ci 35408c2ecf20Sopenharmony_ciint wmi_start_sched_scan(struct wil6210_priv *wil, 35418c2ecf20Sopenharmony_ci struct cfg80211_sched_scan_request *request) 35428c2ecf20Sopenharmony_ci{ 35438c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 35448c2ecf20Sopenharmony_ci int rc; 35458c2ecf20Sopenharmony_ci struct wmi_start_sched_scan_cmd cmd = { 35468c2ecf20Sopenharmony_ci .min_rssi_threshold = S8_MIN, 35478c2ecf20Sopenharmony_ci .initial_delay_sec = cpu_to_le16(request->delay), 35488c2ecf20Sopenharmony_ci }; 35498c2ecf20Sopenharmony_ci struct { 35508c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 35518c2ecf20Sopenharmony_ci struct wmi_start_sched_scan_event evt; 35528c2ecf20Sopenharmony_ci } __packed reply = { 35538c2ecf20Sopenharmony_ci .evt = {.result = WMI_PNO_REJECT}, 35548c2ecf20Sopenharmony_ci }; 35558c2ecf20Sopenharmony_ci 35568c2ecf20Sopenharmony_ci if (!test_bit(WMI_FW_CAPABILITY_PNO, wil->fw_capabilities)) 35578c2ecf20Sopenharmony_ci return -ENOTSUPP; 35588c2ecf20Sopenharmony_ci 35598c2ecf20Sopenharmony_ci if (request->min_rssi_thold >= S8_MIN && 35608c2ecf20Sopenharmony_ci request->min_rssi_thold <= S8_MAX) 35618c2ecf20Sopenharmony_ci cmd.min_rssi_threshold = request->min_rssi_thold; 35628c2ecf20Sopenharmony_ci 35638c2ecf20Sopenharmony_ci wmi_sched_scan_set_ssids(wil, &cmd, request->ssids, request->n_ssids, 35648c2ecf20Sopenharmony_ci request->match_sets, request->n_match_sets); 35658c2ecf20Sopenharmony_ci wmi_sched_scan_set_channels(wil, &cmd, 35668c2ecf20Sopenharmony_ci request->n_channels, request->channels); 35678c2ecf20Sopenharmony_ci wmi_sched_scan_set_plans(wil, &cmd, 35688c2ecf20Sopenharmony_ci request->scan_plans, request->n_scan_plans); 35698c2ecf20Sopenharmony_ci 35708c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_START_SCHED_SCAN_CMDID, vif->mid, 35718c2ecf20Sopenharmony_ci &cmd, sizeof(cmd), 35728c2ecf20Sopenharmony_ci WMI_START_SCHED_SCAN_EVENTID, &reply, sizeof(reply), 35738c2ecf20Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 35748c2ecf20Sopenharmony_ci if (rc) 35758c2ecf20Sopenharmony_ci return rc; 35768c2ecf20Sopenharmony_ci 35778c2ecf20Sopenharmony_ci if (reply.evt.result != WMI_PNO_SUCCESS) { 35788c2ecf20Sopenharmony_ci wil_err(wil, "start sched scan failed, result %d\n", 35798c2ecf20Sopenharmony_ci reply.evt.result); 35808c2ecf20Sopenharmony_ci return -EINVAL; 35818c2ecf20Sopenharmony_ci } 35828c2ecf20Sopenharmony_ci 35838c2ecf20Sopenharmony_ci return 0; 35848c2ecf20Sopenharmony_ci} 35858c2ecf20Sopenharmony_ci 35868c2ecf20Sopenharmony_ciint wmi_stop_sched_scan(struct wil6210_priv *wil) 35878c2ecf20Sopenharmony_ci{ 35888c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 35898c2ecf20Sopenharmony_ci int rc; 35908c2ecf20Sopenharmony_ci struct { 35918c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 35928c2ecf20Sopenharmony_ci struct wmi_stop_sched_scan_event evt; 35938c2ecf20Sopenharmony_ci } __packed reply = { 35948c2ecf20Sopenharmony_ci .evt = {.result = WMI_PNO_REJECT}, 35958c2ecf20Sopenharmony_ci }; 35968c2ecf20Sopenharmony_ci 35978c2ecf20Sopenharmony_ci if (!test_bit(WMI_FW_CAPABILITY_PNO, wil->fw_capabilities)) 35988c2ecf20Sopenharmony_ci return -ENOTSUPP; 35998c2ecf20Sopenharmony_ci 36008c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_STOP_SCHED_SCAN_CMDID, vif->mid, NULL, 0, 36018c2ecf20Sopenharmony_ci WMI_STOP_SCHED_SCAN_EVENTID, &reply, sizeof(reply), 36028c2ecf20Sopenharmony_ci WIL_WMI_CALL_GENERAL_TO_MS); 36038c2ecf20Sopenharmony_ci if (rc) 36048c2ecf20Sopenharmony_ci return rc; 36058c2ecf20Sopenharmony_ci 36068c2ecf20Sopenharmony_ci if (reply.evt.result != WMI_PNO_SUCCESS) { 36078c2ecf20Sopenharmony_ci wil_err(wil, "stop sched scan failed, result %d\n", 36088c2ecf20Sopenharmony_ci reply.evt.result); 36098c2ecf20Sopenharmony_ci return -EINVAL; 36108c2ecf20Sopenharmony_ci } 36118c2ecf20Sopenharmony_ci 36128c2ecf20Sopenharmony_ci return 0; 36138c2ecf20Sopenharmony_ci} 36148c2ecf20Sopenharmony_ci 36158c2ecf20Sopenharmony_ciint wmi_mgmt_tx(struct wil6210_vif *vif, const u8 *buf, size_t len) 36168c2ecf20Sopenharmony_ci{ 36178c2ecf20Sopenharmony_ci size_t total; 36188c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 36198c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt_frame = (void *)buf; 36208c2ecf20Sopenharmony_ci struct wmi_sw_tx_req_cmd *cmd; 36218c2ecf20Sopenharmony_ci struct { 36228c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 36238c2ecf20Sopenharmony_ci struct wmi_sw_tx_complete_event evt; 36248c2ecf20Sopenharmony_ci } __packed evt = { 36258c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 36268c2ecf20Sopenharmony_ci }; 36278c2ecf20Sopenharmony_ci int rc; 36288c2ecf20Sopenharmony_ci 36298c2ecf20Sopenharmony_ci wil_dbg_misc(wil, "mgmt_tx mid %d\n", vif->mid); 36308c2ecf20Sopenharmony_ci wil_hex_dump_misc("mgmt tx frame ", DUMP_PREFIX_OFFSET, 16, 1, buf, 36318c2ecf20Sopenharmony_ci len, true); 36328c2ecf20Sopenharmony_ci 36338c2ecf20Sopenharmony_ci if (len < sizeof(struct ieee80211_hdr_3addr)) 36348c2ecf20Sopenharmony_ci return -EINVAL; 36358c2ecf20Sopenharmony_ci 36368c2ecf20Sopenharmony_ci total = sizeof(*cmd) + len; 36378c2ecf20Sopenharmony_ci if (total < len) { 36388c2ecf20Sopenharmony_ci wil_err(wil, "mgmt_tx invalid len %zu\n", len); 36398c2ecf20Sopenharmony_ci return -EINVAL; 36408c2ecf20Sopenharmony_ci } 36418c2ecf20Sopenharmony_ci 36428c2ecf20Sopenharmony_ci cmd = kmalloc(total, GFP_KERNEL); 36438c2ecf20Sopenharmony_ci if (!cmd) 36448c2ecf20Sopenharmony_ci return -ENOMEM; 36458c2ecf20Sopenharmony_ci 36468c2ecf20Sopenharmony_ci memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN); 36478c2ecf20Sopenharmony_ci cmd->len = cpu_to_le16(len); 36488c2ecf20Sopenharmony_ci memcpy(cmd->payload, buf, len); 36498c2ecf20Sopenharmony_ci 36508c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, vif->mid, cmd, total, 36518c2ecf20Sopenharmony_ci WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000); 36528c2ecf20Sopenharmony_ci if (!rc && evt.evt.status != WMI_FW_STATUS_SUCCESS) { 36538c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "mgmt_tx failed with status %d\n", 36548c2ecf20Sopenharmony_ci evt.evt.status); 36558c2ecf20Sopenharmony_ci rc = -EAGAIN; 36568c2ecf20Sopenharmony_ci } 36578c2ecf20Sopenharmony_ci 36588c2ecf20Sopenharmony_ci kfree(cmd); 36598c2ecf20Sopenharmony_ci 36608c2ecf20Sopenharmony_ci return rc; 36618c2ecf20Sopenharmony_ci} 36628c2ecf20Sopenharmony_ci 36638c2ecf20Sopenharmony_ciint wmi_mgmt_tx_ext(struct wil6210_vif *vif, const u8 *buf, size_t len, 36648c2ecf20Sopenharmony_ci u8 channel, u16 duration_ms) 36658c2ecf20Sopenharmony_ci{ 36668c2ecf20Sopenharmony_ci size_t total; 36678c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 36688c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt_frame = (void *)buf; 36698c2ecf20Sopenharmony_ci struct wmi_sw_tx_req_ext_cmd *cmd; 36708c2ecf20Sopenharmony_ci struct { 36718c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 36728c2ecf20Sopenharmony_ci struct wmi_sw_tx_complete_event evt; 36738c2ecf20Sopenharmony_ci } __packed evt = { 36748c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 36758c2ecf20Sopenharmony_ci }; 36768c2ecf20Sopenharmony_ci int rc; 36778c2ecf20Sopenharmony_ci 36788c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "mgmt_tx_ext mid %d channel %d duration %d\n", 36798c2ecf20Sopenharmony_ci vif->mid, channel, duration_ms); 36808c2ecf20Sopenharmony_ci wil_hex_dump_wmi("mgmt_tx_ext frame ", DUMP_PREFIX_OFFSET, 16, 1, buf, 36818c2ecf20Sopenharmony_ci len, true); 36828c2ecf20Sopenharmony_ci 36838c2ecf20Sopenharmony_ci if (len < sizeof(struct ieee80211_hdr_3addr)) { 36848c2ecf20Sopenharmony_ci wil_err(wil, "short frame. len %zu\n", len); 36858c2ecf20Sopenharmony_ci return -EINVAL; 36868c2ecf20Sopenharmony_ci } 36878c2ecf20Sopenharmony_ci 36888c2ecf20Sopenharmony_ci total = sizeof(*cmd) + len; 36898c2ecf20Sopenharmony_ci if (total < len) { 36908c2ecf20Sopenharmony_ci wil_err(wil, "mgmt_tx_ext invalid len %zu\n", len); 36918c2ecf20Sopenharmony_ci return -EINVAL; 36928c2ecf20Sopenharmony_ci } 36938c2ecf20Sopenharmony_ci 36948c2ecf20Sopenharmony_ci cmd = kzalloc(total, GFP_KERNEL); 36958c2ecf20Sopenharmony_ci if (!cmd) 36968c2ecf20Sopenharmony_ci return -ENOMEM; 36978c2ecf20Sopenharmony_ci 36988c2ecf20Sopenharmony_ci memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN); 36998c2ecf20Sopenharmony_ci cmd->len = cpu_to_le16(len); 37008c2ecf20Sopenharmony_ci memcpy(cmd->payload, buf, len); 37018c2ecf20Sopenharmony_ci cmd->channel = channel - 1; 37028c2ecf20Sopenharmony_ci cmd->duration_ms = cpu_to_le16(duration_ms); 37038c2ecf20Sopenharmony_ci 37048c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_SW_TX_REQ_EXT_CMDID, vif->mid, cmd, total, 37058c2ecf20Sopenharmony_ci WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000); 37068c2ecf20Sopenharmony_ci if (!rc && evt.evt.status != WMI_FW_STATUS_SUCCESS) { 37078c2ecf20Sopenharmony_ci wil_dbg_wmi(wil, "mgmt_tx_ext failed with status %d\n", 37088c2ecf20Sopenharmony_ci evt.evt.status); 37098c2ecf20Sopenharmony_ci rc = -EAGAIN; 37108c2ecf20Sopenharmony_ci } 37118c2ecf20Sopenharmony_ci 37128c2ecf20Sopenharmony_ci kfree(cmd); 37138c2ecf20Sopenharmony_ci 37148c2ecf20Sopenharmony_ci return rc; 37158c2ecf20Sopenharmony_ci} 37168c2ecf20Sopenharmony_ci 37178c2ecf20Sopenharmony_ciint wil_wmi_tx_sring_cfg(struct wil6210_priv *wil, int ring_id) 37188c2ecf20Sopenharmony_ci{ 37198c2ecf20Sopenharmony_ci int rc; 37208c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 37218c2ecf20Sopenharmony_ci struct wil_status_ring *sring = &wil->srings[ring_id]; 37228c2ecf20Sopenharmony_ci struct wmi_tx_status_ring_add_cmd cmd = { 37238c2ecf20Sopenharmony_ci .ring_cfg = { 37248c2ecf20Sopenharmony_ci .ring_size = cpu_to_le16(sring->size), 37258c2ecf20Sopenharmony_ci }, 37268c2ecf20Sopenharmony_ci .irq_index = WIL_TX_STATUS_IRQ_IDX 37278c2ecf20Sopenharmony_ci }; 37288c2ecf20Sopenharmony_ci struct { 37298c2ecf20Sopenharmony_ci struct wmi_cmd_hdr hdr; 37308c2ecf20Sopenharmony_ci struct wmi_tx_status_ring_cfg_done_event evt; 37318c2ecf20Sopenharmony_ci } __packed reply = { 37328c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 37338c2ecf20Sopenharmony_ci }; 37348c2ecf20Sopenharmony_ci 37358c2ecf20Sopenharmony_ci cmd.ring_cfg.ring_id = ring_id; 37368c2ecf20Sopenharmony_ci 37378c2ecf20Sopenharmony_ci cmd.ring_cfg.ring_mem_base = cpu_to_le64(sring->pa); 37388c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_TX_STATUS_RING_ADD_CMDID, vif->mid, &cmd, 37398c2ecf20Sopenharmony_ci sizeof(cmd), WMI_TX_STATUS_RING_CFG_DONE_EVENTID, 37408c2ecf20Sopenharmony_ci &reply, sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); 37418c2ecf20Sopenharmony_ci if (rc) { 37428c2ecf20Sopenharmony_ci wil_err(wil, "TX_STATUS_RING_ADD_CMD failed, rc %d\n", rc); 37438c2ecf20Sopenharmony_ci return rc; 37448c2ecf20Sopenharmony_ci } 37458c2ecf20Sopenharmony_ci 37468c2ecf20Sopenharmony_ci if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { 37478c2ecf20Sopenharmony_ci wil_err(wil, "TX_STATUS_RING_ADD_CMD failed, status %d\n", 37488c2ecf20Sopenharmony_ci reply.evt.status); 37498c2ecf20Sopenharmony_ci return -EINVAL; 37508c2ecf20Sopenharmony_ci } 37518c2ecf20Sopenharmony_ci 37528c2ecf20Sopenharmony_ci sring->hwtail = le32_to_cpu(reply.evt.ring_tail_ptr); 37538c2ecf20Sopenharmony_ci 37548c2ecf20Sopenharmony_ci return 0; 37558c2ecf20Sopenharmony_ci} 37568c2ecf20Sopenharmony_ci 37578c2ecf20Sopenharmony_ciint wil_wmi_cfg_def_rx_offload(struct wil6210_priv *wil, u16 max_rx_pl_per_desc) 37588c2ecf20Sopenharmony_ci{ 37598c2ecf20Sopenharmony_ci struct net_device *ndev = wil->main_ndev; 37608c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(ndev); 37618c2ecf20Sopenharmony_ci int rc; 37628c2ecf20Sopenharmony_ci struct wmi_cfg_def_rx_offload_cmd cmd = { 37638c2ecf20Sopenharmony_ci .max_msdu_size = cpu_to_le16(wil_mtu2macbuf(WIL_MAX_ETH_MTU)), 37648c2ecf20Sopenharmony_ci .max_rx_pl_per_desc = cpu_to_le16(max_rx_pl_per_desc), 37658c2ecf20Sopenharmony_ci .decap_trans_type = WMI_DECAP_TYPE_802_3, 37668c2ecf20Sopenharmony_ci .l2_802_3_offload_ctrl = 0, 37678c2ecf20Sopenharmony_ci .l3_l4_ctrl = 1 << L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS, 37688c2ecf20Sopenharmony_ci }; 37698c2ecf20Sopenharmony_ci struct { 37708c2ecf20Sopenharmony_ci struct wmi_cmd_hdr hdr; 37718c2ecf20Sopenharmony_ci struct wmi_cfg_def_rx_offload_done_event evt; 37728c2ecf20Sopenharmony_ci } __packed reply = { 37738c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 37748c2ecf20Sopenharmony_ci }; 37758c2ecf20Sopenharmony_ci 37768c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_CFG_DEF_RX_OFFLOAD_CMDID, vif->mid, &cmd, 37778c2ecf20Sopenharmony_ci sizeof(cmd), WMI_CFG_DEF_RX_OFFLOAD_DONE_EVENTID, &reply, 37788c2ecf20Sopenharmony_ci sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); 37798c2ecf20Sopenharmony_ci if (rc) { 37808c2ecf20Sopenharmony_ci wil_err(wil, "WMI_CFG_DEF_RX_OFFLOAD_CMD failed, rc %d\n", rc); 37818c2ecf20Sopenharmony_ci return rc; 37828c2ecf20Sopenharmony_ci } 37838c2ecf20Sopenharmony_ci 37848c2ecf20Sopenharmony_ci if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { 37858c2ecf20Sopenharmony_ci wil_err(wil, "WMI_CFG_DEF_RX_OFFLOAD_CMD failed, status %d\n", 37868c2ecf20Sopenharmony_ci reply.evt.status); 37878c2ecf20Sopenharmony_ci return -EINVAL; 37888c2ecf20Sopenharmony_ci } 37898c2ecf20Sopenharmony_ci 37908c2ecf20Sopenharmony_ci return 0; 37918c2ecf20Sopenharmony_ci} 37928c2ecf20Sopenharmony_ci 37938c2ecf20Sopenharmony_ciint wil_wmi_rx_sring_add(struct wil6210_priv *wil, u16 ring_id) 37948c2ecf20Sopenharmony_ci{ 37958c2ecf20Sopenharmony_ci struct net_device *ndev = wil->main_ndev; 37968c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(ndev); 37978c2ecf20Sopenharmony_ci struct wil_status_ring *sring = &wil->srings[ring_id]; 37988c2ecf20Sopenharmony_ci int rc; 37998c2ecf20Sopenharmony_ci struct wmi_rx_status_ring_add_cmd cmd = { 38008c2ecf20Sopenharmony_ci .ring_cfg = { 38018c2ecf20Sopenharmony_ci .ring_size = cpu_to_le16(sring->size), 38028c2ecf20Sopenharmony_ci .ring_id = ring_id, 38038c2ecf20Sopenharmony_ci }, 38048c2ecf20Sopenharmony_ci .rx_msg_type = wil->use_compressed_rx_status ? 38058c2ecf20Sopenharmony_ci WMI_RX_MSG_TYPE_COMPRESSED : 38068c2ecf20Sopenharmony_ci WMI_RX_MSG_TYPE_EXTENDED, 38078c2ecf20Sopenharmony_ci .irq_index = WIL_RX_STATUS_IRQ_IDX, 38088c2ecf20Sopenharmony_ci }; 38098c2ecf20Sopenharmony_ci struct { 38108c2ecf20Sopenharmony_ci struct wmi_cmd_hdr hdr; 38118c2ecf20Sopenharmony_ci struct wmi_rx_status_ring_cfg_done_event evt; 38128c2ecf20Sopenharmony_ci } __packed reply = { 38138c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 38148c2ecf20Sopenharmony_ci }; 38158c2ecf20Sopenharmony_ci 38168c2ecf20Sopenharmony_ci cmd.ring_cfg.ring_mem_base = cpu_to_le64(sring->pa); 38178c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_RX_STATUS_RING_ADD_CMDID, vif->mid, &cmd, 38188c2ecf20Sopenharmony_ci sizeof(cmd), WMI_RX_STATUS_RING_CFG_DONE_EVENTID, &reply, 38198c2ecf20Sopenharmony_ci sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); 38208c2ecf20Sopenharmony_ci if (rc) { 38218c2ecf20Sopenharmony_ci wil_err(wil, "RX_STATUS_RING_ADD_CMD failed, rc %d\n", rc); 38228c2ecf20Sopenharmony_ci return rc; 38238c2ecf20Sopenharmony_ci } 38248c2ecf20Sopenharmony_ci 38258c2ecf20Sopenharmony_ci if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { 38268c2ecf20Sopenharmony_ci wil_err(wil, "RX_STATUS_RING_ADD_CMD failed, status %d\n", 38278c2ecf20Sopenharmony_ci reply.evt.status); 38288c2ecf20Sopenharmony_ci return -EINVAL; 38298c2ecf20Sopenharmony_ci } 38308c2ecf20Sopenharmony_ci 38318c2ecf20Sopenharmony_ci sring->hwtail = le32_to_cpu(reply.evt.ring_tail_ptr); 38328c2ecf20Sopenharmony_ci 38338c2ecf20Sopenharmony_ci return 0; 38348c2ecf20Sopenharmony_ci} 38358c2ecf20Sopenharmony_ci 38368c2ecf20Sopenharmony_ciint wil_wmi_rx_desc_ring_add(struct wil6210_priv *wil, int status_ring_id) 38378c2ecf20Sopenharmony_ci{ 38388c2ecf20Sopenharmony_ci struct net_device *ndev = wil->main_ndev; 38398c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(ndev); 38408c2ecf20Sopenharmony_ci struct wil_ring *ring = &wil->ring_rx; 38418c2ecf20Sopenharmony_ci int rc; 38428c2ecf20Sopenharmony_ci struct wmi_rx_desc_ring_add_cmd cmd = { 38438c2ecf20Sopenharmony_ci .ring_cfg = { 38448c2ecf20Sopenharmony_ci .ring_size = cpu_to_le16(ring->size), 38458c2ecf20Sopenharmony_ci .ring_id = WIL_RX_DESC_RING_ID, 38468c2ecf20Sopenharmony_ci }, 38478c2ecf20Sopenharmony_ci .status_ring_id = status_ring_id, 38488c2ecf20Sopenharmony_ci .irq_index = WIL_RX_STATUS_IRQ_IDX, 38498c2ecf20Sopenharmony_ci }; 38508c2ecf20Sopenharmony_ci struct { 38518c2ecf20Sopenharmony_ci struct wmi_cmd_hdr hdr; 38528c2ecf20Sopenharmony_ci struct wmi_rx_desc_ring_cfg_done_event evt; 38538c2ecf20Sopenharmony_ci } __packed reply = { 38548c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 38558c2ecf20Sopenharmony_ci }; 38568c2ecf20Sopenharmony_ci 38578c2ecf20Sopenharmony_ci cmd.ring_cfg.ring_mem_base = cpu_to_le64(ring->pa); 38588c2ecf20Sopenharmony_ci cmd.sw_tail_host_addr = cpu_to_le64(ring->edma_rx_swtail.pa); 38598c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_RX_DESC_RING_ADD_CMDID, vif->mid, &cmd, 38608c2ecf20Sopenharmony_ci sizeof(cmd), WMI_RX_DESC_RING_CFG_DONE_EVENTID, &reply, 38618c2ecf20Sopenharmony_ci sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); 38628c2ecf20Sopenharmony_ci if (rc) { 38638c2ecf20Sopenharmony_ci wil_err(wil, "WMI_RX_DESC_RING_ADD_CMD failed, rc %d\n", rc); 38648c2ecf20Sopenharmony_ci return rc; 38658c2ecf20Sopenharmony_ci } 38668c2ecf20Sopenharmony_ci 38678c2ecf20Sopenharmony_ci if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { 38688c2ecf20Sopenharmony_ci wil_err(wil, "WMI_RX_DESC_RING_ADD_CMD failed, status %d\n", 38698c2ecf20Sopenharmony_ci reply.evt.status); 38708c2ecf20Sopenharmony_ci return -EINVAL; 38718c2ecf20Sopenharmony_ci } 38728c2ecf20Sopenharmony_ci 38738c2ecf20Sopenharmony_ci ring->hwtail = le32_to_cpu(reply.evt.ring_tail_ptr); 38748c2ecf20Sopenharmony_ci 38758c2ecf20Sopenharmony_ci return 0; 38768c2ecf20Sopenharmony_ci} 38778c2ecf20Sopenharmony_ci 38788c2ecf20Sopenharmony_ciint wil_wmi_tx_desc_ring_add(struct wil6210_vif *vif, int ring_id, int cid, 38798c2ecf20Sopenharmony_ci int tid) 38808c2ecf20Sopenharmony_ci{ 38818c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 38828c2ecf20Sopenharmony_ci int sring_id = wil->tx_sring_idx; /* there is only one TX sring */ 38838c2ecf20Sopenharmony_ci int rc; 38848c2ecf20Sopenharmony_ci struct wil_ring *ring = &wil->ring_tx[ring_id]; 38858c2ecf20Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_id]; 38868c2ecf20Sopenharmony_ci struct wmi_tx_desc_ring_add_cmd cmd = { 38878c2ecf20Sopenharmony_ci .ring_cfg = { 38888c2ecf20Sopenharmony_ci .ring_size = cpu_to_le16(ring->size), 38898c2ecf20Sopenharmony_ci .ring_id = ring_id, 38908c2ecf20Sopenharmony_ci }, 38918c2ecf20Sopenharmony_ci .status_ring_id = sring_id, 38928c2ecf20Sopenharmony_ci .cid = cid, 38938c2ecf20Sopenharmony_ci .tid = tid, 38948c2ecf20Sopenharmony_ci .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, 38958c2ecf20Sopenharmony_ci .max_msdu_size = cpu_to_le16(wil_mtu2macbuf(mtu_max)), 38968c2ecf20Sopenharmony_ci .schd_params = { 38978c2ecf20Sopenharmony_ci .priority = cpu_to_le16(0), 38988c2ecf20Sopenharmony_ci .timeslot_us = cpu_to_le16(0xfff), 38998c2ecf20Sopenharmony_ci } 39008c2ecf20Sopenharmony_ci }; 39018c2ecf20Sopenharmony_ci struct { 39028c2ecf20Sopenharmony_ci struct wmi_cmd_hdr hdr; 39038c2ecf20Sopenharmony_ci struct wmi_tx_desc_ring_cfg_done_event evt; 39048c2ecf20Sopenharmony_ci } __packed reply = { 39058c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 39068c2ecf20Sopenharmony_ci }; 39078c2ecf20Sopenharmony_ci 39088c2ecf20Sopenharmony_ci cmd.ring_cfg.ring_mem_base = cpu_to_le64(ring->pa); 39098c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_TX_DESC_RING_ADD_CMDID, vif->mid, &cmd, 39108c2ecf20Sopenharmony_ci sizeof(cmd), WMI_TX_DESC_RING_CFG_DONE_EVENTID, &reply, 39118c2ecf20Sopenharmony_ci sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); 39128c2ecf20Sopenharmony_ci if (rc) { 39138c2ecf20Sopenharmony_ci wil_err(wil, "WMI_TX_DESC_RING_ADD_CMD failed, rc %d\n", rc); 39148c2ecf20Sopenharmony_ci return rc; 39158c2ecf20Sopenharmony_ci } 39168c2ecf20Sopenharmony_ci 39178c2ecf20Sopenharmony_ci if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { 39188c2ecf20Sopenharmony_ci wil_err(wil, "WMI_TX_DESC_RING_ADD_CMD failed, status %d\n", 39198c2ecf20Sopenharmony_ci reply.evt.status); 39208c2ecf20Sopenharmony_ci return -EINVAL; 39218c2ecf20Sopenharmony_ci } 39228c2ecf20Sopenharmony_ci 39238c2ecf20Sopenharmony_ci spin_lock_bh(&txdata->lock); 39248c2ecf20Sopenharmony_ci ring->hwtail = le32_to_cpu(reply.evt.ring_tail_ptr); 39258c2ecf20Sopenharmony_ci txdata->mid = vif->mid; 39268c2ecf20Sopenharmony_ci txdata->enabled = 1; 39278c2ecf20Sopenharmony_ci spin_unlock_bh(&txdata->lock); 39288c2ecf20Sopenharmony_ci 39298c2ecf20Sopenharmony_ci return 0; 39308c2ecf20Sopenharmony_ci} 39318c2ecf20Sopenharmony_ci 39328c2ecf20Sopenharmony_ciint wil_wmi_bcast_desc_ring_add(struct wil6210_vif *vif, int ring_id) 39338c2ecf20Sopenharmony_ci{ 39348c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 39358c2ecf20Sopenharmony_ci struct wil_ring *ring = &wil->ring_tx[ring_id]; 39368c2ecf20Sopenharmony_ci int rc; 39378c2ecf20Sopenharmony_ci struct wmi_bcast_desc_ring_add_cmd cmd = { 39388c2ecf20Sopenharmony_ci .ring_cfg = { 39398c2ecf20Sopenharmony_ci .ring_size = cpu_to_le16(ring->size), 39408c2ecf20Sopenharmony_ci .ring_id = ring_id, 39418c2ecf20Sopenharmony_ci }, 39428c2ecf20Sopenharmony_ci .max_msdu_size = cpu_to_le16(wil_mtu2macbuf(mtu_max)), 39438c2ecf20Sopenharmony_ci .status_ring_id = wil->tx_sring_idx, 39448c2ecf20Sopenharmony_ci .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, 39458c2ecf20Sopenharmony_ci }; 39468c2ecf20Sopenharmony_ci struct { 39478c2ecf20Sopenharmony_ci struct wmi_cmd_hdr hdr; 39488c2ecf20Sopenharmony_ci struct wmi_rx_desc_ring_cfg_done_event evt; 39498c2ecf20Sopenharmony_ci } __packed reply = { 39508c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 39518c2ecf20Sopenharmony_ci }; 39528c2ecf20Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_id]; 39538c2ecf20Sopenharmony_ci 39548c2ecf20Sopenharmony_ci cmd.ring_cfg.ring_mem_base = cpu_to_le64(ring->pa); 39558c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_BCAST_DESC_RING_ADD_CMDID, vif->mid, &cmd, 39568c2ecf20Sopenharmony_ci sizeof(cmd), WMI_TX_DESC_RING_CFG_DONE_EVENTID, &reply, 39578c2ecf20Sopenharmony_ci sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); 39588c2ecf20Sopenharmony_ci if (rc) { 39598c2ecf20Sopenharmony_ci wil_err(wil, "WMI_BCAST_DESC_RING_ADD_CMD failed, rc %d\n", rc); 39608c2ecf20Sopenharmony_ci return rc; 39618c2ecf20Sopenharmony_ci } 39628c2ecf20Sopenharmony_ci 39638c2ecf20Sopenharmony_ci if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { 39648c2ecf20Sopenharmony_ci wil_err(wil, "Broadcast Tx config failed, status %d\n", 39658c2ecf20Sopenharmony_ci reply.evt.status); 39668c2ecf20Sopenharmony_ci return -EINVAL; 39678c2ecf20Sopenharmony_ci } 39688c2ecf20Sopenharmony_ci 39698c2ecf20Sopenharmony_ci spin_lock_bh(&txdata->lock); 39708c2ecf20Sopenharmony_ci ring->hwtail = le32_to_cpu(reply.evt.ring_tail_ptr); 39718c2ecf20Sopenharmony_ci txdata->mid = vif->mid; 39728c2ecf20Sopenharmony_ci txdata->enabled = 1; 39738c2ecf20Sopenharmony_ci spin_unlock_bh(&txdata->lock); 39748c2ecf20Sopenharmony_ci 39758c2ecf20Sopenharmony_ci return 0; 39768c2ecf20Sopenharmony_ci} 39778c2ecf20Sopenharmony_ci 39788c2ecf20Sopenharmony_ciint wmi_link_stats_cfg(struct wil6210_vif *vif, u32 type, u8 cid, u32 interval) 39798c2ecf20Sopenharmony_ci{ 39808c2ecf20Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 39818c2ecf20Sopenharmony_ci struct wmi_link_stats_cmd cmd = { 39828c2ecf20Sopenharmony_ci .record_type_mask = cpu_to_le32(type), 39838c2ecf20Sopenharmony_ci .cid = cid, 39848c2ecf20Sopenharmony_ci .action = WMI_LINK_STATS_SNAPSHOT, 39858c2ecf20Sopenharmony_ci .interval_msec = cpu_to_le32(interval), 39868c2ecf20Sopenharmony_ci }; 39878c2ecf20Sopenharmony_ci struct { 39888c2ecf20Sopenharmony_ci struct wmi_cmd_hdr wmi; 39898c2ecf20Sopenharmony_ci struct wmi_link_stats_config_done_event evt; 39908c2ecf20Sopenharmony_ci } __packed reply = { 39918c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 39928c2ecf20Sopenharmony_ci }; 39938c2ecf20Sopenharmony_ci int rc; 39948c2ecf20Sopenharmony_ci 39958c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_LINK_STATS_CMDID, vif->mid, &cmd, sizeof(cmd), 39968c2ecf20Sopenharmony_ci WMI_LINK_STATS_CONFIG_DONE_EVENTID, &reply, 39978c2ecf20Sopenharmony_ci sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); 39988c2ecf20Sopenharmony_ci if (rc) { 39998c2ecf20Sopenharmony_ci wil_err(wil, "WMI_LINK_STATS_CMDID failed, rc %d\n", rc); 40008c2ecf20Sopenharmony_ci return rc; 40018c2ecf20Sopenharmony_ci } 40028c2ecf20Sopenharmony_ci 40038c2ecf20Sopenharmony_ci if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { 40048c2ecf20Sopenharmony_ci wil_err(wil, "Link statistics config failed, status %d\n", 40058c2ecf20Sopenharmony_ci reply.evt.status); 40068c2ecf20Sopenharmony_ci return -EINVAL; 40078c2ecf20Sopenharmony_ci } 40088c2ecf20Sopenharmony_ci 40098c2ecf20Sopenharmony_ci return 0; 40108c2ecf20Sopenharmony_ci} 40118c2ecf20Sopenharmony_ci 40128c2ecf20Sopenharmony_ciint wmi_set_cqm_rssi_config(struct wil6210_priv *wil, 40138c2ecf20Sopenharmony_ci s32 rssi_thold, u32 rssi_hyst) 40148c2ecf20Sopenharmony_ci{ 40158c2ecf20Sopenharmony_ci struct net_device *ndev = wil->main_ndev; 40168c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(ndev); 40178c2ecf20Sopenharmony_ci int rc; 40188c2ecf20Sopenharmony_ci struct { 40198c2ecf20Sopenharmony_ci struct wmi_set_link_monitor_cmd cmd; 40208c2ecf20Sopenharmony_ci s8 rssi_thold; 40218c2ecf20Sopenharmony_ci } __packed cmd = { 40228c2ecf20Sopenharmony_ci .cmd = { 40238c2ecf20Sopenharmony_ci .rssi_hyst = rssi_hyst, 40248c2ecf20Sopenharmony_ci .rssi_thresholds_list_size = 1, 40258c2ecf20Sopenharmony_ci }, 40268c2ecf20Sopenharmony_ci .rssi_thold = rssi_thold, 40278c2ecf20Sopenharmony_ci }; 40288c2ecf20Sopenharmony_ci struct { 40298c2ecf20Sopenharmony_ci struct wmi_cmd_hdr hdr; 40308c2ecf20Sopenharmony_ci struct wmi_set_link_monitor_event evt; 40318c2ecf20Sopenharmony_ci } __packed reply = { 40328c2ecf20Sopenharmony_ci .evt = {.status = WMI_FW_STATUS_FAILURE}, 40338c2ecf20Sopenharmony_ci }; 40348c2ecf20Sopenharmony_ci 40358c2ecf20Sopenharmony_ci if (rssi_thold > S8_MAX || rssi_thold < S8_MIN || rssi_hyst > U8_MAX) 40368c2ecf20Sopenharmony_ci return -EINVAL; 40378c2ecf20Sopenharmony_ci 40388c2ecf20Sopenharmony_ci rc = wmi_call(wil, WMI_SET_LINK_MONITOR_CMDID, vif->mid, &cmd, 40398c2ecf20Sopenharmony_ci sizeof(cmd), WMI_SET_LINK_MONITOR_EVENTID, 40408c2ecf20Sopenharmony_ci &reply, sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); 40418c2ecf20Sopenharmony_ci if (rc) { 40428c2ecf20Sopenharmony_ci wil_err(wil, "WMI_SET_LINK_MONITOR_CMDID failed, rc %d\n", rc); 40438c2ecf20Sopenharmony_ci return rc; 40448c2ecf20Sopenharmony_ci } 40458c2ecf20Sopenharmony_ci 40468c2ecf20Sopenharmony_ci if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { 40478c2ecf20Sopenharmony_ci wil_err(wil, "WMI_SET_LINK_MONITOR_CMDID failed, status %d\n", 40488c2ecf20Sopenharmony_ci reply.evt.status); 40498c2ecf20Sopenharmony_ci return -EINVAL; 40508c2ecf20Sopenharmony_ci } 40518c2ecf20Sopenharmony_ci 40528c2ecf20Sopenharmony_ci return 0; 40538c2ecf20Sopenharmony_ci} 4054