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