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