162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Firmware I/O code for mac80211 Prism54 drivers 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> 662306a36Sopenharmony_ci * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de> 762306a36Sopenharmony_ci * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Based on: 1062306a36Sopenharmony_ci * - the islsm (softmac prism54) driver, which is: 1162306a36Sopenharmony_ci * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. 1262306a36Sopenharmony_ci * - stlc45xx driver 1362306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/firmware.h> 1862306a36Sopenharmony_ci#include <linux/etherdevice.h> 1962306a36Sopenharmony_ci#include <linux/export.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <net/mac80211.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "p54.h" 2462306a36Sopenharmony_ci#include "eeprom.h" 2562306a36Sopenharmony_ci#include "lmac.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciint p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct p54_common *priv = dev->priv; 3062306a36Sopenharmony_ci struct exp_if *exp_if; 3162306a36Sopenharmony_ci struct bootrec *bootrec; 3262306a36Sopenharmony_ci u32 *data = (u32 *)fw->data; 3362306a36Sopenharmony_ci u32 *end_data = (u32 *)fw->data + (fw->size >> 2); 3462306a36Sopenharmony_ci u8 *fw_version = NULL; 3562306a36Sopenharmony_ci size_t len; 3662306a36Sopenharmony_ci int i; 3762306a36Sopenharmony_ci int maxlen; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci if (priv->rx_start) 4062306a36Sopenharmony_ci return 0; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci while (data < end_data && *data) 4362306a36Sopenharmony_ci data++; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci while (data < end_data && !*data) 4662306a36Sopenharmony_ci data++; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci bootrec = (struct bootrec *) data; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci while (bootrec->data <= end_data && (bootrec->data + 5162306a36Sopenharmony_ci (len = le32_to_cpu(bootrec->len))) <= end_data) { 5262306a36Sopenharmony_ci u32 code = le32_to_cpu(bootrec->code); 5362306a36Sopenharmony_ci switch (code) { 5462306a36Sopenharmony_ci case BR_CODE_COMPONENT_ID: 5562306a36Sopenharmony_ci priv->fw_interface = be32_to_cpup((__be32 *) 5662306a36Sopenharmony_ci bootrec->data); 5762306a36Sopenharmony_ci switch (priv->fw_interface) { 5862306a36Sopenharmony_ci case FW_LM86: 5962306a36Sopenharmony_ci case FW_LM20: 6062306a36Sopenharmony_ci case FW_LM87: { 6162306a36Sopenharmony_ci char *iftype = (char *)bootrec->data; 6262306a36Sopenharmony_ci wiphy_info(priv->hw->wiphy, 6362306a36Sopenharmony_ci "p54 detected a LM%c%c firmware\n", 6462306a36Sopenharmony_ci iftype[2], iftype[3]); 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci case FW_FMAC: 6862306a36Sopenharmony_ci default: 6962306a36Sopenharmony_ci wiphy_err(priv->hw->wiphy, 7062306a36Sopenharmony_ci "unsupported firmware\n"); 7162306a36Sopenharmony_ci return -ENODEV; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci break; 7462306a36Sopenharmony_ci case BR_CODE_COMPONENT_VERSION: 7562306a36Sopenharmony_ci /* 24 bytes should be enough for all firmwares */ 7662306a36Sopenharmony_ci if (strnlen((unsigned char *) bootrec->data, 24) < 24) 7762306a36Sopenharmony_ci fw_version = (unsigned char *) bootrec->data; 7862306a36Sopenharmony_ci break; 7962306a36Sopenharmony_ci case BR_CODE_DESCR: { 8062306a36Sopenharmony_ci struct bootrec_desc *desc = 8162306a36Sopenharmony_ci (struct bootrec_desc *)bootrec->data; 8262306a36Sopenharmony_ci priv->rx_start = le32_to_cpu(desc->rx_start); 8362306a36Sopenharmony_ci /* FIXME add sanity checking */ 8462306a36Sopenharmony_ci priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500; 8562306a36Sopenharmony_ci priv->headroom = desc->headroom; 8662306a36Sopenharmony_ci priv->tailroom = desc->tailroom; 8762306a36Sopenharmony_ci priv->privacy_caps = desc->privacy_caps; 8862306a36Sopenharmony_ci priv->rx_keycache_size = desc->rx_keycache_size; 8962306a36Sopenharmony_ci if (le32_to_cpu(bootrec->len) == 11) 9062306a36Sopenharmony_ci priv->rx_mtu = le16_to_cpu(desc->rx_mtu); 9162306a36Sopenharmony_ci else 9262306a36Sopenharmony_ci priv->rx_mtu = (size_t) 9362306a36Sopenharmony_ci 0x620 - priv->tx_hdr_len; 9462306a36Sopenharmony_ci maxlen = priv->tx_hdr_len + /* USB devices */ 9562306a36Sopenharmony_ci sizeof(struct p54_rx_data) + 9662306a36Sopenharmony_ci 4 + /* rx alignment */ 9762306a36Sopenharmony_ci IEEE80211_MAX_FRAG_THRESHOLD; 9862306a36Sopenharmony_ci if (priv->rx_mtu > maxlen && PAGE_SIZE == 4096) { 9962306a36Sopenharmony_ci printk(KERN_INFO "p54: rx_mtu reduced from %d " 10062306a36Sopenharmony_ci "to %d\n", priv->rx_mtu, maxlen); 10162306a36Sopenharmony_ci priv->rx_mtu = maxlen; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci case BR_CODE_EXPOSED_IF: 10662306a36Sopenharmony_ci exp_if = (struct exp_if *) bootrec->data; 10762306a36Sopenharmony_ci for (i = 0; i < (len * sizeof(*exp_if) / 4); i++) 10862306a36Sopenharmony_ci if (exp_if[i].if_id == cpu_to_le16(IF_ID_LMAC)) 10962306a36Sopenharmony_ci priv->fw_var = le16_to_cpu(exp_if[i].variant); 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci case BR_CODE_DEPENDENT_IF: 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci case BR_CODE_END_OF_BRA: 11462306a36Sopenharmony_ci case LEGACY_BR_CODE_END_OF_BRA: 11562306a36Sopenharmony_ci end_data = NULL; 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci default: 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci bootrec = (struct bootrec *)&bootrec->data[len]; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (fw_version) { 12462306a36Sopenharmony_ci wiphy_info(priv->hw->wiphy, 12562306a36Sopenharmony_ci "FW rev %s - Softmac protocol %x.%x\n", 12662306a36Sopenharmony_ci fw_version, priv->fw_var >> 8, priv->fw_var & 0xff); 12762306a36Sopenharmony_ci snprintf(dev->wiphy->fw_version, sizeof(dev->wiphy->fw_version), 12862306a36Sopenharmony_ci "%s - %x.%x", fw_version, 12962306a36Sopenharmony_ci priv->fw_var >> 8, priv->fw_var & 0xff); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (priv->fw_var < 0x500) 13362306a36Sopenharmony_ci wiphy_info(priv->hw->wiphy, 13462306a36Sopenharmony_ci "you are using an obsolete firmware. " 13562306a36Sopenharmony_ci "visit http://wireless.wiki.kernel.org/en/users/Drivers/p54 " 13662306a36Sopenharmony_ci "and grab one for \"kernel >= 2.6.28\"!\n"); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (priv->fw_var >= 0x300) { 13962306a36Sopenharmony_ci /* Firmware supports QoS, use it! */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (priv->fw_var >= 0x500) { 14262306a36Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_VO].limit = 16; 14362306a36Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_VI].limit = 16; 14462306a36Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_BE].limit = 16; 14562306a36Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_BK].limit = 16; 14662306a36Sopenharmony_ci } else { 14762306a36Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_VO].limit = 3; 14862306a36Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_VI].limit = 4; 14962306a36Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_BE].limit = 3; 15062306a36Sopenharmony_ci priv->tx_stats[P54_QUEUE_AC_BK].limit = 2; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci priv->hw->queues = P54_QUEUE_AC_NUM; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci wiphy_info(priv->hw->wiphy, 15662306a36Sopenharmony_ci "cryptographic accelerator WEP:%s, TKIP:%s, CCMP:%s\n", 15762306a36Sopenharmony_ci (priv->privacy_caps & BR_DESC_PRIV_CAP_WEP) ? "YES" : "no", 15862306a36Sopenharmony_ci (priv->privacy_caps & 15962306a36Sopenharmony_ci (BR_DESC_PRIV_CAP_TKIP | BR_DESC_PRIV_CAP_MICHAEL)) 16062306a36Sopenharmony_ci ? "YES" : "no", 16162306a36Sopenharmony_ci (priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP) 16262306a36Sopenharmony_ci ? "YES" : "no"); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (priv->rx_keycache_size) { 16562306a36Sopenharmony_ci /* 16662306a36Sopenharmony_ci * NOTE: 16762306a36Sopenharmony_ci * 16862306a36Sopenharmony_ci * The firmware provides at most 255 (0 - 254) slots 16962306a36Sopenharmony_ci * for keys which are then used to offload decryption. 17062306a36Sopenharmony_ci * As a result the 255 entry (aka 0xff) can be used 17162306a36Sopenharmony_ci * safely by the driver to mark keys that didn't fit 17262306a36Sopenharmony_ci * into the full cache. This trick saves us from 17362306a36Sopenharmony_ci * keeping a extra list for uploaded keys. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci priv->used_rxkeys = bitmap_zalloc(priv->rx_keycache_size, 17762306a36Sopenharmony_ci GFP_KERNEL); 17862306a36Sopenharmony_ci if (!priv->used_rxkeys) 17962306a36Sopenharmony_ci return -ENOMEM; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(p54_parse_firmware); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic struct sk_buff *p54_alloc_skb(struct p54_common *priv, u16 hdr_flags, 18762306a36Sopenharmony_ci u16 payload_len, u16 type, gfp_t memflags) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct p54_hdr *hdr; 19062306a36Sopenharmony_ci struct sk_buff *skb; 19162306a36Sopenharmony_ci size_t frame_len = sizeof(*hdr) + payload_len; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (frame_len > P54_MAX_CTRL_FRAME_LEN) 19462306a36Sopenharmony_ci return NULL; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (unlikely(skb_queue_len(&priv->tx_pending) > 64)) 19762306a36Sopenharmony_ci return NULL; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci skb = __dev_alloc_skb(priv->tx_hdr_len + frame_len, memflags); 20062306a36Sopenharmony_ci if (!skb) 20162306a36Sopenharmony_ci return NULL; 20262306a36Sopenharmony_ci skb_reserve(skb, priv->tx_hdr_len); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci hdr = skb_put(skb, sizeof(*hdr)); 20562306a36Sopenharmony_ci hdr->flags = cpu_to_le16(hdr_flags); 20662306a36Sopenharmony_ci hdr->len = cpu_to_le16(payload_len); 20762306a36Sopenharmony_ci hdr->type = cpu_to_le16(type); 20862306a36Sopenharmony_ci hdr->tries = hdr->rts_tries = 0; 20962306a36Sopenharmony_ci return skb; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ciint p54_download_eeprom(struct p54_common *priv, void *buf, 21362306a36Sopenharmony_ci u16 offset, u16 len) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct p54_eeprom_lm86 *eeprom_hdr; 21662306a36Sopenharmony_ci struct sk_buff *skb; 21762306a36Sopenharmony_ci size_t eeprom_hdr_size; 21862306a36Sopenharmony_ci int ret = 0; 21962306a36Sopenharmony_ci long timeout; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (priv->fw_var >= 0x509) 22262306a36Sopenharmony_ci eeprom_hdr_size = sizeof(*eeprom_hdr); 22362306a36Sopenharmony_ci else 22462306a36Sopenharmony_ci eeprom_hdr_size = 0x4; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL, eeprom_hdr_size + 22762306a36Sopenharmony_ci len, P54_CONTROL_TYPE_EEPROM_READBACK, 22862306a36Sopenharmony_ci GFP_KERNEL); 22962306a36Sopenharmony_ci if (unlikely(!skb)) 23062306a36Sopenharmony_ci return -ENOMEM; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci mutex_lock(&priv->eeprom_mutex); 23362306a36Sopenharmony_ci priv->eeprom = buf; 23462306a36Sopenharmony_ci eeprom_hdr = skb_put(skb, eeprom_hdr_size + len); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (priv->fw_var < 0x509) { 23762306a36Sopenharmony_ci eeprom_hdr->v1.offset = cpu_to_le16(offset); 23862306a36Sopenharmony_ci eeprom_hdr->v1.len = cpu_to_le16(len); 23962306a36Sopenharmony_ci } else { 24062306a36Sopenharmony_ci eeprom_hdr->v2.offset = cpu_to_le32(offset); 24162306a36Sopenharmony_ci eeprom_hdr->v2.len = cpu_to_le16(len); 24262306a36Sopenharmony_ci eeprom_hdr->v2.magic2 = 0xf; 24362306a36Sopenharmony_ci memcpy(eeprom_hdr->v2.magic, (const char *)"LOCK", 4); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci p54_tx(priv, skb); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci timeout = wait_for_completion_interruptible_timeout( 24962306a36Sopenharmony_ci &priv->eeprom_comp, HZ); 25062306a36Sopenharmony_ci if (timeout <= 0) { 25162306a36Sopenharmony_ci wiphy_err(priv->hw->wiphy, 25262306a36Sopenharmony_ci "device does not respond or signal received!\n"); 25362306a36Sopenharmony_ci ret = -EBUSY; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci priv->eeprom = NULL; 25662306a36Sopenharmony_ci mutex_unlock(&priv->eeprom_mutex); 25762306a36Sopenharmony_ci return ret; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ciint p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct sk_buff *skb; 26362306a36Sopenharmony_ci struct p54_tim *tim; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*tim), 26662306a36Sopenharmony_ci P54_CONTROL_TYPE_TIM, GFP_ATOMIC); 26762306a36Sopenharmony_ci if (unlikely(!skb)) 26862306a36Sopenharmony_ci return -ENOMEM; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci tim = skb_put(skb, sizeof(*tim)); 27162306a36Sopenharmony_ci tim->count = 1; 27262306a36Sopenharmony_ci tim->entry[0] = cpu_to_le16(set ? (aid | 0x8000) : aid); 27362306a36Sopenharmony_ci p54_tx(priv, skb); 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ciint p54_sta_unlock(struct p54_common *priv, u8 *addr) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct sk_buff *skb; 28062306a36Sopenharmony_ci struct p54_sta_unlock *sta; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*sta), 28362306a36Sopenharmony_ci P54_CONTROL_TYPE_PSM_STA_UNLOCK, GFP_ATOMIC); 28462306a36Sopenharmony_ci if (unlikely(!skb)) 28562306a36Sopenharmony_ci return -ENOMEM; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci sta = skb_put(skb, sizeof(*sta)); 28862306a36Sopenharmony_ci memcpy(sta->addr, addr, ETH_ALEN); 28962306a36Sopenharmony_ci p54_tx(priv, skb); 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ciint p54_tx_cancel(struct p54_common *priv, __le32 req_id) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct sk_buff *skb; 29662306a36Sopenharmony_ci struct p54_txcancel *cancel; 29762306a36Sopenharmony_ci u32 _req_id = le32_to_cpu(req_id); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (unlikely(_req_id < priv->rx_start || _req_id > priv->rx_end)) 30062306a36Sopenharmony_ci return -EINVAL; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*cancel), 30362306a36Sopenharmony_ci P54_CONTROL_TYPE_TXCANCEL, GFP_ATOMIC); 30462306a36Sopenharmony_ci if (unlikely(!skb)) 30562306a36Sopenharmony_ci return -ENOMEM; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci cancel = skb_put(skb, sizeof(*cancel)); 30862306a36Sopenharmony_ci cancel->req_id = req_id; 30962306a36Sopenharmony_ci p54_tx(priv, skb); 31062306a36Sopenharmony_ci return 0; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ciint p54_setup_mac(struct p54_common *priv) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct sk_buff *skb; 31662306a36Sopenharmony_ci struct p54_setup_mac *setup; 31762306a36Sopenharmony_ci u16 mode; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*setup), 32062306a36Sopenharmony_ci P54_CONTROL_TYPE_SETUP, GFP_ATOMIC); 32162306a36Sopenharmony_ci if (!skb) 32262306a36Sopenharmony_ci return -ENOMEM; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci setup = skb_put(skb, sizeof(*setup)); 32562306a36Sopenharmony_ci if (!(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) { 32662306a36Sopenharmony_ci switch (priv->mode) { 32762306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 32862306a36Sopenharmony_ci mode = P54_FILTER_TYPE_STATION; 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 33162306a36Sopenharmony_ci mode = P54_FILTER_TYPE_AP; 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 33462306a36Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 33562306a36Sopenharmony_ci mode = P54_FILTER_TYPE_IBSS; 33662306a36Sopenharmony_ci break; 33762306a36Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 33862306a36Sopenharmony_ci mode = P54_FILTER_TYPE_PROMISCUOUS; 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci default: 34162306a36Sopenharmony_ci mode = P54_FILTER_TYPE_HIBERNATE; 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* 34662306a36Sopenharmony_ci * "TRANSPARENT and PROMISCUOUS are mutually exclusive" 34762306a36Sopenharmony_ci * STSW45X0C LMAC API - page 12 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci if (priv->filter_flags & FIF_OTHER_BSS && 35062306a36Sopenharmony_ci (mode != P54_FILTER_TYPE_PROMISCUOUS)) 35162306a36Sopenharmony_ci mode |= P54_FILTER_TYPE_TRANSPARENT; 35262306a36Sopenharmony_ci } else { 35362306a36Sopenharmony_ci mode = P54_FILTER_TYPE_HIBERNATE; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci setup->mac_mode = cpu_to_le16(mode); 35762306a36Sopenharmony_ci memcpy(setup->mac_addr, priv->mac_addr, ETH_ALEN); 35862306a36Sopenharmony_ci memcpy(setup->bssid, priv->bssid, ETH_ALEN); 35962306a36Sopenharmony_ci setup->rx_antenna = 2 & priv->rx_diversity_mask; /* automatic */ 36062306a36Sopenharmony_ci setup->rx_align = 0; 36162306a36Sopenharmony_ci if (priv->fw_var < 0x500) { 36262306a36Sopenharmony_ci setup->v1.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); 36362306a36Sopenharmony_ci memset(setup->v1.rts_rates, 0, 8); 36462306a36Sopenharmony_ci setup->v1.rx_addr = cpu_to_le32(priv->rx_end); 36562306a36Sopenharmony_ci setup->v1.max_rx = cpu_to_le16(priv->rx_mtu); 36662306a36Sopenharmony_ci setup->v1.rxhw = cpu_to_le16(priv->rxhw); 36762306a36Sopenharmony_ci setup->v1.wakeup_timer = cpu_to_le16(priv->wakeup_timer); 36862306a36Sopenharmony_ci setup->v1.unalloc0 = cpu_to_le16(0); 36962306a36Sopenharmony_ci } else { 37062306a36Sopenharmony_ci setup->v2.rx_addr = cpu_to_le32(priv->rx_end); 37162306a36Sopenharmony_ci setup->v2.max_rx = cpu_to_le16(priv->rx_mtu); 37262306a36Sopenharmony_ci setup->v2.rxhw = cpu_to_le16(priv->rxhw); 37362306a36Sopenharmony_ci setup->v2.timer = cpu_to_le16(priv->wakeup_timer); 37462306a36Sopenharmony_ci setup->v2.truncate = cpu_to_le16(48896); 37562306a36Sopenharmony_ci setup->v2.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); 37662306a36Sopenharmony_ci setup->v2.sbss_offset = 0; 37762306a36Sopenharmony_ci setup->v2.mcast_window = 0; 37862306a36Sopenharmony_ci setup->v2.rx_rssi_threshold = 0; 37962306a36Sopenharmony_ci setup->v2.rx_ed_threshold = 0; 38062306a36Sopenharmony_ci setup->v2.ref_clock = cpu_to_le32(644245094); 38162306a36Sopenharmony_ci setup->v2.lpf_bandwidth = cpu_to_le16(65535); 38262306a36Sopenharmony_ci setup->v2.osc_start_delay = cpu_to_le16(65535); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci p54_tx(priv, skb); 38562306a36Sopenharmony_ci priv->phy_idle = mode == P54_FILTER_TYPE_HIBERNATE; 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ciint p54_scan(struct p54_common *priv, u16 mode, u16 dwell) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct sk_buff *skb; 39262306a36Sopenharmony_ci struct p54_hdr *hdr; 39362306a36Sopenharmony_ci struct p54_scan_head *head; 39462306a36Sopenharmony_ci struct p54_iq_autocal_entry *iq_autocal; 39562306a36Sopenharmony_ci union p54_scan_body_union *body; 39662306a36Sopenharmony_ci struct p54_scan_tail_rate *rate; 39762306a36Sopenharmony_ci struct pda_rssi_cal_entry *rssi; 39862306a36Sopenharmony_ci struct p54_rssi_db_entry *rssi_data; 39962306a36Sopenharmony_ci unsigned int i; 40062306a36Sopenharmony_ci void *entry; 40162306a36Sopenharmony_ci __le16 freq = cpu_to_le16(priv->hw->conf.chandef.chan->center_freq); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*head) + 40462306a36Sopenharmony_ci 2 + sizeof(*iq_autocal) + sizeof(*body) + 40562306a36Sopenharmony_ci sizeof(*rate) + 2 * sizeof(*rssi), 40662306a36Sopenharmony_ci P54_CONTROL_TYPE_SCAN, GFP_ATOMIC); 40762306a36Sopenharmony_ci if (!skb) 40862306a36Sopenharmony_ci return -ENOMEM; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci head = skb_put(skb, sizeof(*head)); 41162306a36Sopenharmony_ci memset(head->scan_params, 0, sizeof(head->scan_params)); 41262306a36Sopenharmony_ci head->mode = cpu_to_le16(mode); 41362306a36Sopenharmony_ci head->dwell = cpu_to_le16(dwell); 41462306a36Sopenharmony_ci head->freq = freq; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { 41762306a36Sopenharmony_ci __le16 *pa_power_points = skb_put(skb, 2); 41862306a36Sopenharmony_ci *pa_power_points = cpu_to_le16(0x0c); 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci iq_autocal = skb_put(skb, sizeof(*iq_autocal)); 42262306a36Sopenharmony_ci for (i = 0; i < priv->iq_autocal_len; i++) { 42362306a36Sopenharmony_ci if (priv->iq_autocal[i].freq != freq) 42462306a36Sopenharmony_ci continue; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci memcpy(iq_autocal, &priv->iq_autocal[i].params, 42762306a36Sopenharmony_ci sizeof(struct p54_iq_autocal_entry)); 42862306a36Sopenharmony_ci break; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci if (i == priv->iq_autocal_len) 43162306a36Sopenharmony_ci goto err; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) 43462306a36Sopenharmony_ci body = skb_put(skb, sizeof(body->longbow)); 43562306a36Sopenharmony_ci else 43662306a36Sopenharmony_ci body = skb_put(skb, sizeof(body->normal)); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci for (i = 0; i < priv->output_limit->entries; i++) { 43962306a36Sopenharmony_ci __le16 *entry_freq = (void *) (priv->output_limit->data + 44062306a36Sopenharmony_ci priv->output_limit->entry_size * i); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (*entry_freq != freq) 44362306a36Sopenharmony_ci continue; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { 44662306a36Sopenharmony_ci memcpy(&body->longbow.power_limits, 44762306a36Sopenharmony_ci (void *) entry_freq + sizeof(__le16), 44862306a36Sopenharmony_ci priv->output_limit->entry_size); 44962306a36Sopenharmony_ci } else { 45062306a36Sopenharmony_ci struct pda_channel_output_limit *limits = 45162306a36Sopenharmony_ci (void *) entry_freq; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci body->normal.val_barker = 0x38; 45462306a36Sopenharmony_ci body->normal.val_bpsk = body->normal.dup_bpsk = 45562306a36Sopenharmony_ci limits->val_bpsk; 45662306a36Sopenharmony_ci body->normal.val_qpsk = body->normal.dup_qpsk = 45762306a36Sopenharmony_ci limits->val_qpsk; 45862306a36Sopenharmony_ci body->normal.val_16qam = body->normal.dup_16qam = 45962306a36Sopenharmony_ci limits->val_16qam; 46062306a36Sopenharmony_ci body->normal.val_64qam = body->normal.dup_64qam = 46162306a36Sopenharmony_ci limits->val_64qam; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci break; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci if (i == priv->output_limit->entries) 46662306a36Sopenharmony_ci goto err; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci entry = (void *)(priv->curve_data->data + priv->curve_data->offset); 46962306a36Sopenharmony_ci for (i = 0; i < priv->curve_data->entries; i++) { 47062306a36Sopenharmony_ci if (*((__le16 *)entry) != freq) { 47162306a36Sopenharmony_ci entry += priv->curve_data->entry_size; 47262306a36Sopenharmony_ci continue; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { 47662306a36Sopenharmony_ci memcpy(&body->longbow.curve_data, 47762306a36Sopenharmony_ci entry + sizeof(__le16), 47862306a36Sopenharmony_ci priv->curve_data->entry_size); 47962306a36Sopenharmony_ci } else { 48062306a36Sopenharmony_ci struct p54_scan_body *chan = &body->normal; 48162306a36Sopenharmony_ci struct pda_pa_curve_data *curve_data = 48262306a36Sopenharmony_ci (void *) priv->curve_data->data; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci entry += sizeof(__le16); 48562306a36Sopenharmony_ci chan->pa_points_per_curve = 8; 48662306a36Sopenharmony_ci memset(chan->curve_data, 0, sizeof(chan->curve_data)); 48762306a36Sopenharmony_ci memcpy(chan->curve_data, entry, 48862306a36Sopenharmony_ci sizeof(struct p54_pa_curve_data_sample) * 48962306a36Sopenharmony_ci min((u8)8, curve_data->points_per_channel)); 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci break; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci if (i == priv->curve_data->entries) 49462306a36Sopenharmony_ci goto err; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if ((priv->fw_var >= 0x500) && (priv->fw_var < 0x509)) { 49762306a36Sopenharmony_ci rate = skb_put(skb, sizeof(*rate)); 49862306a36Sopenharmony_ci rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); 49962306a36Sopenharmony_ci for (i = 0; i < sizeof(rate->rts_rates); i++) 50062306a36Sopenharmony_ci rate->rts_rates[i] = i; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci rssi = skb_put(skb, sizeof(*rssi)); 50462306a36Sopenharmony_ci rssi_data = p54_rssi_find(priv, le16_to_cpu(freq)); 50562306a36Sopenharmony_ci rssi->mul = cpu_to_le16(rssi_data->mul); 50662306a36Sopenharmony_ci rssi->add = cpu_to_le16(rssi_data->add); 50762306a36Sopenharmony_ci if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { 50862306a36Sopenharmony_ci /* Longbow frontend needs ever more */ 50962306a36Sopenharmony_ci rssi = skb_put(skb, sizeof(*rssi)); 51062306a36Sopenharmony_ci rssi->mul = cpu_to_le16(rssi_data->longbow_unkn); 51162306a36Sopenharmony_ci rssi->add = cpu_to_le16(rssi_data->longbow_unk2); 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (priv->fw_var >= 0x509) { 51562306a36Sopenharmony_ci rate = skb_put(skb, sizeof(*rate)); 51662306a36Sopenharmony_ci rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); 51762306a36Sopenharmony_ci for (i = 0; i < sizeof(rate->rts_rates); i++) 51862306a36Sopenharmony_ci rate->rts_rates[i] = i; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci hdr = (struct p54_hdr *) skb->data; 52262306a36Sopenharmony_ci hdr->len = cpu_to_le16(skb->len - sizeof(*hdr)); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci p54_tx(priv, skb); 52562306a36Sopenharmony_ci priv->cur_rssi = rssi_data; 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cierr: 52962306a36Sopenharmony_ci wiphy_err(priv->hw->wiphy, "frequency change to channel %d failed.\n", 53062306a36Sopenharmony_ci ieee80211_frequency_to_channel( 53162306a36Sopenharmony_ci priv->hw->conf.chandef.chan->center_freq)); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci dev_kfree_skb_any(skb); 53462306a36Sopenharmony_ci return -EINVAL; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ciint p54_set_leds(struct p54_common *priv) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci struct sk_buff *skb; 54062306a36Sopenharmony_ci struct p54_led *led; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*led), 54362306a36Sopenharmony_ci P54_CONTROL_TYPE_LED, GFP_ATOMIC); 54462306a36Sopenharmony_ci if (unlikely(!skb)) 54562306a36Sopenharmony_ci return -ENOMEM; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci led = skb_put(skb, sizeof(*led)); 54862306a36Sopenharmony_ci led->flags = cpu_to_le16(0x0003); 54962306a36Sopenharmony_ci led->mask[0] = led->mask[1] = cpu_to_le16(priv->softled_state); 55062306a36Sopenharmony_ci led->delay[0] = cpu_to_le16(1); 55162306a36Sopenharmony_ci led->delay[1] = cpu_to_le16(0); 55262306a36Sopenharmony_ci p54_tx(priv, skb); 55362306a36Sopenharmony_ci return 0; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ciint p54_set_edcf(struct p54_common *priv) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct sk_buff *skb; 55962306a36Sopenharmony_ci struct p54_edcf *edcf; 56062306a36Sopenharmony_ci u8 rtd; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*edcf), 56362306a36Sopenharmony_ci P54_CONTROL_TYPE_DCFINIT, GFP_ATOMIC); 56462306a36Sopenharmony_ci if (unlikely(!skb)) 56562306a36Sopenharmony_ci return -ENOMEM; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci edcf = skb_put(skb, sizeof(*edcf)); 56862306a36Sopenharmony_ci if (priv->use_short_slot) { 56962306a36Sopenharmony_ci edcf->slottime = 9; 57062306a36Sopenharmony_ci edcf->sifs = 0x10; 57162306a36Sopenharmony_ci edcf->eofpad = 0x00; 57262306a36Sopenharmony_ci } else { 57362306a36Sopenharmony_ci edcf->slottime = 20; 57462306a36Sopenharmony_ci edcf->sifs = 0x0a; 57562306a36Sopenharmony_ci edcf->eofpad = 0x06; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci /* 57862306a36Sopenharmony_ci * calculate the extra round trip delay according to the 57962306a36Sopenharmony_ci * formula from 802.11-2007 17.3.8.6. 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_ci rtd = 3 * priv->coverage_class; 58262306a36Sopenharmony_ci edcf->slottime += rtd; 58362306a36Sopenharmony_ci edcf->round_trip_delay = cpu_to_le16(rtd); 58462306a36Sopenharmony_ci /* (see prism54/isl_oid.h for further details) */ 58562306a36Sopenharmony_ci edcf->frameburst = cpu_to_le16(0); 58662306a36Sopenharmony_ci edcf->flags = 0; 58762306a36Sopenharmony_ci memset(edcf->mapping, 0, sizeof(edcf->mapping)); 58862306a36Sopenharmony_ci memcpy(edcf->queue, priv->qos_params, sizeof(edcf->queue)); 58962306a36Sopenharmony_ci p54_tx(priv, skb); 59062306a36Sopenharmony_ci return 0; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ciint p54_set_ps(struct p54_common *priv) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct sk_buff *skb; 59662306a36Sopenharmony_ci struct p54_psm *psm; 59762306a36Sopenharmony_ci unsigned int i; 59862306a36Sopenharmony_ci u16 mode; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (priv->hw->conf.flags & IEEE80211_CONF_PS && 60162306a36Sopenharmony_ci !priv->powersave_override) 60262306a36Sopenharmony_ci mode = P54_PSM | P54_PSM_BEACON_TIMEOUT | P54_PSM_DTIM | 60362306a36Sopenharmony_ci P54_PSM_CHECKSUM | P54_PSM_MCBC; 60462306a36Sopenharmony_ci else 60562306a36Sopenharmony_ci mode = P54_PSM_CAM; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*psm), 60862306a36Sopenharmony_ci P54_CONTROL_TYPE_PSM, GFP_ATOMIC); 60962306a36Sopenharmony_ci if (!skb) 61062306a36Sopenharmony_ci return -ENOMEM; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci psm = skb_put(skb, sizeof(*psm)); 61362306a36Sopenharmony_ci psm->mode = cpu_to_le16(mode); 61462306a36Sopenharmony_ci psm->aid = cpu_to_le16(priv->aid); 61562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(psm->intervals); i++) { 61662306a36Sopenharmony_ci psm->intervals[i].interval = 61762306a36Sopenharmony_ci cpu_to_le16(priv->hw->conf.listen_interval); 61862306a36Sopenharmony_ci psm->intervals[i].periods = cpu_to_le16(1); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci psm->beacon_rssi_skip_max = 200; 62262306a36Sopenharmony_ci psm->rssi_delta_threshold = 0; 62362306a36Sopenharmony_ci psm->nr = 1; 62462306a36Sopenharmony_ci psm->exclude[0] = WLAN_EID_TIM; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci p54_tx(priv, skb); 62762306a36Sopenharmony_ci priv->phy_ps = mode != P54_PSM_CAM; 62862306a36Sopenharmony_ci return 0; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ciint p54_init_xbow_synth(struct p54_common *priv) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci struct sk_buff *skb; 63462306a36Sopenharmony_ci struct p54_xbow_synth *xbow; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*xbow), 63762306a36Sopenharmony_ci P54_CONTROL_TYPE_XBOW_SYNTH_CFG, GFP_KERNEL); 63862306a36Sopenharmony_ci if (unlikely(!skb)) 63962306a36Sopenharmony_ci return -ENOMEM; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci xbow = skb_put(skb, sizeof(*xbow)); 64262306a36Sopenharmony_ci xbow->magic1 = cpu_to_le16(0x1); 64362306a36Sopenharmony_ci xbow->magic2 = cpu_to_le16(0x2); 64462306a36Sopenharmony_ci xbow->freq = cpu_to_le16(5390); 64562306a36Sopenharmony_ci memset(xbow->padding, 0, sizeof(xbow->padding)); 64662306a36Sopenharmony_ci p54_tx(priv, skb); 64762306a36Sopenharmony_ci return 0; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ciint p54_upload_key(struct p54_common *priv, u8 algo, int slot, u8 idx, u8 len, 65162306a36Sopenharmony_ci u8 *addr, u8* key) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct sk_buff *skb; 65462306a36Sopenharmony_ci struct p54_keycache *rxkey; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey), 65762306a36Sopenharmony_ci P54_CONTROL_TYPE_RX_KEYCACHE, GFP_KERNEL); 65862306a36Sopenharmony_ci if (unlikely(!skb)) 65962306a36Sopenharmony_ci return -ENOMEM; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci rxkey = skb_put(skb, sizeof(*rxkey)); 66262306a36Sopenharmony_ci rxkey->entry = slot; 66362306a36Sopenharmony_ci rxkey->key_id = idx; 66462306a36Sopenharmony_ci rxkey->key_type = algo; 66562306a36Sopenharmony_ci if (addr) 66662306a36Sopenharmony_ci memcpy(rxkey->mac, addr, ETH_ALEN); 66762306a36Sopenharmony_ci else 66862306a36Sopenharmony_ci eth_broadcast_addr(rxkey->mac); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci switch (algo) { 67162306a36Sopenharmony_ci case P54_CRYPTO_WEP: 67262306a36Sopenharmony_ci case P54_CRYPTO_AESCCMP: 67362306a36Sopenharmony_ci rxkey->key_len = min_t(u8, 16, len); 67462306a36Sopenharmony_ci memcpy(rxkey->key, key, rxkey->key_len); 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci case P54_CRYPTO_TKIPMICHAEL: 67862306a36Sopenharmony_ci rxkey->key_len = 24; 67962306a36Sopenharmony_ci memcpy(rxkey->key, key, 16); 68062306a36Sopenharmony_ci memcpy(&(rxkey->key[16]), &(key 68162306a36Sopenharmony_ci [NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]), 8); 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci case P54_CRYPTO_NONE: 68562306a36Sopenharmony_ci rxkey->key_len = 0; 68662306a36Sopenharmony_ci memset(rxkey->key, 0, sizeof(rxkey->key)); 68762306a36Sopenharmony_ci break; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci default: 69062306a36Sopenharmony_ci wiphy_err(priv->hw->wiphy, 69162306a36Sopenharmony_ci "invalid cryptographic algorithm: %d\n", algo); 69262306a36Sopenharmony_ci dev_kfree_skb(skb); 69362306a36Sopenharmony_ci return -EINVAL; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci p54_tx(priv, skb); 69762306a36Sopenharmony_ci return 0; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ciint p54_fetch_statistics(struct p54_common *priv) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct ieee80211_tx_info *txinfo; 70362306a36Sopenharmony_ci struct p54_tx_info *p54info; 70462306a36Sopenharmony_ci struct sk_buff *skb; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL, 70762306a36Sopenharmony_ci sizeof(struct p54_statistics), 70862306a36Sopenharmony_ci P54_CONTROL_TYPE_STAT_READBACK, GFP_KERNEL); 70962306a36Sopenharmony_ci if (!skb) 71062306a36Sopenharmony_ci return -ENOMEM; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* 71362306a36Sopenharmony_ci * The statistic feedback causes some extra headaches here, if it 71462306a36Sopenharmony_ci * is not to crash/corrupt the firmware data structures. 71562306a36Sopenharmony_ci * 71662306a36Sopenharmony_ci * Unlike all other Control Get OIDs we can not use helpers like 71762306a36Sopenharmony_ci * skb_put to reserve the space for the data we're requesting. 71862306a36Sopenharmony_ci * Instead the extra frame length -which will hold the results later- 71962306a36Sopenharmony_ci * will only be told to the p54_assign_address, so that following 72062306a36Sopenharmony_ci * frames won't be placed into the allegedly empty area. 72162306a36Sopenharmony_ci */ 72262306a36Sopenharmony_ci txinfo = IEEE80211_SKB_CB(skb); 72362306a36Sopenharmony_ci p54info = (void *) txinfo->rate_driver_data; 72462306a36Sopenharmony_ci p54info->extra_len = sizeof(struct p54_statistics); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci p54_tx(priv, skb); 72762306a36Sopenharmony_ci return 0; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ciint p54_set_groupfilter(struct p54_common *priv) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci struct p54_group_address_table *grp; 73362306a36Sopenharmony_ci struct sk_buff *skb; 73462306a36Sopenharmony_ci bool on = false; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*grp), 73762306a36Sopenharmony_ci P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE, GFP_KERNEL); 73862306a36Sopenharmony_ci if (!skb) 73962306a36Sopenharmony_ci return -ENOMEM; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci grp = skb_put(skb, sizeof(*grp)); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci on = !(priv->filter_flags & FIF_ALLMULTI) && 74462306a36Sopenharmony_ci (priv->mc_maclist_num > 0 && 74562306a36Sopenharmony_ci priv->mc_maclist_num <= MC_FILTER_ADDRESS_NUM); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (on) { 74862306a36Sopenharmony_ci grp->filter_enable = cpu_to_le16(1); 74962306a36Sopenharmony_ci grp->num_address = cpu_to_le16(priv->mc_maclist_num); 75062306a36Sopenharmony_ci memcpy(grp->mac_list, priv->mc_maclist, sizeof(grp->mac_list)); 75162306a36Sopenharmony_ci } else { 75262306a36Sopenharmony_ci grp->filter_enable = cpu_to_le16(0); 75362306a36Sopenharmony_ci grp->num_address = cpu_to_le16(0); 75462306a36Sopenharmony_ci memset(grp->mac_list, 0, sizeof(grp->mac_list)); 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci p54_tx(priv, skb); 75862306a36Sopenharmony_ci return 0; 75962306a36Sopenharmony_ci} 760