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