18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Firmware I/O code for mac80211 Prism54 drivers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> 68c2ecf20Sopenharmony_ci * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de> 78c2ecf20Sopenharmony_ci * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on: 108c2ecf20Sopenharmony_ci * - the islsm (softmac prism54) driver, which is: 118c2ecf20Sopenharmony_ci * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. 128c2ecf20Sopenharmony_ci * - stlc45xx driver 138c2ecf20Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/firmware.h> 188c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 198c2ecf20Sopenharmony_ci#include <linux/export.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <net/mac80211.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "p54.h" 248c2ecf20Sopenharmony_ci#include "eeprom.h" 258c2ecf20Sopenharmony_ci#include "lmac.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciint p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci struct p54_common *priv = dev->priv; 308c2ecf20Sopenharmony_ci struct exp_if *exp_if; 318c2ecf20Sopenharmony_ci struct bootrec *bootrec; 328c2ecf20Sopenharmony_ci u32 *data = (u32 *)fw->data; 338c2ecf20Sopenharmony_ci u32 *end_data = (u32 *)fw->data + (fw->size >> 2); 348c2ecf20Sopenharmony_ci u8 *fw_version = NULL; 358c2ecf20Sopenharmony_ci size_t len; 368c2ecf20Sopenharmony_ci int i; 378c2ecf20Sopenharmony_ci int maxlen; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (priv->rx_start) 408c2ecf20Sopenharmony_ci return 0; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci while (data < end_data && *data) 438c2ecf20Sopenharmony_ci data++; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci while (data < end_data && !*data) 468c2ecf20Sopenharmony_ci data++; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci bootrec = (struct bootrec *) data; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci while (bootrec->data <= end_data && (bootrec->data + 518c2ecf20Sopenharmony_ci (len = le32_to_cpu(bootrec->len))) <= end_data) { 528c2ecf20Sopenharmony_ci u32 code = le32_to_cpu(bootrec->code); 538c2ecf20Sopenharmony_ci switch (code) { 548c2ecf20Sopenharmony_ci case BR_CODE_COMPONENT_ID: 558c2ecf20Sopenharmony_ci priv->fw_interface = be32_to_cpup((__be32 *) 568c2ecf20Sopenharmony_ci bootrec->data); 578c2ecf20Sopenharmony_ci switch (priv->fw_interface) { 588c2ecf20Sopenharmony_ci case FW_LM86: 598c2ecf20Sopenharmony_ci case FW_LM20: 608c2ecf20Sopenharmony_ci case FW_LM87: { 618c2ecf20Sopenharmony_ci char *iftype = (char *)bootrec->data; 628c2ecf20Sopenharmony_ci wiphy_info(priv->hw->wiphy, 638c2ecf20Sopenharmony_ci "p54 detected a LM%c%c firmware\n", 648c2ecf20Sopenharmony_ci iftype[2], iftype[3]); 658c2ecf20Sopenharmony_ci break; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci case FW_FMAC: 688c2ecf20Sopenharmony_ci default: 698c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 708c2ecf20Sopenharmony_ci "unsupported firmware\n"); 718c2ecf20Sopenharmony_ci return -ENODEV; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci break; 748c2ecf20Sopenharmony_ci case BR_CODE_COMPONENT_VERSION: 758c2ecf20Sopenharmony_ci /* 24 bytes should be enough for all firmwares */ 768c2ecf20Sopenharmony_ci if (strnlen((unsigned char *) bootrec->data, 24) < 24) 778c2ecf20Sopenharmony_ci fw_version = (unsigned char *) bootrec->data; 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci case BR_CODE_DESCR: { 808c2ecf20Sopenharmony_ci struct bootrec_desc *desc = 818c2ecf20Sopenharmony_ci (struct bootrec_desc *)bootrec->data; 828c2ecf20Sopenharmony_ci priv->rx_start = le32_to_cpu(desc->rx_start); 838c2ecf20Sopenharmony_ci /* FIXME add sanity checking */ 848c2ecf20Sopenharmony_ci priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500; 858c2ecf20Sopenharmony_ci priv->headroom = desc->headroom; 868c2ecf20Sopenharmony_ci priv->tailroom = desc->tailroom; 878c2ecf20Sopenharmony_ci priv->privacy_caps = desc->privacy_caps; 888c2ecf20Sopenharmony_ci priv->rx_keycache_size = desc->rx_keycache_size; 898c2ecf20Sopenharmony_ci if (le32_to_cpu(bootrec->len) == 11) 908c2ecf20Sopenharmony_ci priv->rx_mtu = le16_to_cpu(desc->rx_mtu); 918c2ecf20Sopenharmony_ci else 928c2ecf20Sopenharmony_ci priv->rx_mtu = (size_t) 938c2ecf20Sopenharmony_ci 0x620 - priv->tx_hdr_len; 948c2ecf20Sopenharmony_ci maxlen = priv->tx_hdr_len + /* USB devices */ 958c2ecf20Sopenharmony_ci sizeof(struct p54_rx_data) + 968c2ecf20Sopenharmony_ci 4 + /* rx alignment */ 978c2ecf20Sopenharmony_ci IEEE80211_MAX_FRAG_THRESHOLD; 988c2ecf20Sopenharmony_ci if (priv->rx_mtu > maxlen && PAGE_SIZE == 4096) { 998c2ecf20Sopenharmony_ci printk(KERN_INFO "p54: rx_mtu reduced from %d " 1008c2ecf20Sopenharmony_ci "to %d\n", priv->rx_mtu, maxlen); 1018c2ecf20Sopenharmony_ci priv->rx_mtu = maxlen; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci case BR_CODE_EXPOSED_IF: 1068c2ecf20Sopenharmony_ci exp_if = (struct exp_if *) bootrec->data; 1078c2ecf20Sopenharmony_ci for (i = 0; i < (len * sizeof(*exp_if) / 4); i++) 1088c2ecf20Sopenharmony_ci if (exp_if[i].if_id == cpu_to_le16(IF_ID_LMAC)) 1098c2ecf20Sopenharmony_ci priv->fw_var = le16_to_cpu(exp_if[i].variant); 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci case BR_CODE_DEPENDENT_IF: 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci case BR_CODE_END_OF_BRA: 1148c2ecf20Sopenharmony_ci case LEGACY_BR_CODE_END_OF_BRA: 1158c2ecf20Sopenharmony_ci end_data = NULL; 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci default: 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci bootrec = (struct bootrec *)&bootrec->data[len]; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (fw_version) { 1248c2ecf20Sopenharmony_ci wiphy_info(priv->hw->wiphy, 1258c2ecf20Sopenharmony_ci "FW rev %s - Softmac protocol %x.%x\n", 1268c2ecf20Sopenharmony_ci fw_version, priv->fw_var >> 8, priv->fw_var & 0xff); 1278c2ecf20Sopenharmony_ci snprintf(dev->wiphy->fw_version, sizeof(dev->wiphy->fw_version), 1288c2ecf20Sopenharmony_ci "%s - %x.%x", fw_version, 1298c2ecf20Sopenharmony_ci priv->fw_var >> 8, priv->fw_var & 0xff); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (priv->fw_var < 0x500) 1338c2ecf20Sopenharmony_ci wiphy_info(priv->hw->wiphy, 1348c2ecf20Sopenharmony_ci "you are using an obsolete firmware. " 1358c2ecf20Sopenharmony_ci "visit http://wireless.wiki.kernel.org/en/users/Drivers/p54 " 1368c2ecf20Sopenharmony_ci "and grab one for \"kernel >= 2.6.28\"!\n"); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (priv->fw_var >= 0x300) { 1398c2ecf20Sopenharmony_ci /* Firmware supports QoS, use it! */ 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (priv->fw_var >= 0x500) { 1428c2ecf20Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_VO].limit = 16; 1438c2ecf20Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_VI].limit = 16; 1448c2ecf20Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_BE].limit = 16; 1458c2ecf20Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_BK].limit = 16; 1468c2ecf20Sopenharmony_ci } else { 1478c2ecf20Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_VO].limit = 3; 1488c2ecf20Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_VI].limit = 4; 1498c2ecf20Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_BE].limit = 3; 1508c2ecf20Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_BK].limit = 2; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci priv->hw->queues = P54_QUEUE_AC_NUM; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci wiphy_info(priv->hw->wiphy, 1568c2ecf20Sopenharmony_ci "cryptographic accelerator WEP:%s, TKIP:%s, CCMP:%s\n", 1578c2ecf20Sopenharmony_ci (priv->privacy_caps & BR_DESC_PRIV_CAP_WEP) ? "YES" : "no", 1588c2ecf20Sopenharmony_ci (priv->privacy_caps & 1598c2ecf20Sopenharmony_ci (BR_DESC_PRIV_CAP_TKIP | BR_DESC_PRIV_CAP_MICHAEL)) 1608c2ecf20Sopenharmony_ci ? "YES" : "no", 1618c2ecf20Sopenharmony_ci (priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP) 1628c2ecf20Sopenharmony_ci ? "YES" : "no"); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (priv->rx_keycache_size) { 1658c2ecf20Sopenharmony_ci /* 1668c2ecf20Sopenharmony_ci * NOTE: 1678c2ecf20Sopenharmony_ci * 1688c2ecf20Sopenharmony_ci * The firmware provides at most 255 (0 - 254) slots 1698c2ecf20Sopenharmony_ci * for keys which are then used to offload decryption. 1708c2ecf20Sopenharmony_ci * As a result the 255 entry (aka 0xff) can be used 1718c2ecf20Sopenharmony_ci * safely by the driver to mark keys that didn't fit 1728c2ecf20Sopenharmony_ci * into the full cache. This trick saves us from 1738c2ecf20Sopenharmony_ci * keeping a extra list for uploaded keys. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci priv->used_rxkeys = kcalloc(BITS_TO_LONGS(priv->rx_keycache_size), 1778c2ecf20Sopenharmony_ci sizeof(long), 1788c2ecf20Sopenharmony_ci GFP_KERNEL); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (!priv->used_rxkeys) 1818c2ecf20Sopenharmony_ci return -ENOMEM; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return 0; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(p54_parse_firmware); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic struct sk_buff *p54_alloc_skb(struct p54_common *priv, u16 hdr_flags, 1898c2ecf20Sopenharmony_ci u16 payload_len, u16 type, gfp_t memflags) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct p54_hdr *hdr; 1928c2ecf20Sopenharmony_ci struct sk_buff *skb; 1938c2ecf20Sopenharmony_ci size_t frame_len = sizeof(*hdr) + payload_len; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (frame_len > P54_MAX_CTRL_FRAME_LEN) 1968c2ecf20Sopenharmony_ci return NULL; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (unlikely(skb_queue_len(&priv->tx_pending) > 64)) 1998c2ecf20Sopenharmony_ci return NULL; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci skb = __dev_alloc_skb(priv->tx_hdr_len + frame_len, memflags); 2028c2ecf20Sopenharmony_ci if (!skb) 2038c2ecf20Sopenharmony_ci return NULL; 2048c2ecf20Sopenharmony_ci skb_reserve(skb, priv->tx_hdr_len); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci hdr = skb_put(skb, sizeof(*hdr)); 2078c2ecf20Sopenharmony_ci hdr->flags = cpu_to_le16(hdr_flags); 2088c2ecf20Sopenharmony_ci hdr->len = cpu_to_le16(payload_len); 2098c2ecf20Sopenharmony_ci hdr->type = cpu_to_le16(type); 2108c2ecf20Sopenharmony_ci hdr->tries = hdr->rts_tries = 0; 2118c2ecf20Sopenharmony_ci return skb; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ciint p54_download_eeprom(struct p54_common *priv, void *buf, 2158c2ecf20Sopenharmony_ci u16 offset, u16 len) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct p54_eeprom_lm86 *eeprom_hdr; 2188c2ecf20Sopenharmony_ci struct sk_buff *skb; 2198c2ecf20Sopenharmony_ci size_t eeprom_hdr_size; 2208c2ecf20Sopenharmony_ci int ret = 0; 2218c2ecf20Sopenharmony_ci long timeout; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (priv->fw_var >= 0x509) 2248c2ecf20Sopenharmony_ci eeprom_hdr_size = sizeof(*eeprom_hdr); 2258c2ecf20Sopenharmony_ci else 2268c2ecf20Sopenharmony_ci eeprom_hdr_size = 0x4; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL, eeprom_hdr_size + 2298c2ecf20Sopenharmony_ci len, P54_CONTROL_TYPE_EEPROM_READBACK, 2308c2ecf20Sopenharmony_ci GFP_KERNEL); 2318c2ecf20Sopenharmony_ci if (unlikely(!skb)) 2328c2ecf20Sopenharmony_ci return -ENOMEM; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci mutex_lock(&priv->eeprom_mutex); 2358c2ecf20Sopenharmony_ci priv->eeprom = buf; 2368c2ecf20Sopenharmony_ci eeprom_hdr = skb_put(skb, eeprom_hdr_size + len); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (priv->fw_var < 0x509) { 2398c2ecf20Sopenharmony_ci eeprom_hdr->v1.offset = cpu_to_le16(offset); 2408c2ecf20Sopenharmony_ci eeprom_hdr->v1.len = cpu_to_le16(len); 2418c2ecf20Sopenharmony_ci } else { 2428c2ecf20Sopenharmony_ci eeprom_hdr->v2.offset = cpu_to_le32(offset); 2438c2ecf20Sopenharmony_ci eeprom_hdr->v2.len = cpu_to_le16(len); 2448c2ecf20Sopenharmony_ci eeprom_hdr->v2.magic2 = 0xf; 2458c2ecf20Sopenharmony_ci memcpy(eeprom_hdr->v2.magic, (const char *)"LOCK", 4); 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci p54_tx(priv, skb); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci timeout = wait_for_completion_interruptible_timeout( 2518c2ecf20Sopenharmony_ci &priv->eeprom_comp, HZ); 2528c2ecf20Sopenharmony_ci if (timeout <= 0) { 2538c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 2548c2ecf20Sopenharmony_ci "device does not respond or signal received!\n"); 2558c2ecf20Sopenharmony_ci ret = -EBUSY; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci priv->eeprom = NULL; 2588c2ecf20Sopenharmony_ci mutex_unlock(&priv->eeprom_mutex); 2598c2ecf20Sopenharmony_ci return ret; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ciint p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct sk_buff *skb; 2658c2ecf20Sopenharmony_ci struct p54_tim *tim; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*tim), 2688c2ecf20Sopenharmony_ci P54_CONTROL_TYPE_TIM, GFP_ATOMIC); 2698c2ecf20Sopenharmony_ci if (unlikely(!skb)) 2708c2ecf20Sopenharmony_ci return -ENOMEM; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci tim = skb_put(skb, sizeof(*tim)); 2738c2ecf20Sopenharmony_ci tim->count = 1; 2748c2ecf20Sopenharmony_ci tim->entry[0] = cpu_to_le16(set ? (aid | 0x8000) : aid); 2758c2ecf20Sopenharmony_ci p54_tx(priv, skb); 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ciint p54_sta_unlock(struct p54_common *priv, u8 *addr) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct sk_buff *skb; 2828c2ecf20Sopenharmony_ci struct p54_sta_unlock *sta; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*sta), 2858c2ecf20Sopenharmony_ci P54_CONTROL_TYPE_PSM_STA_UNLOCK, GFP_ATOMIC); 2868c2ecf20Sopenharmony_ci if (unlikely(!skb)) 2878c2ecf20Sopenharmony_ci return -ENOMEM; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci sta = skb_put(skb, sizeof(*sta)); 2908c2ecf20Sopenharmony_ci memcpy(sta->addr, addr, ETH_ALEN); 2918c2ecf20Sopenharmony_ci p54_tx(priv, skb); 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ciint p54_tx_cancel(struct p54_common *priv, __le32 req_id) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct sk_buff *skb; 2988c2ecf20Sopenharmony_ci struct p54_txcancel *cancel; 2998c2ecf20Sopenharmony_ci u32 _req_id = le32_to_cpu(req_id); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (unlikely(_req_id < priv->rx_start || _req_id > priv->rx_end)) 3028c2ecf20Sopenharmony_ci return -EINVAL; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*cancel), 3058c2ecf20Sopenharmony_ci P54_CONTROL_TYPE_TXCANCEL, GFP_ATOMIC); 3068c2ecf20Sopenharmony_ci if (unlikely(!skb)) 3078c2ecf20Sopenharmony_ci return -ENOMEM; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci cancel = skb_put(skb, sizeof(*cancel)); 3108c2ecf20Sopenharmony_ci cancel->req_id = req_id; 3118c2ecf20Sopenharmony_ci p54_tx(priv, skb); 3128c2ecf20Sopenharmony_ci return 0; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ciint p54_setup_mac(struct p54_common *priv) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct sk_buff *skb; 3188c2ecf20Sopenharmony_ci struct p54_setup_mac *setup; 3198c2ecf20Sopenharmony_ci u16 mode; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*setup), 3228c2ecf20Sopenharmony_ci P54_CONTROL_TYPE_SETUP, GFP_ATOMIC); 3238c2ecf20Sopenharmony_ci if (!skb) 3248c2ecf20Sopenharmony_ci return -ENOMEM; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci setup = skb_put(skb, sizeof(*setup)); 3278c2ecf20Sopenharmony_ci if (!(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) { 3288c2ecf20Sopenharmony_ci switch (priv->mode) { 3298c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 3308c2ecf20Sopenharmony_ci mode = P54_FILTER_TYPE_STATION; 3318c2ecf20Sopenharmony_ci break; 3328c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 3338c2ecf20Sopenharmony_ci mode = P54_FILTER_TYPE_AP; 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 3368c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 3378c2ecf20Sopenharmony_ci mode = P54_FILTER_TYPE_IBSS; 3388c2ecf20Sopenharmony_ci break; 3398c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 3408c2ecf20Sopenharmony_ci mode = P54_FILTER_TYPE_PROMISCUOUS; 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci default: 3438c2ecf20Sopenharmony_ci mode = P54_FILTER_TYPE_HIBERNATE; 3448c2ecf20Sopenharmony_ci break; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* 3488c2ecf20Sopenharmony_ci * "TRANSPARENT and PROMISCUOUS are mutually exclusive" 3498c2ecf20Sopenharmony_ci * STSW45X0C LMAC API - page 12 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_ci if (priv->filter_flags & FIF_OTHER_BSS && 3528c2ecf20Sopenharmony_ci (mode != P54_FILTER_TYPE_PROMISCUOUS)) 3538c2ecf20Sopenharmony_ci mode |= P54_FILTER_TYPE_TRANSPARENT; 3548c2ecf20Sopenharmony_ci } else { 3558c2ecf20Sopenharmony_ci mode = P54_FILTER_TYPE_HIBERNATE; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci setup->mac_mode = cpu_to_le16(mode); 3598c2ecf20Sopenharmony_ci memcpy(setup->mac_addr, priv->mac_addr, ETH_ALEN); 3608c2ecf20Sopenharmony_ci memcpy(setup->bssid, priv->bssid, ETH_ALEN); 3618c2ecf20Sopenharmony_ci setup->rx_antenna = 2 & priv->rx_diversity_mask; /* automatic */ 3628c2ecf20Sopenharmony_ci setup->rx_align = 0; 3638c2ecf20Sopenharmony_ci if (priv->fw_var < 0x500) { 3648c2ecf20Sopenharmony_ci setup->v1.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); 3658c2ecf20Sopenharmony_ci memset(setup->v1.rts_rates, 0, 8); 3668c2ecf20Sopenharmony_ci setup->v1.rx_addr = cpu_to_le32(priv->rx_end); 3678c2ecf20Sopenharmony_ci setup->v1.max_rx = cpu_to_le16(priv->rx_mtu); 3688c2ecf20Sopenharmony_ci setup->v1.rxhw = cpu_to_le16(priv->rxhw); 3698c2ecf20Sopenharmony_ci setup->v1.wakeup_timer = cpu_to_le16(priv->wakeup_timer); 3708c2ecf20Sopenharmony_ci setup->v1.unalloc0 = cpu_to_le16(0); 3718c2ecf20Sopenharmony_ci } else { 3728c2ecf20Sopenharmony_ci setup->v2.rx_addr = cpu_to_le32(priv->rx_end); 3738c2ecf20Sopenharmony_ci setup->v2.max_rx = cpu_to_le16(priv->rx_mtu); 3748c2ecf20Sopenharmony_ci setup->v2.rxhw = cpu_to_le16(priv->rxhw); 3758c2ecf20Sopenharmony_ci setup->v2.timer = cpu_to_le16(priv->wakeup_timer); 3768c2ecf20Sopenharmony_ci setup->v2.truncate = cpu_to_le16(48896); 3778c2ecf20Sopenharmony_ci setup->v2.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); 3788c2ecf20Sopenharmony_ci setup->v2.sbss_offset = 0; 3798c2ecf20Sopenharmony_ci setup->v2.mcast_window = 0; 3808c2ecf20Sopenharmony_ci setup->v2.rx_rssi_threshold = 0; 3818c2ecf20Sopenharmony_ci setup->v2.rx_ed_threshold = 0; 3828c2ecf20Sopenharmony_ci setup->v2.ref_clock = cpu_to_le32(644245094); 3838c2ecf20Sopenharmony_ci setup->v2.lpf_bandwidth = cpu_to_le16(65535); 3848c2ecf20Sopenharmony_ci setup->v2.osc_start_delay = cpu_to_le16(65535); 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci p54_tx(priv, skb); 3878c2ecf20Sopenharmony_ci priv->phy_idle = mode == P54_FILTER_TYPE_HIBERNATE; 3888c2ecf20Sopenharmony_ci return 0; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ciint p54_scan(struct p54_common *priv, u16 mode, u16 dwell) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct sk_buff *skb; 3948c2ecf20Sopenharmony_ci struct p54_hdr *hdr; 3958c2ecf20Sopenharmony_ci struct p54_scan_head *head; 3968c2ecf20Sopenharmony_ci struct p54_iq_autocal_entry *iq_autocal; 3978c2ecf20Sopenharmony_ci union p54_scan_body_union *body; 3988c2ecf20Sopenharmony_ci struct p54_scan_tail_rate *rate; 3998c2ecf20Sopenharmony_ci struct pda_rssi_cal_entry *rssi; 4008c2ecf20Sopenharmony_ci struct p54_rssi_db_entry *rssi_data; 4018c2ecf20Sopenharmony_ci unsigned int i; 4028c2ecf20Sopenharmony_ci void *entry; 4038c2ecf20Sopenharmony_ci __le16 freq = cpu_to_le16(priv->hw->conf.chandef.chan->center_freq); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*head) + 4068c2ecf20Sopenharmony_ci 2 + sizeof(*iq_autocal) + sizeof(*body) + 4078c2ecf20Sopenharmony_ci sizeof(*rate) + 2 * sizeof(*rssi), 4088c2ecf20Sopenharmony_ci P54_CONTROL_TYPE_SCAN, GFP_ATOMIC); 4098c2ecf20Sopenharmony_ci if (!skb) 4108c2ecf20Sopenharmony_ci return -ENOMEM; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci head = skb_put(skb, sizeof(*head)); 4138c2ecf20Sopenharmony_ci memset(head->scan_params, 0, sizeof(head->scan_params)); 4148c2ecf20Sopenharmony_ci head->mode = cpu_to_le16(mode); 4158c2ecf20Sopenharmony_ci head->dwell = cpu_to_le16(dwell); 4168c2ecf20Sopenharmony_ci head->freq = freq; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { 4198c2ecf20Sopenharmony_ci __le16 *pa_power_points = skb_put(skb, 2); 4208c2ecf20Sopenharmony_ci *pa_power_points = cpu_to_le16(0x0c); 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci iq_autocal = skb_put(skb, sizeof(*iq_autocal)); 4248c2ecf20Sopenharmony_ci for (i = 0; i < priv->iq_autocal_len; i++) { 4258c2ecf20Sopenharmony_ci if (priv->iq_autocal[i].freq != freq) 4268c2ecf20Sopenharmony_ci continue; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci memcpy(iq_autocal, &priv->iq_autocal[i].params, 4298c2ecf20Sopenharmony_ci sizeof(struct p54_iq_autocal_entry)); 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci if (i == priv->iq_autocal_len) 4338c2ecf20Sopenharmony_ci goto err; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) 4368c2ecf20Sopenharmony_ci body = skb_put(skb, sizeof(body->longbow)); 4378c2ecf20Sopenharmony_ci else 4388c2ecf20Sopenharmony_ci body = skb_put(skb, sizeof(body->normal)); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci for (i = 0; i < priv->output_limit->entries; i++) { 4418c2ecf20Sopenharmony_ci __le16 *entry_freq = (void *) (priv->output_limit->data + 4428c2ecf20Sopenharmony_ci priv->output_limit->entry_size * i); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (*entry_freq != freq) 4458c2ecf20Sopenharmony_ci continue; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { 4488c2ecf20Sopenharmony_ci memcpy(&body->longbow.power_limits, 4498c2ecf20Sopenharmony_ci (void *) entry_freq + sizeof(__le16), 4508c2ecf20Sopenharmony_ci priv->output_limit->entry_size); 4518c2ecf20Sopenharmony_ci } else { 4528c2ecf20Sopenharmony_ci struct pda_channel_output_limit *limits = 4538c2ecf20Sopenharmony_ci (void *) entry_freq; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci body->normal.val_barker = 0x38; 4568c2ecf20Sopenharmony_ci body->normal.val_bpsk = body->normal.dup_bpsk = 4578c2ecf20Sopenharmony_ci limits->val_bpsk; 4588c2ecf20Sopenharmony_ci body->normal.val_qpsk = body->normal.dup_qpsk = 4598c2ecf20Sopenharmony_ci limits->val_qpsk; 4608c2ecf20Sopenharmony_ci body->normal.val_16qam = body->normal.dup_16qam = 4618c2ecf20Sopenharmony_ci limits->val_16qam; 4628c2ecf20Sopenharmony_ci body->normal.val_64qam = body->normal.dup_64qam = 4638c2ecf20Sopenharmony_ci limits->val_64qam; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci break; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci if (i == priv->output_limit->entries) 4688c2ecf20Sopenharmony_ci goto err; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci entry = (void *)(priv->curve_data->data + priv->curve_data->offset); 4718c2ecf20Sopenharmony_ci for (i = 0; i < priv->curve_data->entries; i++) { 4728c2ecf20Sopenharmony_ci if (*((__le16 *)entry) != freq) { 4738c2ecf20Sopenharmony_ci entry += priv->curve_data->entry_size; 4748c2ecf20Sopenharmony_ci continue; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { 4788c2ecf20Sopenharmony_ci memcpy(&body->longbow.curve_data, 4798c2ecf20Sopenharmony_ci entry + sizeof(__le16), 4808c2ecf20Sopenharmony_ci priv->curve_data->entry_size); 4818c2ecf20Sopenharmony_ci } else { 4828c2ecf20Sopenharmony_ci struct p54_scan_body *chan = &body->normal; 4838c2ecf20Sopenharmony_ci struct pda_pa_curve_data *curve_data = 4848c2ecf20Sopenharmony_ci (void *) priv->curve_data->data; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci entry += sizeof(__le16); 4878c2ecf20Sopenharmony_ci chan->pa_points_per_curve = 8; 4888c2ecf20Sopenharmony_ci memset(chan->curve_data, 0, sizeof(chan->curve_data)); 4898c2ecf20Sopenharmony_ci memcpy(chan->curve_data, entry, 4908c2ecf20Sopenharmony_ci sizeof(struct p54_pa_curve_data_sample) * 4918c2ecf20Sopenharmony_ci min((u8)8, curve_data->points_per_channel)); 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci break; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci if (i == priv->curve_data->entries) 4968c2ecf20Sopenharmony_ci goto err; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if ((priv->fw_var >= 0x500) && (priv->fw_var < 0x509)) { 4998c2ecf20Sopenharmony_ci rate = skb_put(skb, sizeof(*rate)); 5008c2ecf20Sopenharmony_ci rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); 5018c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(rate->rts_rates); i++) 5028c2ecf20Sopenharmony_ci rate->rts_rates[i] = i; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci rssi = skb_put(skb, sizeof(*rssi)); 5068c2ecf20Sopenharmony_ci rssi_data = p54_rssi_find(priv, le16_to_cpu(freq)); 5078c2ecf20Sopenharmony_ci rssi->mul = cpu_to_le16(rssi_data->mul); 5088c2ecf20Sopenharmony_ci rssi->add = cpu_to_le16(rssi_data->add); 5098c2ecf20Sopenharmony_ci if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { 5108c2ecf20Sopenharmony_ci /* Longbow frontend needs ever more */ 5118c2ecf20Sopenharmony_ci rssi = skb_put(skb, sizeof(*rssi)); 5128c2ecf20Sopenharmony_ci rssi->mul = cpu_to_le16(rssi_data->longbow_unkn); 5138c2ecf20Sopenharmony_ci rssi->add = cpu_to_le16(rssi_data->longbow_unk2); 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (priv->fw_var >= 0x509) { 5178c2ecf20Sopenharmony_ci rate = skb_put(skb, sizeof(*rate)); 5188c2ecf20Sopenharmony_ci rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); 5198c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(rate->rts_rates); i++) 5208c2ecf20Sopenharmony_ci rate->rts_rates[i] = i; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci hdr = (struct p54_hdr *) skb->data; 5248c2ecf20Sopenharmony_ci hdr->len = cpu_to_le16(skb->len - sizeof(*hdr)); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci p54_tx(priv, skb); 5278c2ecf20Sopenharmony_ci priv->cur_rssi = rssi_data; 5288c2ecf20Sopenharmony_ci return 0; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cierr: 5318c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, "frequency change to channel %d failed.\n", 5328c2ecf20Sopenharmony_ci ieee80211_frequency_to_channel( 5338c2ecf20Sopenharmony_ci priv->hw->conf.chandef.chan->center_freq)); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 5368c2ecf20Sopenharmony_ci return -EINVAL; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ciint p54_set_leds(struct p54_common *priv) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct sk_buff *skb; 5428c2ecf20Sopenharmony_ci struct p54_led *led; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*led), 5458c2ecf20Sopenharmony_ci P54_CONTROL_TYPE_LED, GFP_ATOMIC); 5468c2ecf20Sopenharmony_ci if (unlikely(!skb)) 5478c2ecf20Sopenharmony_ci return -ENOMEM; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci led = skb_put(skb, sizeof(*led)); 5508c2ecf20Sopenharmony_ci led->flags = cpu_to_le16(0x0003); 5518c2ecf20Sopenharmony_ci led->mask[0] = led->mask[1] = cpu_to_le16(priv->softled_state); 5528c2ecf20Sopenharmony_ci led->delay[0] = cpu_to_le16(1); 5538c2ecf20Sopenharmony_ci led->delay[1] = cpu_to_le16(0); 5548c2ecf20Sopenharmony_ci p54_tx(priv, skb); 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ciint p54_set_edcf(struct p54_common *priv) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci struct sk_buff *skb; 5618c2ecf20Sopenharmony_ci struct p54_edcf *edcf; 5628c2ecf20Sopenharmony_ci u8 rtd; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*edcf), 5658c2ecf20Sopenharmony_ci P54_CONTROL_TYPE_DCFINIT, GFP_ATOMIC); 5668c2ecf20Sopenharmony_ci if (unlikely(!skb)) 5678c2ecf20Sopenharmony_ci return -ENOMEM; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci edcf = skb_put(skb, sizeof(*edcf)); 5708c2ecf20Sopenharmony_ci if (priv->use_short_slot) { 5718c2ecf20Sopenharmony_ci edcf->slottime = 9; 5728c2ecf20Sopenharmony_ci edcf->sifs = 0x10; 5738c2ecf20Sopenharmony_ci edcf->eofpad = 0x00; 5748c2ecf20Sopenharmony_ci } else { 5758c2ecf20Sopenharmony_ci edcf->slottime = 20; 5768c2ecf20Sopenharmony_ci edcf->sifs = 0x0a; 5778c2ecf20Sopenharmony_ci edcf->eofpad = 0x06; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci /* 5808c2ecf20Sopenharmony_ci * calculate the extra round trip delay according to the 5818c2ecf20Sopenharmony_ci * formula from 802.11-2007 17.3.8.6. 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_ci rtd = 3 * priv->coverage_class; 5848c2ecf20Sopenharmony_ci edcf->slottime += rtd; 5858c2ecf20Sopenharmony_ci edcf->round_trip_delay = cpu_to_le16(rtd); 5868c2ecf20Sopenharmony_ci /* (see prism54/isl_oid.h for further details) */ 5878c2ecf20Sopenharmony_ci edcf->frameburst = cpu_to_le16(0); 5888c2ecf20Sopenharmony_ci edcf->flags = 0; 5898c2ecf20Sopenharmony_ci memset(edcf->mapping, 0, sizeof(edcf->mapping)); 5908c2ecf20Sopenharmony_ci memcpy(edcf->queue, priv->qos_params, sizeof(edcf->queue)); 5918c2ecf20Sopenharmony_ci p54_tx(priv, skb); 5928c2ecf20Sopenharmony_ci return 0; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ciint p54_set_ps(struct p54_common *priv) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci struct sk_buff *skb; 5988c2ecf20Sopenharmony_ci struct p54_psm *psm; 5998c2ecf20Sopenharmony_ci unsigned int i; 6008c2ecf20Sopenharmony_ci u16 mode; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (priv->hw->conf.flags & IEEE80211_CONF_PS && 6038c2ecf20Sopenharmony_ci !priv->powersave_override) 6048c2ecf20Sopenharmony_ci mode = P54_PSM | P54_PSM_BEACON_TIMEOUT | P54_PSM_DTIM | 6058c2ecf20Sopenharmony_ci P54_PSM_CHECKSUM | P54_PSM_MCBC; 6068c2ecf20Sopenharmony_ci else 6078c2ecf20Sopenharmony_ci mode = P54_PSM_CAM; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*psm), 6108c2ecf20Sopenharmony_ci P54_CONTROL_TYPE_PSM, GFP_ATOMIC); 6118c2ecf20Sopenharmony_ci if (!skb) 6128c2ecf20Sopenharmony_ci return -ENOMEM; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci psm = skb_put(skb, sizeof(*psm)); 6158c2ecf20Sopenharmony_ci psm->mode = cpu_to_le16(mode); 6168c2ecf20Sopenharmony_ci psm->aid = cpu_to_le16(priv->aid); 6178c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(psm->intervals); i++) { 6188c2ecf20Sopenharmony_ci psm->intervals[i].interval = 6198c2ecf20Sopenharmony_ci cpu_to_le16(priv->hw->conf.listen_interval); 6208c2ecf20Sopenharmony_ci psm->intervals[i].periods = cpu_to_le16(1); 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci psm->beacon_rssi_skip_max = 200; 6248c2ecf20Sopenharmony_ci psm->rssi_delta_threshold = 0; 6258c2ecf20Sopenharmony_ci psm->nr = 1; 6268c2ecf20Sopenharmony_ci psm->exclude[0] = WLAN_EID_TIM; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci p54_tx(priv, skb); 6298c2ecf20Sopenharmony_ci priv->phy_ps = mode != P54_PSM_CAM; 6308c2ecf20Sopenharmony_ci return 0; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ciint p54_init_xbow_synth(struct p54_common *priv) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci struct sk_buff *skb; 6368c2ecf20Sopenharmony_ci struct p54_xbow_synth *xbow; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*xbow), 6398c2ecf20Sopenharmony_ci P54_CONTROL_TYPE_XBOW_SYNTH_CFG, GFP_KERNEL); 6408c2ecf20Sopenharmony_ci if (unlikely(!skb)) 6418c2ecf20Sopenharmony_ci return -ENOMEM; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci xbow = skb_put(skb, sizeof(*xbow)); 6448c2ecf20Sopenharmony_ci xbow->magic1 = cpu_to_le16(0x1); 6458c2ecf20Sopenharmony_ci xbow->magic2 = cpu_to_le16(0x2); 6468c2ecf20Sopenharmony_ci xbow->freq = cpu_to_le16(5390); 6478c2ecf20Sopenharmony_ci memset(xbow->padding, 0, sizeof(xbow->padding)); 6488c2ecf20Sopenharmony_ci p54_tx(priv, skb); 6498c2ecf20Sopenharmony_ci return 0; 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ciint p54_upload_key(struct p54_common *priv, u8 algo, int slot, u8 idx, u8 len, 6538c2ecf20Sopenharmony_ci u8 *addr, u8* key) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct sk_buff *skb; 6568c2ecf20Sopenharmony_ci struct p54_keycache *rxkey; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey), 6598c2ecf20Sopenharmony_ci P54_CONTROL_TYPE_RX_KEYCACHE, GFP_KERNEL); 6608c2ecf20Sopenharmony_ci if (unlikely(!skb)) 6618c2ecf20Sopenharmony_ci return -ENOMEM; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci rxkey = skb_put(skb, sizeof(*rxkey)); 6648c2ecf20Sopenharmony_ci rxkey->entry = slot; 6658c2ecf20Sopenharmony_ci rxkey->key_id = idx; 6668c2ecf20Sopenharmony_ci rxkey->key_type = algo; 6678c2ecf20Sopenharmony_ci if (addr) 6688c2ecf20Sopenharmony_ci memcpy(rxkey->mac, addr, ETH_ALEN); 6698c2ecf20Sopenharmony_ci else 6708c2ecf20Sopenharmony_ci eth_broadcast_addr(rxkey->mac); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci switch (algo) { 6738c2ecf20Sopenharmony_ci case P54_CRYPTO_WEP: 6748c2ecf20Sopenharmony_ci case P54_CRYPTO_AESCCMP: 6758c2ecf20Sopenharmony_ci rxkey->key_len = min_t(u8, 16, len); 6768c2ecf20Sopenharmony_ci memcpy(rxkey->key, key, rxkey->key_len); 6778c2ecf20Sopenharmony_ci break; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci case P54_CRYPTO_TKIPMICHAEL: 6808c2ecf20Sopenharmony_ci rxkey->key_len = 24; 6818c2ecf20Sopenharmony_ci memcpy(rxkey->key, key, 16); 6828c2ecf20Sopenharmony_ci memcpy(&(rxkey->key[16]), &(key 6838c2ecf20Sopenharmony_ci [NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]), 8); 6848c2ecf20Sopenharmony_ci break; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci case P54_CRYPTO_NONE: 6878c2ecf20Sopenharmony_ci rxkey->key_len = 0; 6888c2ecf20Sopenharmony_ci memset(rxkey->key, 0, sizeof(rxkey->key)); 6898c2ecf20Sopenharmony_ci break; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci default: 6928c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 6938c2ecf20Sopenharmony_ci "invalid cryptographic algorithm: %d\n", algo); 6948c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 6958c2ecf20Sopenharmony_ci return -EINVAL; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci p54_tx(priv, skb); 6998c2ecf20Sopenharmony_ci return 0; 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ciint p54_fetch_statistics(struct p54_common *priv) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci struct ieee80211_tx_info *txinfo; 7058c2ecf20Sopenharmony_ci struct p54_tx_info *p54info; 7068c2ecf20Sopenharmony_ci struct sk_buff *skb; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL, 7098c2ecf20Sopenharmony_ci sizeof(struct p54_statistics), 7108c2ecf20Sopenharmony_ci P54_CONTROL_TYPE_STAT_READBACK, GFP_KERNEL); 7118c2ecf20Sopenharmony_ci if (!skb) 7128c2ecf20Sopenharmony_ci return -ENOMEM; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci /* 7158c2ecf20Sopenharmony_ci * The statistic feedback causes some extra headaches here, if it 7168c2ecf20Sopenharmony_ci * is not to crash/corrupt the firmware data structures. 7178c2ecf20Sopenharmony_ci * 7188c2ecf20Sopenharmony_ci * Unlike all other Control Get OIDs we can not use helpers like 7198c2ecf20Sopenharmony_ci * skb_put to reserve the space for the data we're requesting. 7208c2ecf20Sopenharmony_ci * Instead the extra frame length -which will hold the results later- 7218c2ecf20Sopenharmony_ci * will only be told to the p54_assign_address, so that following 7228c2ecf20Sopenharmony_ci * frames won't be placed into the allegedly empty area. 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_ci txinfo = IEEE80211_SKB_CB(skb); 7258c2ecf20Sopenharmony_ci p54info = (void *) txinfo->rate_driver_data; 7268c2ecf20Sopenharmony_ci p54info->extra_len = sizeof(struct p54_statistics); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci p54_tx(priv, skb); 7298c2ecf20Sopenharmony_ci return 0; 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ciint p54_set_groupfilter(struct p54_common *priv) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci struct p54_group_address_table *grp; 7358c2ecf20Sopenharmony_ci struct sk_buff *skb; 7368c2ecf20Sopenharmony_ci bool on = false; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*grp), 7398c2ecf20Sopenharmony_ci P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE, GFP_KERNEL); 7408c2ecf20Sopenharmony_ci if (!skb) 7418c2ecf20Sopenharmony_ci return -ENOMEM; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci grp = skb_put(skb, sizeof(*grp)); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci on = !(priv->filter_flags & FIF_ALLMULTI) && 7468c2ecf20Sopenharmony_ci (priv->mc_maclist_num > 0 && 7478c2ecf20Sopenharmony_ci priv->mc_maclist_num <= MC_FILTER_ADDRESS_NUM); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci if (on) { 7508c2ecf20Sopenharmony_ci grp->filter_enable = cpu_to_le16(1); 7518c2ecf20Sopenharmony_ci grp->num_address = cpu_to_le16(priv->mc_maclist_num); 7528c2ecf20Sopenharmony_ci memcpy(grp->mac_list, priv->mc_maclist, sizeof(grp->mac_list)); 7538c2ecf20Sopenharmony_ci } else { 7548c2ecf20Sopenharmony_ci grp->filter_enable = cpu_to_le16(0); 7558c2ecf20Sopenharmony_ci grp->num_address = cpu_to_le16(0); 7568c2ecf20Sopenharmony_ci memset(grp->mac_list, 0, sizeof(grp->mac_list)); 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci p54_tx(priv, skb); 7608c2ecf20Sopenharmony_ci return 0; 7618c2ecf20Sopenharmony_ci} 762