brintos

brintos / linux-shallow public Read only

0
0
Text · 20.3 KiB · 772084a Raw
760 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Firmware I/O code for mac80211 Prism54 drivers4 *5 * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>6 * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>7 * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>8 *9 * Based on:10 * - the islsm (softmac prism54) driver, which is:11 *   Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.12 * - stlc45xx driver13 *   Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).14 */15 16#include <linux/slab.h>17#include <linux/firmware.h>18#include <linux/etherdevice.h>19#include <linux/export.h>20 21#include <net/mac80211.h>22 23#include "p54.h"24#include "eeprom.h"25#include "lmac.h"26 27int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)28{29	struct p54_common *priv = dev->priv;30	struct exp_if *exp_if;31	struct bootrec *bootrec;32	u32 *data = (u32 *)fw->data;33	u32 *end_data = (u32 *)fw->data + (fw->size >> 2);34	u8 *fw_version = NULL;35	size_t len;36	int i;37	int maxlen;38 39	if (priv->rx_start)40		return 0;41 42	while (data < end_data && *data)43		data++;44 45	while (data < end_data && !*data)46		data++;47 48	bootrec = (struct bootrec *) data;49 50	while (bootrec->data <= end_data && (bootrec->data +51	       (len = le32_to_cpu(bootrec->len))) <= end_data) {52		u32 code = le32_to_cpu(bootrec->code);53		switch (code) {54		case BR_CODE_COMPONENT_ID:55			priv->fw_interface = be32_to_cpup((__be32 *)56					     bootrec->data);57			switch (priv->fw_interface) {58			case FW_LM86:59			case FW_LM20:60			case FW_LM87: {61				char *iftype = (char *)bootrec->data;62				wiphy_info(priv->hw->wiphy,63					   "p54 detected a LM%c%c firmware\n",64					   iftype[2], iftype[3]);65				break;66				}67			case FW_FMAC:68			default:69				wiphy_err(priv->hw->wiphy,70					  "unsupported firmware\n");71				return -ENODEV;72			}73			break;74		case BR_CODE_COMPONENT_VERSION:75			/* 24 bytes should be enough for all firmwares */76			if (strnlen((unsigned char *) bootrec->data, 24) < 24)77				fw_version = (unsigned char *) bootrec->data;78			break;79		case BR_CODE_DESCR: {80			struct bootrec_desc *desc =81				(struct bootrec_desc *)bootrec->data;82			priv->rx_start = le32_to_cpu(desc->rx_start);83			/* FIXME add sanity checking */84			priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500;85			priv->headroom = desc->headroom;86			priv->tailroom = desc->tailroom;87			priv->privacy_caps = desc->privacy_caps;88			priv->rx_keycache_size = desc->rx_keycache_size;89			if (le32_to_cpu(bootrec->len) == 11)90				priv->rx_mtu = le16_to_cpu(desc->rx_mtu);91			else92				priv->rx_mtu = (size_t)93					0x620 - priv->tx_hdr_len;94			maxlen = priv->tx_hdr_len + /* USB devices */95				 sizeof(struct p54_rx_data) +96				 4 + /* rx alignment */97				 IEEE80211_MAX_FRAG_THRESHOLD;98			if (priv->rx_mtu > maxlen && PAGE_SIZE == 4096) {99				printk(KERN_INFO "p54: rx_mtu reduced from %d "100				       "to %d\n", priv->rx_mtu, maxlen);101				priv->rx_mtu = maxlen;102			}103			break;104			}105		case BR_CODE_EXPOSED_IF:106			exp_if = (struct exp_if *) bootrec->data;107			for (i = 0; i < (len * sizeof(*exp_if) / 4); i++)108				if (exp_if[i].if_id == cpu_to_le16(IF_ID_LMAC))109					priv->fw_var = le16_to_cpu(exp_if[i].variant);110			break;111		case BR_CODE_DEPENDENT_IF:112			break;113		case BR_CODE_END_OF_BRA:114		case LEGACY_BR_CODE_END_OF_BRA:115			end_data = NULL;116			break;117		default:118			break;119		}120		bootrec = (struct bootrec *)&bootrec->data[len];121	}122 123	if (fw_version) {124		wiphy_info(priv->hw->wiphy,125			   "FW rev %s - Softmac protocol %x.%x\n",126			   fw_version, priv->fw_var >> 8, priv->fw_var & 0xff);127		snprintf(dev->wiphy->fw_version, sizeof(dev->wiphy->fw_version),128				"%.19s - %x.%x", fw_version,129				priv->fw_var >> 8, priv->fw_var & 0xff);130	}131 132	if (priv->fw_var < 0x500)133		wiphy_info(priv->hw->wiphy,134			   "you are using an obsolete firmware. "135			   "visit http://wireless.wiki.kernel.org/en/users/Drivers/p54 "136			   "and grab one for \"kernel >= 2.6.28\"!\n");137 138	if (priv->fw_var >= 0x300) {139		/* Firmware supports QoS, use it! */140 141		if (priv->fw_var >= 0x500) {142			priv->tx_stats[P54_QUEUE_AC_VO].limit = 16;143			priv->tx_stats[P54_QUEUE_AC_VI].limit = 16;144			priv->tx_stats[P54_QUEUE_AC_BE].limit = 16;145			priv->tx_stats[P54_QUEUE_AC_BK].limit = 16;146		} else {147			priv->tx_stats[P54_QUEUE_AC_VO].limit = 3;148			priv->tx_stats[P54_QUEUE_AC_VI].limit = 4;149			priv->tx_stats[P54_QUEUE_AC_BE].limit = 3;150			priv->tx_stats[P54_QUEUE_AC_BK].limit = 2;151		}152		priv->hw->queues = P54_QUEUE_AC_NUM;153	}154 155	wiphy_info(priv->hw->wiphy,156		   "cryptographic accelerator WEP:%s, TKIP:%s, CCMP:%s\n",157		   (priv->privacy_caps & BR_DESC_PRIV_CAP_WEP) ? "YES" : "no",158		   (priv->privacy_caps &159		    (BR_DESC_PRIV_CAP_TKIP | BR_DESC_PRIV_CAP_MICHAEL))160		   ? "YES" : "no",161		   (priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)162		   ? "YES" : "no");163 164	if (priv->rx_keycache_size) {165		/*166		 * NOTE:167		 *168		 * The firmware provides at most 255 (0 - 254) slots169		 * for keys which are then used to offload decryption.170		 * As a result the 255 entry (aka 0xff) can be used171		 * safely by the driver to mark keys that didn't fit172		 * into the full cache. This trick saves us from173		 * keeping a extra list for uploaded keys.174		 */175 176		priv->used_rxkeys = bitmap_zalloc(priv->rx_keycache_size,177						  GFP_KERNEL);178		if (!priv->used_rxkeys)179			return -ENOMEM;180	}181 182	return 0;183}184EXPORT_SYMBOL_GPL(p54_parse_firmware);185 186static struct sk_buff *p54_alloc_skb(struct p54_common *priv, u16 hdr_flags,187				     u16 payload_len, u16 type, gfp_t memflags)188{189	struct p54_hdr *hdr;190	struct sk_buff *skb;191	size_t frame_len = sizeof(*hdr) + payload_len;192 193	if (frame_len > P54_MAX_CTRL_FRAME_LEN)194		return NULL;195 196	if (unlikely(skb_queue_len(&priv->tx_pending) > 64))197		return NULL;198 199	skb = __dev_alloc_skb(priv->tx_hdr_len + frame_len, memflags);200	if (!skb)201		return NULL;202	skb_reserve(skb, priv->tx_hdr_len);203 204	hdr = skb_put(skb, sizeof(*hdr));205	hdr->flags = cpu_to_le16(hdr_flags);206	hdr->len = cpu_to_le16(payload_len);207	hdr->type = cpu_to_le16(type);208	hdr->tries = hdr->rts_tries = 0;209	return skb;210}211 212int p54_download_eeprom(struct p54_common *priv, void *buf,213			u16 offset, u16 len)214{215	struct p54_eeprom_lm86 *eeprom_hdr;216	struct sk_buff *skb;217	size_t eeprom_hdr_size;218	int ret = 0;219	long time_left;220 221	if (priv->fw_var >= 0x509)222		eeprom_hdr_size = sizeof(*eeprom_hdr);223	else224		eeprom_hdr_size = 0x4;225 226	skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL, eeprom_hdr_size +227			    len, P54_CONTROL_TYPE_EEPROM_READBACK,228			    GFP_KERNEL);229	if (unlikely(!skb))230		return -ENOMEM;231 232	mutex_lock(&priv->eeprom_mutex);233	priv->eeprom = buf;234	eeprom_hdr = skb_put(skb, eeprom_hdr_size + len);235 236	if (priv->fw_var < 0x509) {237		eeprom_hdr->v1.offset = cpu_to_le16(offset);238		eeprom_hdr->v1.len = cpu_to_le16(len);239	} else {240		eeprom_hdr->v2.offset = cpu_to_le32(offset);241		eeprom_hdr->v2.len = cpu_to_le16(len);242		eeprom_hdr->v2.magic2 = 0xf;243		memcpy(eeprom_hdr->v2.magic, (const char *)"LOCK", 4);244	}245 246	p54_tx(priv, skb);247 248	time_left = wait_for_completion_interruptible_timeout(249			&priv->eeprom_comp, HZ);250	if (time_left <= 0) {251		wiphy_err(priv->hw->wiphy,252			"device does not respond or signal received!\n");253		ret = -EBUSY;254	}255	priv->eeprom = NULL;256	mutex_unlock(&priv->eeprom_mutex);257	return ret;258}259 260int p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set)261{262	struct sk_buff *skb;263	struct p54_tim *tim;264 265	skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*tim),266			    P54_CONTROL_TYPE_TIM, GFP_ATOMIC);267	if (unlikely(!skb))268		return -ENOMEM;269 270	tim = skb_put(skb, sizeof(*tim));271	tim->count = 1;272	tim->entry[0] = cpu_to_le16(set ? (aid | 0x8000) : aid);273	p54_tx(priv, skb);274	return 0;275}276 277int p54_sta_unlock(struct p54_common *priv, u8 *addr)278{279	struct sk_buff *skb;280	struct p54_sta_unlock *sta;281 282	skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*sta),283			    P54_CONTROL_TYPE_PSM_STA_UNLOCK, GFP_ATOMIC);284	if (unlikely(!skb))285		return -ENOMEM;286 287	sta = skb_put(skb, sizeof(*sta));288	memcpy(sta->addr, addr, ETH_ALEN);289	p54_tx(priv, skb);290	return 0;291}292 293int p54_tx_cancel(struct p54_common *priv, __le32 req_id)294{295	struct sk_buff *skb;296	struct p54_txcancel *cancel;297	u32 _req_id = le32_to_cpu(req_id);298 299	if (unlikely(_req_id < priv->rx_start || _req_id > priv->rx_end))300		return -EINVAL;301 302	skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*cancel),303			    P54_CONTROL_TYPE_TXCANCEL, GFP_ATOMIC);304	if (unlikely(!skb))305		return -ENOMEM;306 307	cancel = skb_put(skb, sizeof(*cancel));308	cancel->req_id = req_id;309	p54_tx(priv, skb);310	return 0;311}312 313int p54_setup_mac(struct p54_common *priv)314{315	struct sk_buff *skb;316	struct p54_setup_mac *setup;317	u16 mode;318 319	skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*setup),320			    P54_CONTROL_TYPE_SETUP, GFP_ATOMIC);321	if (!skb)322		return -ENOMEM;323 324	setup = skb_put(skb, sizeof(*setup));325	if (!(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) {326		switch (priv->mode) {327		case NL80211_IFTYPE_STATION:328			mode = P54_FILTER_TYPE_STATION;329			break;330		case NL80211_IFTYPE_AP:331			mode = P54_FILTER_TYPE_AP;332			break;333		case NL80211_IFTYPE_ADHOC:334		case NL80211_IFTYPE_MESH_POINT:335			mode = P54_FILTER_TYPE_IBSS;336			break;337		case NL80211_IFTYPE_MONITOR:338			mode = P54_FILTER_TYPE_PROMISCUOUS;339			break;340		default:341			mode = P54_FILTER_TYPE_HIBERNATE;342			break;343		}344 345		/*346		 * "TRANSPARENT and PROMISCUOUS are mutually exclusive"347		 * STSW45X0C LMAC API - page 12348		 */349		if (priv->filter_flags & FIF_OTHER_BSS &&350		    (mode != P54_FILTER_TYPE_PROMISCUOUS))351			mode |= P54_FILTER_TYPE_TRANSPARENT;352	} else {353		mode = P54_FILTER_TYPE_HIBERNATE;354	}355 356	setup->mac_mode = cpu_to_le16(mode);357	memcpy(setup->mac_addr, priv->mac_addr, ETH_ALEN);358	memcpy(setup->bssid, priv->bssid, ETH_ALEN);359	setup->rx_antenna = 2 & priv->rx_diversity_mask; /* automatic */360	setup->rx_align = 0;361	if (priv->fw_var < 0x500) {362		setup->v1.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);363		memset(setup->v1.rts_rates, 0, 8);364		setup->v1.rx_addr = cpu_to_le32(priv->rx_end);365		setup->v1.max_rx = cpu_to_le16(priv->rx_mtu);366		setup->v1.rxhw = cpu_to_le16(priv->rxhw);367		setup->v1.wakeup_timer = cpu_to_le16(priv->wakeup_timer);368		setup->v1.unalloc0 = cpu_to_le16(0);369	} else {370		setup->v2.rx_addr = cpu_to_le32(priv->rx_end);371		setup->v2.max_rx = cpu_to_le16(priv->rx_mtu);372		setup->v2.rxhw = cpu_to_le16(priv->rxhw);373		setup->v2.timer = cpu_to_le16(priv->wakeup_timer);374		setup->v2.truncate = cpu_to_le16(48896);375		setup->v2.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);376		setup->v2.sbss_offset = 0;377		setup->v2.mcast_window = 0;378		setup->v2.rx_rssi_threshold = 0;379		setup->v2.rx_ed_threshold = 0;380		setup->v2.ref_clock = cpu_to_le32(644245094);381		setup->v2.lpf_bandwidth = cpu_to_le16(65535);382		setup->v2.osc_start_delay = cpu_to_le16(65535);383	}384	p54_tx(priv, skb);385	priv->phy_idle = mode == P54_FILTER_TYPE_HIBERNATE;386	return 0;387}388 389int p54_scan(struct p54_common *priv, u16 mode, u16 dwell)390{391	struct sk_buff *skb;392	struct p54_hdr *hdr;393	struct p54_scan_head *head;394	struct p54_iq_autocal_entry *iq_autocal;395	union p54_scan_body_union *body;396	struct p54_scan_tail_rate *rate;397	struct pda_rssi_cal_entry *rssi;398	struct p54_rssi_db_entry *rssi_data;399	unsigned int i;400	void *entry;401	__le16 freq = cpu_to_le16(priv->hw->conf.chandef.chan->center_freq);402 403	skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*head) +404			    2 + sizeof(*iq_autocal) + sizeof(*body) +405			    sizeof(*rate) + 2 * sizeof(*rssi),406			    P54_CONTROL_TYPE_SCAN, GFP_ATOMIC);407	if (!skb)408		return -ENOMEM;409 410	head = skb_put(skb, sizeof(*head));411	memset(head->scan_params, 0, sizeof(head->scan_params));412	head->mode = cpu_to_le16(mode);413	head->dwell = cpu_to_le16(dwell);414	head->freq = freq;415 416	if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {417		__le16 *pa_power_points = skb_put(skb, 2);418		*pa_power_points = cpu_to_le16(0x0c);419	}420 421	iq_autocal = skb_put(skb, sizeof(*iq_autocal));422	for (i = 0; i < priv->iq_autocal_len; i++) {423		if (priv->iq_autocal[i].freq != freq)424			continue;425 426		memcpy(iq_autocal, &priv->iq_autocal[i].params,427		       sizeof(struct p54_iq_autocal_entry));428		break;429	}430	if (i == priv->iq_autocal_len)431		goto err;432 433	if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW)434		body = skb_put(skb, sizeof(body->longbow));435	else436		body = skb_put(skb, sizeof(body->normal));437 438	for (i = 0; i < priv->output_limit->entries; i++) {439		__le16 *entry_freq = (void *) (priv->output_limit->data +440				     priv->output_limit->entry_size * i);441 442		if (*entry_freq != freq)443			continue;444 445		if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {446			memcpy(&body->longbow.power_limits,447			       (void *) entry_freq + sizeof(__le16),448			       priv->output_limit->entry_size);449		} else {450			struct pda_channel_output_limit *limits =451			       (void *) entry_freq;452 453			body->normal.val_barker = 0x38;454			body->normal.val_bpsk = body->normal.dup_bpsk =455				limits->val_bpsk;456			body->normal.val_qpsk = body->normal.dup_qpsk =457				limits->val_qpsk;458			body->normal.val_16qam = body->normal.dup_16qam =459				limits->val_16qam;460			body->normal.val_64qam = body->normal.dup_64qam =461				limits->val_64qam;462		}463		break;464	}465	if (i == priv->output_limit->entries)466		goto err;467 468	entry = (void *)(priv->curve_data->data + priv->curve_data->offset);469	for (i = 0; i < priv->curve_data->entries; i++) {470		if (*((__le16 *)entry) != freq) {471			entry += priv->curve_data->entry_size;472			continue;473		}474 475		if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {476			memcpy(&body->longbow.curve_data,477				entry + sizeof(__le16),478				priv->curve_data->entry_size);479		} else {480			struct p54_scan_body *chan = &body->normal;481			struct pda_pa_curve_data *curve_data =482				(void *) priv->curve_data->data;483 484			entry += sizeof(__le16);485			chan->pa_points_per_curve = 8;486			memset(chan->curve_data, 0, sizeof(chan->curve_data));487			memcpy(chan->curve_data, entry,488			       sizeof(struct p54_pa_curve_data_sample) *489			       min((u8)8, curve_data->points_per_channel));490		}491		break;492	}493	if (i == priv->curve_data->entries)494		goto err;495 496	if ((priv->fw_var >= 0x500) && (priv->fw_var < 0x509)) {497		rate = skb_put(skb, sizeof(*rate));498		rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);499		for (i = 0; i < sizeof(rate->rts_rates); i++)500			rate->rts_rates[i] = i;501	}502 503	rssi = skb_put(skb, sizeof(*rssi));504	rssi_data = p54_rssi_find(priv, le16_to_cpu(freq));505	rssi->mul = cpu_to_le16(rssi_data->mul);506	rssi->add = cpu_to_le16(rssi_data->add);507	if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {508		/* Longbow frontend needs ever more */509		rssi = skb_put(skb, sizeof(*rssi));510		rssi->mul = cpu_to_le16(rssi_data->longbow_unkn);511		rssi->add = cpu_to_le16(rssi_data->longbow_unk2);512	}513 514	if (priv->fw_var >= 0x509) {515		rate = skb_put(skb, sizeof(*rate));516		rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);517		for (i = 0; i < sizeof(rate->rts_rates); i++)518			rate->rts_rates[i] = i;519	}520 521	hdr = (struct p54_hdr *) skb->data;522	hdr->len = cpu_to_le16(skb->len - sizeof(*hdr));523 524	p54_tx(priv, skb);525	priv->cur_rssi = rssi_data;526	return 0;527 528err:529	wiphy_err(priv->hw->wiphy, "frequency change to channel %d failed.\n",530		  ieee80211_frequency_to_channel(531			  priv->hw->conf.chandef.chan->center_freq));532 533	dev_kfree_skb_any(skb);534	return -EINVAL;535}536 537int p54_set_leds(struct p54_common *priv)538{539	struct sk_buff *skb;540	struct p54_led *led;541 542	skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*led),543			    P54_CONTROL_TYPE_LED, GFP_ATOMIC);544	if (unlikely(!skb))545		return -ENOMEM;546 547	led = skb_put(skb, sizeof(*led));548	led->flags = cpu_to_le16(0x0003);549	led->mask[0] = led->mask[1] = cpu_to_le16(priv->softled_state);550	led->delay[0] = cpu_to_le16(1);551	led->delay[1] = cpu_to_le16(0);552	p54_tx(priv, skb);553	return 0;554}555 556int p54_set_edcf(struct p54_common *priv)557{558	struct sk_buff *skb;559	struct p54_edcf *edcf;560	u8 rtd;561 562	skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*edcf),563			    P54_CONTROL_TYPE_DCFINIT, GFP_ATOMIC);564	if (unlikely(!skb))565		return -ENOMEM;566 567	edcf = skb_put(skb, sizeof(*edcf));568	if (priv->use_short_slot) {569		edcf->slottime = 9;570		edcf->sifs = 0x10;571		edcf->eofpad = 0x00;572	} else {573		edcf->slottime = 20;574		edcf->sifs = 0x0a;575		edcf->eofpad = 0x06;576	}577	/*578	 * calculate the extra round trip delay according to the579	 * formula from 802.11-2007 17.3.8.6.580	 */581	rtd = 3 * priv->coverage_class;582	edcf->slottime += rtd;583	edcf->round_trip_delay = cpu_to_le16(rtd);584	/* (see prism54/isl_oid.h for further details) */585	edcf->frameburst = cpu_to_le16(0);586	edcf->flags = 0;587	memset(edcf->mapping, 0, sizeof(edcf->mapping));588	memcpy(edcf->queue, priv->qos_params, sizeof(edcf->queue));589	p54_tx(priv, skb);590	return 0;591}592 593int p54_set_ps(struct p54_common *priv)594{595	struct sk_buff *skb;596	struct p54_psm *psm;597	unsigned int i;598	u16 mode;599 600	if (priv->hw->conf.flags & IEEE80211_CONF_PS &&601	    !priv->powersave_override)602		mode = P54_PSM | P54_PSM_BEACON_TIMEOUT | P54_PSM_DTIM |603		       P54_PSM_CHECKSUM | P54_PSM_MCBC;604	else605		mode = P54_PSM_CAM;606 607	skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*psm),608			    P54_CONTROL_TYPE_PSM, GFP_ATOMIC);609	if (!skb)610		return -ENOMEM;611 612	psm = skb_put(skb, sizeof(*psm));613	psm->mode = cpu_to_le16(mode);614	psm->aid = cpu_to_le16(priv->aid);615	for (i = 0; i < ARRAY_SIZE(psm->intervals); i++) {616		psm->intervals[i].interval =617			cpu_to_le16(priv->hw->conf.listen_interval);618		psm->intervals[i].periods = cpu_to_le16(1);619	}620 621	psm->beacon_rssi_skip_max = 200;622	psm->rssi_delta_threshold = 0;623	psm->nr = 1;624	psm->exclude[0] = WLAN_EID_TIM;625 626	p54_tx(priv, skb);627	priv->phy_ps = mode != P54_PSM_CAM;628	return 0;629}630 631int p54_init_xbow_synth(struct p54_common *priv)632{633	struct sk_buff *skb;634	struct p54_xbow_synth *xbow;635 636	skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*xbow),637			    P54_CONTROL_TYPE_XBOW_SYNTH_CFG, GFP_KERNEL);638	if (unlikely(!skb))639		return -ENOMEM;640 641	xbow = skb_put(skb, sizeof(*xbow));642	xbow->magic1 = cpu_to_le16(0x1);643	xbow->magic2 = cpu_to_le16(0x2);644	xbow->freq = cpu_to_le16(5390);645	memset(xbow->padding, 0, sizeof(xbow->padding));646	p54_tx(priv, skb);647	return 0;648}649 650int p54_upload_key(struct p54_common *priv, u8 algo, int slot, u8 idx, u8 len,651		   u8 *addr, u8* key)652{653	struct sk_buff *skb;654	struct p54_keycache *rxkey;655 656	skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey),657			    P54_CONTROL_TYPE_RX_KEYCACHE, GFP_KERNEL);658	if (unlikely(!skb))659		return -ENOMEM;660 661	rxkey = skb_put(skb, sizeof(*rxkey));662	rxkey->entry = slot;663	rxkey->key_id = idx;664	rxkey->key_type = algo;665	if (addr)666		memcpy(rxkey->mac, addr, ETH_ALEN);667	else668		eth_broadcast_addr(rxkey->mac);669 670	switch (algo) {671	case P54_CRYPTO_WEP:672	case P54_CRYPTO_AESCCMP:673		rxkey->key_len = min_t(u8, 16, len);674		memcpy(rxkey->key, key, rxkey->key_len);675		break;676 677	case P54_CRYPTO_TKIPMICHAEL:678		rxkey->key_len = 24;679		memcpy(rxkey->key, key, 16);680		memcpy(&(rxkey->key[16]), &(key681			[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]), 8);682		break;683 684	case P54_CRYPTO_NONE:685		rxkey->key_len = 0;686		memset(rxkey->key, 0, sizeof(rxkey->key));687		break;688 689	default:690		wiphy_err(priv->hw->wiphy,691			  "invalid cryptographic algorithm: %d\n", algo);692		dev_kfree_skb(skb);693		return -EINVAL;694	}695 696	p54_tx(priv, skb);697	return 0;698}699 700int p54_fetch_statistics(struct p54_common *priv)701{702	struct ieee80211_tx_info *txinfo;703	struct p54_tx_info *p54info;704	struct sk_buff *skb;705 706	skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL,707			    sizeof(struct p54_statistics),708			    P54_CONTROL_TYPE_STAT_READBACK, GFP_KERNEL);709	if (!skb)710		return -ENOMEM;711 712	/*713	 * The statistic feedback causes some extra headaches here, if it714	 * is not to crash/corrupt the firmware data structures.715	 *716	 * Unlike all other Control Get OIDs we can not use helpers like717	 * skb_put to reserve the space for the data we're requesting.718	 * Instead the extra frame length -which will hold the results later-719	 * will only be told to the p54_assign_address, so that following720	 * frames won't be placed into the  allegedly empty area.721	 */722	txinfo = IEEE80211_SKB_CB(skb);723	p54info = (void *) txinfo->rate_driver_data;724	p54info->extra_len = sizeof(struct p54_statistics);725 726	p54_tx(priv, skb);727	return 0;728}729 730int p54_set_groupfilter(struct p54_common *priv)731{732	struct p54_group_address_table *grp;733	struct sk_buff *skb;734	bool on = false;735 736	skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*grp),737			    P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE, GFP_KERNEL);738	if (!skb)739		return -ENOMEM;740 741	grp = skb_put(skb, sizeof(*grp));742 743	on = !(priv->filter_flags & FIF_ALLMULTI) &&744	     (priv->mc_maclist_num > 0 &&745	      priv->mc_maclist_num <= MC_FILTER_ADDRESS_NUM);746 747	if (on) {748		grp->filter_enable = cpu_to_le16(1);749		grp->num_address = cpu_to_le16(priv->mc_maclist_num);750		memcpy(grp->mac_list, priv->mc_maclist, sizeof(grp->mac_list));751	} else {752		grp->filter_enable = cpu_to_le16(0);753		grp->num_address = cpu_to_le16(0);754		memset(grp->mac_list, 0, sizeof(grp->mac_list));755	}756 757	p54_tx(priv, skb);758	return 0;759}760