Part of reorganising wireless drivers directory and Kconfig.
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/en/users/Drivers/p54
S: Maintained
-F: drivers/net/wireless/p54/
+F: drivers/net/wireless/intersil/p54/
PA SEMI ETHERNET DRIVER
M: Olof Johansson <olof@lixom.net>
source "drivers/net/wireless/ath/Kconfig"
source "drivers/net/wireless/orinoco/Kconfig"
-source "drivers/net/wireless/p54/Kconfig"
source "drivers/net/wireless/rt2x00/Kconfig"
source "drivers/net/wireless/mediatek/Kconfig"
source "drivers/net/wireless/realtek/rtlwifi/Kconfig"
obj-$(CONFIG_WL_MEDIATEK) += mediatek/
-obj-$(CONFIG_P54_COMMON) += p54/
-
obj-$(CONFIG_ATH_CARDS) += ath/
obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
if WLAN_VENDOR_INTERSIL
source "drivers/net/wireless/intersil/hostap/Kconfig"
+source "drivers/net/wireless/intersil/p54/Kconfig"
endif # WLAN_VENDOR_INTERSIL
obj-$(CONFIG_HOSTAP) += hostap/
+obj-$(CONFIG_P54_COMMON) += p54/
--- /dev/null
+config P54_COMMON
+ tristate "Softmac Prism54 support"
+ depends on MAC80211
+ select FW_LOADER
+ select CRC_CCITT
+ ---help---
+ This is common code for isl38xx/stlc45xx based modules.
+ This module does nothing by itself - the USB/PCI/SPI front-ends
+ also need to be enabled in order to support any devices.
+
+ These devices require softmac firmware which can be found at
+ <http://wireless.kernel.org/en/users/Drivers/p54>
+
+ If you choose to build a module, it'll be called p54common.
+
+config P54_USB
+ tristate "Prism54 USB support"
+ depends on P54_COMMON && USB
+ select CRC32
+ ---help---
+ This driver is for USB isl38xx based wireless cards.
+
+ These devices require softmac firmware which can be found at
+ <http://wireless.kernel.org/en/users/Drivers/p54>
+
+ If you choose to build a module, it'll be called p54usb.
+
+config P54_PCI
+ tristate "Prism54 PCI support"
+ depends on P54_COMMON && PCI
+ ---help---
+ This driver is for PCI isl38xx based wireless cards.
+ This driver supports most devices that are supported by the
+ fullmac prism54 driver plus many devices which are not
+ supported by the fullmac driver/firmware.
+
+ This driver requires softmac firmware which can be found at
+ <http://wireless.kernel.org/en/users/Drivers/p54>
+
+ If you choose to build a module, it'll be called p54pci.
+
+config P54_SPI
+ tristate "Prism54 SPI (stlc45xx) support"
+ depends on P54_COMMON && SPI_MASTER
+ ---help---
+ This driver is for stlc4550 or stlc4560 based wireless chips
+ such as Nokia's N800/N810 Portable Internet Tablet.
+
+ If you choose to build a module, it'll be called p54spi.
+
+config P54_SPI_DEFAULT_EEPROM
+ bool "Include fallback EEPROM blob"
+ depends on P54_SPI
+ default n
+ ---help---
+ Unlike the PCI or USB devices, the SPI variants don't have
+ a dedicated EEPROM chip to store all device specific values
+ for calibration, country and interface settings.
+
+ The driver will try to load the image "3826.eeprom", if the
+ file is put at the right place. (usually /lib/firmware.)
+
+ Only if this request fails, this option will provide a
+ backup set of generic values to get the device working.
+
+ Enabling this option adds about 4k to p54spi.
+
+config P54_LEDS
+ bool
+ depends on P54_COMMON && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = P54_COMMON)
+ default y
--- /dev/null
+p54common-objs := eeprom.o fwio.o txrx.o main.o
+p54common-$(CONFIG_P54_LEDS) += led.o
+
+obj-$(CONFIG_P54_COMMON) += p54common.o
+obj-$(CONFIG_P54_USB) += p54usb.o
+obj-$(CONFIG_P54_PCI) += p54pci.o
+obj-$(CONFIG_P54_SPI) += p54spi.o
--- /dev/null
+/*
+ * EEPROM parser code for mac80211 Prism54 drivers
+ *
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * Based on:
+ * - the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ * - stlc45xx driver
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/sort.h>
+#include <linux/slab.h>
+
+#include <net/mac80211.h>
+#include <linux/crc-ccitt.h>
+#include <linux/export.h>
+
+#include "p54.h"
+#include "eeprom.h"
+#include "lmac.h"
+
+static struct ieee80211_rate p54_bgrates[] = {
+ { .bitrate = 10, .hw_value = 0, },
+ { .bitrate = 20, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 55, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 110, .hw_value = 3, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 60, .hw_value = 4, },
+ { .bitrate = 90, .hw_value = 5, },
+ { .bitrate = 120, .hw_value = 6, },
+ { .bitrate = 180, .hw_value = 7, },
+ { .bitrate = 240, .hw_value = 8, },
+ { .bitrate = 360, .hw_value = 9, },
+ { .bitrate = 480, .hw_value = 10, },
+ { .bitrate = 540, .hw_value = 11, },
+};
+
+static struct ieee80211_rate p54_arates[] = {
+ { .bitrate = 60, .hw_value = 4, },
+ { .bitrate = 90, .hw_value = 5, },
+ { .bitrate = 120, .hw_value = 6, },
+ { .bitrate = 180, .hw_value = 7, },
+ { .bitrate = 240, .hw_value = 8, },
+ { .bitrate = 360, .hw_value = 9, },
+ { .bitrate = 480, .hw_value = 10, },
+ { .bitrate = 540, .hw_value = 11, },
+};
+
+static struct p54_rssi_db_entry p54_rssi_default = {
+ /*
+ * The defaults are taken from usb-logs of the
+ * vendor driver. So, they should be safe to
+ * use in case we can't get a match from the
+ * rssi <-> dBm conversion database.
+ */
+ .mul = 130,
+ .add = -398,
+};
+
+#define CHAN_HAS_CAL BIT(0)
+#define CHAN_HAS_LIMIT BIT(1)
+#define CHAN_HAS_CURVE BIT(2)
+#define CHAN_HAS_ALL (CHAN_HAS_CAL | CHAN_HAS_LIMIT | CHAN_HAS_CURVE)
+
+struct p54_channel_entry {
+ u16 freq;
+ u16 data;
+ int index;
+ int max_power;
+ enum ieee80211_band band;
+};
+
+struct p54_channel_list {
+ struct p54_channel_entry *channels;
+ size_t entries;
+ size_t max_entries;
+ size_t band_channel_num[IEEE80211_NUM_BANDS];
+};
+
+static int p54_get_band_from_freq(u16 freq)
+{
+ /* FIXME: sync these values with the 802.11 spec */
+
+ if ((freq >= 2412) && (freq <= 2484))
+ return IEEE80211_BAND_2GHZ;
+
+ if ((freq >= 4920) && (freq <= 5825))
+ return IEEE80211_BAND_5GHZ;
+
+ return -1;
+}
+
+static int same_band(u16 freq, u16 freq2)
+{
+ return p54_get_band_from_freq(freq) == p54_get_band_from_freq(freq2);
+}
+
+static int p54_compare_channels(const void *_a,
+ const void *_b)
+{
+ const struct p54_channel_entry *a = _a;
+ const struct p54_channel_entry *b = _b;
+
+ return a->freq - b->freq;
+}
+
+static int p54_compare_rssichan(const void *_a,
+ const void *_b)
+{
+ const struct p54_rssi_db_entry *a = _a;
+ const struct p54_rssi_db_entry *b = _b;
+
+ return a->freq - b->freq;
+}
+
+static int p54_fill_band_bitrates(struct ieee80211_hw *dev,
+ struct ieee80211_supported_band *band_entry,
+ enum ieee80211_band band)
+{
+ /* TODO: generate rate array dynamically */
+
+ switch (band) {
+ case IEEE80211_BAND_2GHZ:
+ band_entry->bitrates = p54_bgrates;
+ band_entry->n_bitrates = ARRAY_SIZE(p54_bgrates);
+ break;
+ case IEEE80211_BAND_5GHZ:
+ band_entry->bitrates = p54_arates;
+ band_entry->n_bitrates = ARRAY_SIZE(p54_arates);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int p54_generate_band(struct ieee80211_hw *dev,
+ struct p54_channel_list *list,
+ unsigned int *chan_num,
+ enum ieee80211_band band)
+{
+ struct p54_common *priv = dev->priv;
+ struct ieee80211_supported_band *tmp, *old;
+ unsigned int i, j;
+ int ret = -ENOMEM;
+
+ if ((!list->entries) || (!list->band_channel_num[band]))
+ return -EINVAL;
+
+ tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ goto err_out;
+
+ tmp->channels = kzalloc(sizeof(struct ieee80211_channel) *
+ list->band_channel_num[band], GFP_KERNEL);
+ if (!tmp->channels)
+ goto err_out;
+
+ ret = p54_fill_band_bitrates(dev, tmp, band);
+ if (ret)
+ goto err_out;
+
+ for (i = 0, j = 0; (j < list->band_channel_num[band]) &&
+ (i < list->entries); i++) {
+ struct p54_channel_entry *chan = &list->channels[i];
+ struct ieee80211_channel *dest = &tmp->channels[j];
+
+ if (chan->band != band)
+ continue;
+
+ if (chan->data != CHAN_HAS_ALL) {
+ wiphy_err(dev->wiphy, "%s%s%s is/are missing for "
+ "channel:%d [%d MHz].\n",
+ (chan->data & CHAN_HAS_CAL ? "" :
+ " [iqauto calibration data]"),
+ (chan->data & CHAN_HAS_LIMIT ? "" :
+ " [output power limits]"),
+ (chan->data & CHAN_HAS_CURVE ? "" :
+ " [curve data]"),
+ chan->index, chan->freq);
+ continue;
+ }
+
+ dest->band = chan->band;
+ dest->center_freq = chan->freq;
+ dest->max_power = chan->max_power;
+ priv->survey[*chan_num].channel = &tmp->channels[j];
+ priv->survey[*chan_num].filled = SURVEY_INFO_NOISE_DBM |
+ SURVEY_INFO_TIME |
+ SURVEY_INFO_TIME_BUSY |
+ SURVEY_INFO_TIME_TX;
+ dest->hw_value = (*chan_num);
+ j++;
+ (*chan_num)++;
+ }
+
+ if (j == 0) {
+ wiphy_err(dev->wiphy, "Disabling totally damaged %d GHz band\n",
+ (band == IEEE80211_BAND_2GHZ) ? 2 : 5);
+
+ ret = -ENODATA;
+ goto err_out;
+ }
+
+ tmp->n_channels = j;
+ old = priv->band_table[band];
+ priv->band_table[band] = tmp;
+ if (old) {
+ kfree(old->channels);
+ kfree(old);
+ }
+
+ return 0;
+
+err_out:
+ if (tmp) {
+ kfree(tmp->channels);
+ kfree(tmp);
+ }
+
+ return ret;
+}
+
+static struct p54_channel_entry *p54_update_channel_param(struct p54_channel_list *list,
+ u16 freq, u16 data)
+{
+ int i;
+ struct p54_channel_entry *entry = NULL;
+
+ /*
+ * usually all lists in the eeprom are mostly sorted.
+ * so it's very likely that the entry we are looking for
+ * is right at the end of the list
+ */
+ for (i = list->entries; i >= 0; i--) {
+ if (freq == list->channels[i].freq) {
+ entry = &list->channels[i];
+ break;
+ }
+ }
+
+ if ((i < 0) && (list->entries < list->max_entries)) {
+ /* entry does not exist yet. Initialize a new one. */
+ int band = p54_get_band_from_freq(freq);
+
+ /*
+ * filter out frequencies which don't belong into
+ * any supported band.
+ */
+ if (band >= 0) {
+ i = list->entries++;
+ list->band_channel_num[band]++;
+
+ entry = &list->channels[i];
+ entry->freq = freq;
+ entry->band = band;
+ entry->index = ieee80211_frequency_to_channel(freq);
+ entry->max_power = 0;
+ entry->data = 0;
+ }
+ }
+
+ if (entry)
+ entry->data |= data;
+
+ return entry;
+}
+
+static int p54_get_maxpower(struct p54_common *priv, void *data)
+{
+ switch (priv->rxhw & PDR_SYNTH_FRONTEND_MASK) {
+ case PDR_SYNTH_FRONTEND_LONGBOW: {
+ struct pda_channel_output_limit_longbow *pda = data;
+ int j;
+ u16 rawpower = 0;
+ pda = data;
+ for (j = 0; j < ARRAY_SIZE(pda->point); j++) {
+ struct pda_channel_output_limit_point_longbow *point =
+ &pda->point[j];
+ rawpower = max_t(u16,
+ rawpower, le16_to_cpu(point->val_qpsk));
+ rawpower = max_t(u16,
+ rawpower, le16_to_cpu(point->val_bpsk));
+ rawpower = max_t(u16,
+ rawpower, le16_to_cpu(point->val_16qam));
+ rawpower = max_t(u16,
+ rawpower, le16_to_cpu(point->val_64qam));
+ }
+ /* longbow seems to use 1/16 dBm units */
+ return rawpower / 16;
+ }
+
+ case PDR_SYNTH_FRONTEND_DUETTE3:
+ case PDR_SYNTH_FRONTEND_DUETTE2:
+ case PDR_SYNTH_FRONTEND_FRISBEE:
+ case PDR_SYNTH_FRONTEND_XBOW: {
+ struct pda_channel_output_limit *pda = data;
+ u8 rawpower = 0;
+ rawpower = max(rawpower, pda->val_qpsk);
+ rawpower = max(rawpower, pda->val_bpsk);
+ rawpower = max(rawpower, pda->val_16qam);
+ rawpower = max(rawpower, pda->val_64qam);
+ /* raw values are in 1/4 dBm units */
+ return rawpower / 4;
+ }
+
+ default:
+ return 20;
+ }
+}
+
+static int p54_generate_channel_lists(struct ieee80211_hw *dev)
+{
+ struct p54_common *priv = dev->priv;
+ struct p54_channel_list *list;
+ unsigned int i, j, k, max_channel_num;
+ int ret = 0;
+ u16 freq;
+
+ if ((priv->iq_autocal_len != priv->curve_data->entries) ||
+ (priv->iq_autocal_len != priv->output_limit->entries))
+ wiphy_err(dev->wiphy,
+ "Unsupported or damaged EEPROM detected. "
+ "You may not be able to use all channels.\n");
+
+ max_channel_num = max_t(unsigned int, priv->output_limit->entries,
+ priv->iq_autocal_len);
+ max_channel_num = max_t(unsigned int, max_channel_num,
+ priv->curve_data->entries);
+
+ list = kzalloc(sizeof(*list), GFP_KERNEL);
+ if (!list) {
+ ret = -ENOMEM;
+ goto free;
+ }
+ priv->chan_num = max_channel_num;
+ priv->survey = kzalloc(sizeof(struct survey_info) * max_channel_num,
+ GFP_KERNEL);
+ if (!priv->survey) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ list->max_entries = max_channel_num;
+ list->channels = kzalloc(sizeof(struct p54_channel_entry) *
+ max_channel_num, GFP_KERNEL);
+ if (!list->channels) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ for (i = 0; i < max_channel_num; i++) {
+ if (i < priv->iq_autocal_len) {
+ freq = le16_to_cpu(priv->iq_autocal[i].freq);
+ p54_update_channel_param(list, freq, CHAN_HAS_CAL);
+ }
+
+ if (i < priv->output_limit->entries) {
+ struct p54_channel_entry *tmp;
+
+ void *data = (void *) ((unsigned long) i *
+ priv->output_limit->entry_size +
+ priv->output_limit->offset +
+ priv->output_limit->data);
+
+ freq = le16_to_cpup((__le16 *) data);
+ tmp = p54_update_channel_param(list, freq,
+ CHAN_HAS_LIMIT);
+ if (tmp) {
+ tmp->max_power = p54_get_maxpower(priv, data);
+ }
+ }
+
+ if (i < priv->curve_data->entries) {
+ freq = le16_to_cpup((__le16 *) (i *
+ priv->curve_data->entry_size +
+ priv->curve_data->offset +
+ priv->curve_data->data));
+
+ p54_update_channel_param(list, freq, CHAN_HAS_CURVE);
+ }
+ }
+
+ /* sort the channel list by frequency */
+ sort(list->channels, list->entries, sizeof(struct p54_channel_entry),
+ p54_compare_channels, NULL);
+
+ k = 0;
+ for (i = 0, j = 0; i < IEEE80211_NUM_BANDS; i++) {
+ if (p54_generate_band(dev, list, &k, i) == 0)
+ j++;
+ }
+ if (j == 0) {
+ /* no useable band available. */
+ ret = -EINVAL;
+ }
+
+free:
+ if (list) {
+ kfree(list->channels);
+ kfree(list);
+ }
+ if (ret) {
+ kfree(priv->survey);
+ priv->survey = NULL;
+ }
+
+ return ret;
+}
+
+static int p54_convert_rev0(struct ieee80211_hw *dev,
+ struct pda_pa_curve_data *curve_data)
+{
+ struct p54_common *priv = dev->priv;
+ struct p54_pa_curve_data_sample *dst;
+ struct pda_pa_curve_data_sample_rev0 *src;
+ size_t cd_len = sizeof(*curve_data) +
+ (curve_data->points_per_channel*sizeof(*dst) + 2) *
+ curve_data->channels;
+ unsigned int i, j;
+ void *source, *target;
+
+ priv->curve_data = kmalloc(sizeof(*priv->curve_data) + cd_len,
+ GFP_KERNEL);
+ if (!priv->curve_data)
+ return -ENOMEM;
+
+ priv->curve_data->entries = curve_data->channels;
+ priv->curve_data->entry_size = sizeof(__le16) +
+ sizeof(*dst) * curve_data->points_per_channel;
+ priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data);
+ priv->curve_data->len = cd_len;
+ memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data));
+ source = curve_data->data;
+ target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data;
+ for (i = 0; i < curve_data->channels; i++) {
+ __le16 *freq = source;
+ source += sizeof(__le16);
+ *((__le16 *)target) = *freq;
+ target += sizeof(__le16);
+ for (j = 0; j < curve_data->points_per_channel; j++) {
+ dst = target;
+ src = source;
+
+ dst->rf_power = src->rf_power;
+ dst->pa_detector = src->pa_detector;
+ dst->data_64qam = src->pcv;
+ /* "invent" the points for the other modulations */
+#define SUB(x, y) (u8)(((x) - (y)) > (x) ? 0 : (x) - (y))
+ dst->data_16qam = SUB(src->pcv, 12);
+ dst->data_qpsk = SUB(dst->data_16qam, 12);
+ dst->data_bpsk = SUB(dst->data_qpsk, 12);
+ dst->data_barker = SUB(dst->data_bpsk, 14);
+#undef SUB
+ target += sizeof(*dst);
+ source += sizeof(*src);
+ }
+ }
+
+ return 0;
+}
+
+static int p54_convert_rev1(struct ieee80211_hw *dev,
+ struct pda_pa_curve_data *curve_data)
+{
+ struct p54_common *priv = dev->priv;
+ struct p54_pa_curve_data_sample *dst;
+ struct pda_pa_curve_data_sample_rev1 *src;
+ size_t cd_len = sizeof(*curve_data) +
+ (curve_data->points_per_channel*sizeof(*dst) + 2) *
+ curve_data->channels;
+ unsigned int i, j;
+ void *source, *target;
+
+ priv->curve_data = kzalloc(cd_len + sizeof(*priv->curve_data),
+ GFP_KERNEL);
+ if (!priv->curve_data)
+ return -ENOMEM;
+
+ priv->curve_data->entries = curve_data->channels;
+ priv->curve_data->entry_size = sizeof(__le16) +
+ sizeof(*dst) * curve_data->points_per_channel;
+ priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data);
+ priv->curve_data->len = cd_len;
+ memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data));
+ source = curve_data->data;
+ target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data;
+ for (i = 0; i < curve_data->channels; i++) {
+ __le16 *freq = source;
+ source += sizeof(__le16);
+ *((__le16 *)target) = *freq;
+ target += sizeof(__le16);
+ for (j = 0; j < curve_data->points_per_channel; j++) {
+ memcpy(target, source, sizeof(*src));
+
+ target += sizeof(*dst);
+ source += sizeof(*src);
+ }
+ source++;
+ }
+
+ return 0;
+}
+
+static const char *p54_rf_chips[] = { "INVALID-0", "Duette3", "Duette2",
+ "Frisbee", "Xbow", "Longbow", "INVALID-6", "INVALID-7" };
+
+static int p54_parse_rssical(struct ieee80211_hw *dev,
+ u8 *data, int len, u16 type)
+{
+ struct p54_common *priv = dev->priv;
+ struct p54_rssi_db_entry *entry;
+ size_t db_len, entries;
+ int offset = 0, i;
+
+ if (type != PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) {
+ entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2;
+ if (len != sizeof(struct pda_rssi_cal_entry) * entries) {
+ wiphy_err(dev->wiphy, "rssical size mismatch.\n");
+ goto err_data;
+ }
+ } else {
+ /*
+ * Some devices (Dell 1450 USB, Xbow 5GHz card, etc...)
+ * have an empty two byte header.
+ */
+ if (*((__le16 *)&data[offset]) == cpu_to_le16(0))
+ offset += 2;
+
+ entries = (len - offset) /
+ sizeof(struct pda_rssi_cal_ext_entry);
+
+ if (len < offset ||
+ (len - offset) % sizeof(struct pda_rssi_cal_ext_entry) ||
+ entries == 0) {
+ wiphy_err(dev->wiphy, "invalid rssi database.\n");
+ goto err_data;
+ }
+ }
+
+ db_len = sizeof(*entry) * entries;
+ priv->rssi_db = kzalloc(db_len + sizeof(*priv->rssi_db), GFP_KERNEL);
+ if (!priv->rssi_db)
+ return -ENOMEM;
+
+ priv->rssi_db->offset = 0;
+ priv->rssi_db->entries = entries;
+ priv->rssi_db->entry_size = sizeof(*entry);
+ priv->rssi_db->len = db_len;
+
+ entry = (void *)((unsigned long)priv->rssi_db->data + priv->rssi_db->offset);
+ if (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) {
+ struct pda_rssi_cal_ext_entry *cal = (void *) &data[offset];
+
+ for (i = 0; i < entries; i++) {
+ entry[i].freq = le16_to_cpu(cal[i].freq);
+ entry[i].mul = (s16) le16_to_cpu(cal[i].mul);
+ entry[i].add = (s16) le16_to_cpu(cal[i].add);
+ }
+ } else {
+ struct pda_rssi_cal_entry *cal = (void *) &data[offset];
+
+ for (i = 0; i < entries; i++) {
+ u16 freq = 0;
+ switch (i) {
+ case IEEE80211_BAND_2GHZ:
+ freq = 2437;
+ break;
+ case IEEE80211_BAND_5GHZ:
+ freq = 5240;
+ break;
+ }
+
+ entry[i].freq = freq;
+ entry[i].mul = (s16) le16_to_cpu(cal[i].mul);
+ entry[i].add = (s16) le16_to_cpu(cal[i].add);
+ }
+ }
+
+ /* sort the list by channel frequency */
+ sort(entry, entries, sizeof(*entry), p54_compare_rssichan, NULL);
+ return 0;
+
+err_data:
+ wiphy_err(dev->wiphy,
+ "rssi calibration data packing type:(%x) len:%d.\n",
+ type, len);
+
+ print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE, data, len);
+
+ wiphy_err(dev->wiphy, "please report this issue.\n");
+ return -EINVAL;
+}
+
+struct p54_rssi_db_entry *p54_rssi_find(struct p54_common *priv, const u16 freq)
+{
+ struct p54_rssi_db_entry *entry;
+ int i, found = -1;
+
+ if (!priv->rssi_db)
+ return &p54_rssi_default;
+
+ entry = (void *)(priv->rssi_db->data + priv->rssi_db->offset);
+ for (i = 0; i < priv->rssi_db->entries; i++) {
+ if (!same_band(freq, entry[i].freq))
+ continue;
+
+ if (found == -1) {
+ found = i;
+ continue;
+ }
+
+ /* nearest match */
+ if (abs(freq - entry[i].freq) <
+ abs(freq - entry[found].freq)) {
+ found = i;
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ return found < 0 ? &p54_rssi_default : &entry[found];
+}
+
+static void p54_parse_default_country(struct ieee80211_hw *dev,
+ void *data, int len)
+{
+ struct pda_country *country;
+
+ if (len != sizeof(*country)) {
+ wiphy_err(dev->wiphy,
+ "found possible invalid default country eeprom entry. (entry size: %d)\n",
+ len);
+
+ print_hex_dump_bytes("country:", DUMP_PREFIX_NONE,
+ data, len);
+
+ wiphy_err(dev->wiphy, "please report this issue.\n");
+ return;
+ }
+
+ country = (struct pda_country *) data;
+ if (country->flags == PDR_COUNTRY_CERT_CODE_PSEUDO)
+ regulatory_hint(dev->wiphy, country->alpha2);
+ else {
+ /* TODO:
+ * write a shared/common function that converts
+ * "Regulatory domain codes" (802.11-2007 14.8.2.2)
+ * into ISO/IEC 3166-1 alpha2 for regulatory_hint.
+ */
+ }
+}
+
+static int p54_convert_output_limits(struct ieee80211_hw *dev,
+ u8 *data, size_t len)
+{
+ struct p54_common *priv = dev->priv;
+
+ if (len < 2)
+ return -EINVAL;
+
+ if (data[0] != 0) {
+ wiphy_err(dev->wiphy, "unknown output power db revision:%x\n",
+ data[0]);
+ return -EINVAL;
+ }
+
+ if (2 + data[1] * sizeof(struct pda_channel_output_limit) > len)
+ return -EINVAL;
+
+ priv->output_limit = kmalloc(data[1] *
+ sizeof(struct pda_channel_output_limit) +
+ sizeof(*priv->output_limit), GFP_KERNEL);
+
+ if (!priv->output_limit)
+ return -ENOMEM;
+
+ priv->output_limit->offset = 0;
+ priv->output_limit->entries = data[1];
+ priv->output_limit->entry_size =
+ sizeof(struct pda_channel_output_limit);
+ priv->output_limit->len = priv->output_limit->entry_size *
+ priv->output_limit->entries +
+ priv->output_limit->offset;
+
+ memcpy(priv->output_limit->data, &data[2],
+ data[1] * sizeof(struct pda_channel_output_limit));
+
+ return 0;
+}
+
+static struct p54_cal_database *p54_convert_db(struct pda_custom_wrapper *src,
+ size_t total_len)
+{
+ struct p54_cal_database *dst;
+ size_t payload_len, entries, entry_size, offset;
+
+ payload_len = le16_to_cpu(src->len);
+ entries = le16_to_cpu(src->entries);
+ entry_size = le16_to_cpu(src->entry_size);
+ offset = le16_to_cpu(src->offset);
+ if (((entries * entry_size + offset) != payload_len) ||
+ (payload_len + sizeof(*src) != total_len))
+ return NULL;
+
+ dst = kmalloc(sizeof(*dst) + payload_len, GFP_KERNEL);
+ if (!dst)
+ return NULL;
+
+ dst->entries = entries;
+ dst->entry_size = entry_size;
+ dst->offset = offset;
+ dst->len = payload_len;
+
+ memcpy(dst->data, src->data, payload_len);
+ return dst;
+}
+
+int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
+{
+ struct p54_common *priv = dev->priv;
+ struct eeprom_pda_wrap *wrap;
+ struct pda_entry *entry;
+ unsigned int data_len, entry_len;
+ void *tmp;
+ int err;
+ u8 *end = (u8 *)eeprom + len;
+ u16 synth = 0;
+ u16 crc16 = ~0;
+
+ wrap = (struct eeprom_pda_wrap *) eeprom;
+ entry = (void *)wrap->data + le16_to_cpu(wrap->len);
+
+ /* verify that at least the entry length/code fits */
+ while ((u8 *)entry <= end - sizeof(*entry)) {
+ entry_len = le16_to_cpu(entry->len);
+ data_len = ((entry_len - 1) << 1);
+
+ /* abort if entry exceeds whole structure */
+ if ((u8 *)entry + sizeof(*entry) + data_len > end)
+ break;
+
+ switch (le16_to_cpu(entry->code)) {
+ case PDR_MAC_ADDRESS:
+ if (data_len != ETH_ALEN)
+ break;
+ SET_IEEE80211_PERM_ADDR(dev, entry->data);
+ break;
+ case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS:
+ if (priv->output_limit)
+ break;
+ err = p54_convert_output_limits(dev, entry->data,
+ data_len);
+ if (err)
+ goto err;
+ break;
+ case PDR_PRISM_PA_CAL_CURVE_DATA: {
+ struct pda_pa_curve_data *curve_data =
+ (struct pda_pa_curve_data *)entry->data;
+ if (data_len < sizeof(*curve_data)) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ switch (curve_data->cal_method_rev) {
+ case 0:
+ err = p54_convert_rev0(dev, curve_data);
+ break;
+ case 1:
+ err = p54_convert_rev1(dev, curve_data);
+ break;
+ default:
+ wiphy_err(dev->wiphy,
+ "unknown curve data revision %d\n",
+ curve_data->cal_method_rev);
+ err = -ENODEV;
+ break;
+ }
+ if (err)
+ goto err;
+ }
+ break;
+ case PDR_PRISM_ZIF_TX_IQ_CALIBRATION:
+ priv->iq_autocal = kmemdup(entry->data, data_len,
+ GFP_KERNEL);
+ if (!priv->iq_autocal) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ priv->iq_autocal_len = data_len / sizeof(struct pda_iq_autocal_entry);
+ break;
+ case PDR_DEFAULT_COUNTRY:
+ p54_parse_default_country(dev, entry->data, data_len);
+ break;
+ case PDR_INTERFACE_LIST:
+ tmp = entry->data;
+ while ((u8 *)tmp < entry->data + data_len) {
+ struct exp_if *exp_if = tmp;
+ if (exp_if->if_id == cpu_to_le16(IF_ID_ISL39000))
+ synth = le16_to_cpu(exp_if->variant);
+ tmp += sizeof(*exp_if);
+ }
+ break;
+ case PDR_HARDWARE_PLATFORM_COMPONENT_ID:
+ if (data_len < 2)
+ break;
+ priv->version = *(u8 *)(entry->data + 1);
+ break;
+ case PDR_RSSI_LINEAR_APPROXIMATION:
+ case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND:
+ case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED:
+ err = p54_parse_rssical(dev, entry->data, data_len,
+ le16_to_cpu(entry->code));
+ if (err)
+ goto err;
+ break;
+ case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2: {
+ struct pda_custom_wrapper *pda = (void *) entry->data;
+ __le16 *src;
+ u16 *dst;
+ int i;
+
+ if (priv->rssi_db || data_len < sizeof(*pda))
+ break;
+
+ priv->rssi_db = p54_convert_db(pda, data_len);
+ if (!priv->rssi_db)
+ break;
+
+ src = (void *) priv->rssi_db->data;
+ dst = (void *) priv->rssi_db->data;
+
+ for (i = 0; i < priv->rssi_db->entries; i++)
+ *(dst++) = (s16) le16_to_cpu(*(src++));
+
+ }
+ break;
+ case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: {
+ struct pda_custom_wrapper *pda = (void *) entry->data;
+ if (priv->output_limit || data_len < sizeof(*pda))
+ break;
+ priv->output_limit = p54_convert_db(pda, data_len);
+ }
+ break;
+ case PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM: {
+ struct pda_custom_wrapper *pda = (void *) entry->data;
+ if (priv->curve_data || data_len < sizeof(*pda))
+ break;
+ priv->curve_data = p54_convert_db(pda, data_len);
+ }
+ break;
+ case PDR_END:
+ crc16 = ~crc_ccitt(crc16, (u8 *) entry, sizeof(*entry));
+ if (crc16 != le16_to_cpup((__le16 *)entry->data)) {
+ wiphy_err(dev->wiphy, "eeprom failed checksum "
+ "test!\n");
+ err = -ENOMSG;
+ goto err;
+ } else {
+ goto good_eeprom;
+ }
+ break;
+ default:
+ break;
+ }
+
+ crc16 = crc_ccitt(crc16, (u8 *)entry, (entry_len + 1) * 2);
+ entry = (void *)entry + (entry_len + 1) * 2;
+ }
+
+ wiphy_err(dev->wiphy, "unexpected end of eeprom data.\n");
+ err = -ENODATA;
+ goto err;
+
+good_eeprom:
+ if (!synth || !priv->iq_autocal || !priv->output_limit ||
+ !priv->curve_data) {
+ wiphy_err(dev->wiphy,
+ "not all required entries found in eeprom!\n");
+ err = -EINVAL;
+ goto err;
+ }
+
+ priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK;
+
+ err = p54_generate_channel_lists(dev);
+ if (err)
+ goto err;
+
+ if (priv->rxhw == PDR_SYNTH_FRONTEND_XBOW)
+ p54_init_xbow_synth(priv);
+ if (!(synth & PDR_SYNTH_24_GHZ_DISABLED))
+ dev->wiphy->bands[IEEE80211_BAND_2GHZ] =
+ priv->band_table[IEEE80211_BAND_2GHZ];
+ if (!(synth & PDR_SYNTH_5_GHZ_DISABLED))
+ dev->wiphy->bands[IEEE80211_BAND_5GHZ] =
+ priv->band_table[IEEE80211_BAND_5GHZ];
+ if ((synth & PDR_SYNTH_RX_DIV_MASK) == PDR_SYNTH_RX_DIV_SUPPORTED)
+ priv->rx_diversity_mask = 3;
+ if ((synth & PDR_SYNTH_TX_DIV_MASK) == PDR_SYNTH_TX_DIV_SUPPORTED)
+ priv->tx_diversity_mask = 3;
+
+ if (!is_valid_ether_addr(dev->wiphy->perm_addr)) {
+ u8 perm_addr[ETH_ALEN];
+
+ wiphy_warn(dev->wiphy,
+ "Invalid hwaddr! Using randomly generated MAC addr\n");
+ eth_random_addr(perm_addr);
+ SET_IEEE80211_PERM_ADDR(dev, perm_addr);
+ }
+
+ priv->cur_rssi = &p54_rssi_default;
+
+ wiphy_info(dev->wiphy, "hwaddr %pM, MAC:isl38%02x RF:%s\n",
+ dev->wiphy->perm_addr, priv->version,
+ p54_rf_chips[priv->rxhw]);
+
+ return 0;
+
+err:
+ kfree(priv->iq_autocal);
+ kfree(priv->output_limit);
+ kfree(priv->curve_data);
+ kfree(priv->rssi_db);
+ kfree(priv->survey);
+ priv->iq_autocal = NULL;
+ priv->output_limit = NULL;
+ priv->curve_data = NULL;
+ priv->rssi_db = NULL;
+ priv->survey = NULL;
+
+ wiphy_err(dev->wiphy, "eeprom parse failed!\n");
+ return err;
+}
+EXPORT_SYMBOL_GPL(p54_parse_eeprom);
+
+int p54_read_eeprom(struct ieee80211_hw *dev)
+{
+ struct p54_common *priv = dev->priv;
+ size_t eeprom_size = 0x2020, offset = 0, blocksize, maxblocksize;
+ int ret = -ENOMEM;
+ void *eeprom;
+
+ maxblocksize = EEPROM_READBACK_LEN;
+ if (priv->fw_var >= 0x509)
+ maxblocksize -= 0xc;
+ else
+ maxblocksize -= 0x4;
+
+ eeprom = kzalloc(eeprom_size, GFP_KERNEL);
+ if (unlikely(!eeprom))
+ goto free;
+
+ while (eeprom_size) {
+ blocksize = min(eeprom_size, maxblocksize);
+ ret = p54_download_eeprom(priv, eeprom + offset,
+ offset, blocksize);
+ if (unlikely(ret))
+ goto free;
+
+ offset += blocksize;
+ eeprom_size -= blocksize;
+ }
+
+ ret = p54_parse_eeprom(dev, eeprom, offset);
+free:
+ kfree(eeprom);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(p54_read_eeprom);
--- /dev/null
+/*
+ * eeprom specific definitions for mac80211 Prism54 drivers
+ *
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
+ *
+ * Based on:
+ * - the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ *
+ * - LMAC API interface header file for STLC4560 (lmac_longbow.h)
+ * Copyright (C) 2007 Conexant Systems, Inc.
+ *
+ * - islmvc driver
+ * Copyright (C) 2001 Intersil Americas Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef EEPROM_H
+#define EEPROM_H
+
+/* PDA defines are Copyright (C) 2005 Nokia Corporation (taken from islsm_pda.h) */
+
+struct pda_entry {
+ __le16 len; /* includes both code and data */
+ __le16 code;
+ u8 data[0];
+} __packed;
+
+struct eeprom_pda_wrap {
+ __le32 magic;
+ __le16 pad;
+ __le16 len;
+ __le32 arm_opcode;
+ u8 data[0];
+} __packed;
+
+struct p54_iq_autocal_entry {
+ __le16 iq_param[4];
+} __packed;
+
+struct pda_iq_autocal_entry {
+ __le16 freq;
+ struct p54_iq_autocal_entry params;
+} __packed;
+
+struct pda_channel_output_limit {
+ __le16 freq;
+ u8 val_bpsk;
+ u8 val_qpsk;
+ u8 val_16qam;
+ u8 val_64qam;
+ u8 rate_set_mask;
+ u8 rate_set_size;
+} __packed;
+
+struct pda_channel_output_limit_point_longbow {
+ __le16 val_bpsk;
+ __le16 val_qpsk;
+ __le16 val_16qam;
+ __le16 val_64qam;
+} __packed;
+
+struct pda_channel_output_limit_longbow {
+ __le16 freq;
+ struct pda_channel_output_limit_point_longbow point[3];
+} __packed;
+
+struct pda_pa_curve_data_sample_rev0 {
+ u8 rf_power;
+ u8 pa_detector;
+ u8 pcv;
+} __packed;
+
+struct pda_pa_curve_data_sample_rev1 {
+ u8 rf_power;
+ u8 pa_detector;
+ u8 data_barker;
+ u8 data_bpsk;
+ u8 data_qpsk;
+ u8 data_16qam;
+ u8 data_64qam;
+} __packed;
+
+struct pda_pa_curve_data {
+ u8 cal_method_rev;
+ u8 channels;
+ u8 points_per_channel;
+ u8 padding;
+ u8 data[0];
+} __packed;
+
+struct pda_rssi_cal_ext_entry {
+ __le16 freq;
+ __le16 mul;
+ __le16 add;
+} __packed;
+
+struct pda_rssi_cal_entry {
+ __le16 mul;
+ __le16 add;
+} __packed;
+
+struct pda_country {
+ u8 regdomain;
+ u8 alpha2[2];
+ u8 flags;
+} __packed;
+
+struct pda_antenna_gain {
+ struct {
+ u8 gain_5GHz; /* 0.25 dBi units */
+ u8 gain_2GHz; /* 0.25 dBi units */
+ } __packed antenna[0];
+} __packed;
+
+struct pda_custom_wrapper {
+ __le16 entries;
+ __le16 entry_size;
+ __le16 offset;
+ __le16 len;
+ u8 data[0];
+} __packed;
+
+/*
+ * this defines the PDR codes used to build PDAs as defined in document
+ * number 553155. The current implementation mirrors version 1.1 of the
+ * document and lists only PDRs supported by the ARM platform.
+ */
+
+/* common and choice range (0x0000 - 0x0fff) */
+#define PDR_END 0x0000
+#define PDR_MANUFACTURING_PART_NUMBER 0x0001
+#define PDR_PDA_VERSION 0x0002
+#define PDR_NIC_SERIAL_NUMBER 0x0003
+#define PDR_NIC_RAM_SIZE 0x0005
+#define PDR_RFMODEM_SUP_RANGE 0x0006
+#define PDR_PRISM_MAC_SUP_RANGE 0x0007
+#define PDR_NIC_ID 0x0008
+
+#define PDR_MAC_ADDRESS 0x0101
+#define PDR_REGULATORY_DOMAIN_LIST 0x0103 /* obsolete */
+#define PDR_ALLOWED_CHAN_SET 0x0104
+#define PDR_DEFAULT_CHAN 0x0105
+#define PDR_TEMPERATURE_TYPE 0x0107
+
+#define PDR_IFR_SETTING 0x0200
+#define PDR_RFR_SETTING 0x0201
+#define PDR_3861_BASELINE_REG_SETTINGS 0x0202
+#define PDR_3861_SHADOW_REG_SETTINGS 0x0203
+#define PDR_3861_IFRF_REG_SETTINGS 0x0204
+
+#define PDR_3861_CHAN_CALIB_SET_POINTS 0x0300
+#define PDR_3861_CHAN_CALIB_INTEGRATOR 0x0301
+
+#define PDR_3842_PRISM_II_NIC_CONFIG 0x0400
+#define PDR_PRISM_USB_ID 0x0401
+#define PDR_PRISM_PCI_ID 0x0402
+#define PDR_PRISM_PCI_IF_CONFIG 0x0403
+#define PDR_PRISM_PCI_PM_CONFIG 0x0404
+
+#define PDR_3861_MF_TEST_CHAN_SET_POINTS 0x0900
+#define PDR_3861_MF_TEST_CHAN_INTEGRATORS 0x0901
+
+/* ARM range (0x1000 - 0x1fff) */
+#define PDR_COUNTRY_INFORMATION 0x1000 /* obsolete */
+#define PDR_INTERFACE_LIST 0x1001
+#define PDR_HARDWARE_PLATFORM_COMPONENT_ID 0x1002
+#define PDR_OEM_NAME 0x1003
+#define PDR_PRODUCT_NAME 0x1004
+#define PDR_UTF8_OEM_NAME 0x1005
+#define PDR_UTF8_PRODUCT_NAME 0x1006
+#define PDR_COUNTRY_LIST 0x1007
+#define PDR_DEFAULT_COUNTRY 0x1008
+
+#define PDR_ANTENNA_GAIN 0x1100
+
+#define PDR_PRISM_INDIGO_PA_CALIBRATION_DATA 0x1901
+#define PDR_RSSI_LINEAR_APPROXIMATION 0x1902
+#define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS 0x1903
+#define PDR_PRISM_PA_CAL_CURVE_DATA 0x1904
+#define PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND 0x1905
+#define PDR_PRISM_ZIF_TX_IQ_CALIBRATION 0x1906
+#define PDR_REGULATORY_POWER_LIMITS 0x1907
+#define PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED 0x1908
+#define PDR_RADIATED_TRANSMISSION_CORRECTION 0x1909
+#define PDR_PRISM_TX_IQ_CALIBRATION 0x190a
+
+/* reserved range (0x2000 - 0x7fff) */
+
+/* customer range (0x8000 - 0xffff) */
+#define PDR_BASEBAND_REGISTERS 0x8000
+#define PDR_PER_CHANNEL_BASEBAND_REGISTERS 0x8001
+
+/* used by our modificated eeprom image */
+#define PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM 0xDEAD
+#define PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2 0xCAFF
+#define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM 0xBEEF
+#define PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM 0xB05D
+
+/* Interface Definitions */
+#define PDR_INTERFACE_ROLE_SERVER 0x0000
+#define PDR_INTERFACE_ROLE_CLIENT 0x0001
+
+/* PDR definitions for default country & country list */
+#define PDR_COUNTRY_CERT_CODE 0x80
+#define PDR_COUNTRY_CERT_CODE_REAL 0x00
+#define PDR_COUNTRY_CERT_CODE_PSEUDO 0x80
+#define PDR_COUNTRY_CERT_BAND 0x40
+#define PDR_COUNTRY_CERT_BAND_2GHZ 0x00
+#define PDR_COUNTRY_CERT_BAND_5GHZ 0x40
+#define PDR_COUNTRY_CERT_IODOOR 0x30
+#define PDR_COUNTRY_CERT_IODOOR_BOTH 0x00
+#define PDR_COUNTRY_CERT_IODOOR_INDOOR 0x20
+#define PDR_COUNTRY_CERT_IODOOR_OUTDOOR 0x30
+#define PDR_COUNTRY_CERT_INDEX 0x0f
+
+/* Specific LMAC FW/HW variant definitions */
+#define PDR_SYNTH_FRONTEND_MASK 0x0007
+#define PDR_SYNTH_FRONTEND_DUETTE3 0x0001
+#define PDR_SYNTH_FRONTEND_DUETTE2 0x0002
+#define PDR_SYNTH_FRONTEND_FRISBEE 0x0003
+#define PDR_SYNTH_FRONTEND_XBOW 0x0004
+#define PDR_SYNTH_FRONTEND_LONGBOW 0x0005
+#define PDR_SYNTH_IQ_CAL_MASK 0x0018
+#define PDR_SYNTH_IQ_CAL_PA_DETECTOR 0x0000
+#define PDR_SYNTH_IQ_CAL_DISABLED 0x0008
+#define PDR_SYNTH_IQ_CAL_ZIF 0x0010
+#define PDR_SYNTH_FAA_SWITCH_MASK 0x0020
+#define PDR_SYNTH_FAA_SWITCH_ENABLED 0x0020
+#define PDR_SYNTH_24_GHZ_MASK 0x0040
+#define PDR_SYNTH_24_GHZ_DISABLED 0x0040
+#define PDR_SYNTH_5_GHZ_MASK 0x0080
+#define PDR_SYNTH_5_GHZ_DISABLED 0x0080
+#define PDR_SYNTH_RX_DIV_MASK 0x0100
+#define PDR_SYNTH_RX_DIV_SUPPORTED 0x0100
+#define PDR_SYNTH_TX_DIV_MASK 0x0200
+#define PDR_SYNTH_TX_DIV_SUPPORTED 0x0200
+#define PDR_SYNTH_ASM_MASK 0x0400
+#define PDR_SYNTH_ASM_XSWON 0x0400
+
+#endif /* EEPROM_H */
--- /dev/null
+/*
+ * Firmware I/O code for mac80211 Prism54 drivers
+ *
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * Based on:
+ * - the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ * - stlc45xx driver
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/export.h>
+
+#include <net/mac80211.h>
+
+#include "p54.h"
+#include "eeprom.h"
+#include "lmac.h"
+
+int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
+{
+ struct p54_common *priv = dev->priv;
+ struct exp_if *exp_if;
+ struct bootrec *bootrec;
+ u32 *data = (u32 *)fw->data;
+ u32 *end_data = (u32 *)fw->data + (fw->size >> 2);
+ u8 *fw_version = NULL;
+ size_t len;
+ int i;
+ int maxlen;
+
+ if (priv->rx_start)
+ return 0;
+
+ while (data < end_data && *data)
+ data++;
+
+ while (data < end_data && !*data)
+ data++;
+
+ bootrec = (struct bootrec *) data;
+
+ while (bootrec->data <= end_data && (bootrec->data +
+ (len = le32_to_cpu(bootrec->len))) <= end_data) {
+ u32 code = le32_to_cpu(bootrec->code);
+ switch (code) {
+ case BR_CODE_COMPONENT_ID:
+ priv->fw_interface = be32_to_cpup((__be32 *)
+ bootrec->data);
+ switch (priv->fw_interface) {
+ case FW_LM86:
+ case FW_LM20:
+ case FW_LM87: {
+ char *iftype = (char *)bootrec->data;
+ wiphy_info(priv->hw->wiphy,
+ "p54 detected a LM%c%c firmware\n",
+ iftype[2], iftype[3]);
+ break;
+ }
+ case FW_FMAC:
+ default:
+ wiphy_err(priv->hw->wiphy,
+ "unsupported firmware\n");
+ return -ENODEV;
+ }
+ break;
+ case BR_CODE_COMPONENT_VERSION:
+ /* 24 bytes should be enough for all firmwares */
+ if (strnlen((unsigned char *) bootrec->data, 24) < 24)
+ fw_version = (unsigned char *) bootrec->data;
+ break;
+ case BR_CODE_DESCR: {
+ struct bootrec_desc *desc =
+ (struct bootrec_desc *)bootrec->data;
+ priv->rx_start = le32_to_cpu(desc->rx_start);
+ /* FIXME add sanity checking */
+ priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500;
+ priv->headroom = desc->headroom;
+ priv->tailroom = desc->tailroom;
+ priv->privacy_caps = desc->privacy_caps;
+ priv->rx_keycache_size = desc->rx_keycache_size;
+ if (le32_to_cpu(bootrec->len) == 11)
+ priv->rx_mtu = le16_to_cpu(desc->rx_mtu);
+ else
+ priv->rx_mtu = (size_t)
+ 0x620 - priv->tx_hdr_len;
+ maxlen = priv->tx_hdr_len + /* USB devices */
+ sizeof(struct p54_rx_data) +
+ 4 + /* rx alignment */
+ IEEE80211_MAX_FRAG_THRESHOLD;
+ if (priv->rx_mtu > maxlen && PAGE_SIZE == 4096) {
+ printk(KERN_INFO "p54: rx_mtu reduced from %d "
+ "to %d\n", priv->rx_mtu, maxlen);
+ priv->rx_mtu = maxlen;
+ }
+ break;
+ }
+ case BR_CODE_EXPOSED_IF:
+ exp_if = (struct exp_if *) bootrec->data;
+ for (i = 0; i < (len * sizeof(*exp_if) / 4); i++)
+ if (exp_if[i].if_id == cpu_to_le16(IF_ID_LMAC))
+ priv->fw_var = le16_to_cpu(exp_if[i].variant);
+ break;
+ case BR_CODE_DEPENDENT_IF:
+ break;
+ case BR_CODE_END_OF_BRA:
+ case LEGACY_BR_CODE_END_OF_BRA:
+ end_data = NULL;
+ break;
+ default:
+ break;
+ }
+ bootrec = (struct bootrec *)&bootrec->data[len];
+ }
+
+ if (fw_version) {
+ wiphy_info(priv->hw->wiphy,
+ "FW rev %s - Softmac protocol %x.%x\n",
+ fw_version, priv->fw_var >> 8, priv->fw_var & 0xff);
+ snprintf(dev->wiphy->fw_version, sizeof(dev->wiphy->fw_version),
+ "%s - %x.%x", fw_version,
+ priv->fw_var >> 8, priv->fw_var & 0xff);
+ }
+
+ if (priv->fw_var < 0x500)
+ wiphy_info(priv->hw->wiphy,
+ "you are using an obsolete firmware. "
+ "visit http://wireless.kernel.org/en/users/Drivers/p54 "
+ "and grab one for \"kernel >= 2.6.28\"!\n");
+
+ if (priv->fw_var >= 0x300) {
+ /* Firmware supports QoS, use it! */
+
+ if (priv->fw_var >= 0x500) {
+ priv->tx_stats[P54_QUEUE_AC_VO].limit = 16;
+ priv->tx_stats[P54_QUEUE_AC_VI].limit = 16;
+ priv->tx_stats[P54_QUEUE_AC_BE].limit = 16;
+ priv->tx_stats[P54_QUEUE_AC_BK].limit = 16;
+ } else {
+ priv->tx_stats[P54_QUEUE_AC_VO].limit = 3;
+ priv->tx_stats[P54_QUEUE_AC_VI].limit = 4;
+ priv->tx_stats[P54_QUEUE_AC_BE].limit = 3;
+ priv->tx_stats[P54_QUEUE_AC_BK].limit = 2;
+ }
+ priv->hw->queues = P54_QUEUE_AC_NUM;
+ }
+
+ wiphy_info(priv->hw->wiphy,
+ "cryptographic accelerator WEP:%s, TKIP:%s, CCMP:%s\n",
+ (priv->privacy_caps & BR_DESC_PRIV_CAP_WEP) ? "YES" : "no",
+ (priv->privacy_caps &
+ (BR_DESC_PRIV_CAP_TKIP | BR_DESC_PRIV_CAP_MICHAEL))
+ ? "YES" : "no",
+ (priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)
+ ? "YES" : "no");
+
+ if (priv->rx_keycache_size) {
+ /*
+ * NOTE:
+ *
+ * The firmware provides at most 255 (0 - 254) slots
+ * for keys which are then used to offload decryption.
+ * As a result the 255 entry (aka 0xff) can be used
+ * safely by the driver to mark keys that didn't fit
+ * into the full cache. This trick saves us from
+ * keeping a extra list for uploaded keys.
+ */
+
+ priv->used_rxkeys = kzalloc(BITS_TO_LONGS(
+ priv->rx_keycache_size), GFP_KERNEL);
+
+ if (!priv->used_rxkeys)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(p54_parse_firmware);
+
+static struct sk_buff *p54_alloc_skb(struct p54_common *priv, u16 hdr_flags,
+ u16 payload_len, u16 type, gfp_t memflags)
+{
+ struct p54_hdr *hdr;
+ struct sk_buff *skb;
+ size_t frame_len = sizeof(*hdr) + payload_len;
+
+ if (frame_len > P54_MAX_CTRL_FRAME_LEN)
+ return NULL;
+
+ if (unlikely(skb_queue_len(&priv->tx_pending) > 64))
+ return NULL;
+
+ skb = __dev_alloc_skb(priv->tx_hdr_len + frame_len, memflags);
+ if (!skb)
+ return NULL;
+ skb_reserve(skb, priv->tx_hdr_len);
+
+ hdr = (struct p54_hdr *) skb_put(skb, sizeof(*hdr));
+ hdr->flags = cpu_to_le16(hdr_flags);
+ hdr->len = cpu_to_le16(payload_len);
+ hdr->type = cpu_to_le16(type);
+ hdr->tries = hdr->rts_tries = 0;
+ return skb;
+}
+
+int p54_download_eeprom(struct p54_common *priv, void *buf,
+ u16 offset, u16 len)
+{
+ struct p54_eeprom_lm86 *eeprom_hdr;
+ struct sk_buff *skb;
+ size_t eeprom_hdr_size;
+ int ret = 0;
+ long timeout;
+
+ if (priv->fw_var >= 0x509)
+ eeprom_hdr_size = sizeof(*eeprom_hdr);
+ else
+ eeprom_hdr_size = 0x4;
+
+ skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL, eeprom_hdr_size +
+ len, P54_CONTROL_TYPE_EEPROM_READBACK,
+ GFP_KERNEL);
+ if (unlikely(!skb))
+ return -ENOMEM;
+
+ mutex_lock(&priv->eeprom_mutex);
+ priv->eeprom = buf;
+ eeprom_hdr = (struct p54_eeprom_lm86 *) skb_put(skb,
+ eeprom_hdr_size + len);
+
+ if (priv->fw_var < 0x509) {
+ eeprom_hdr->v1.offset = cpu_to_le16(offset);
+ eeprom_hdr->v1.len = cpu_to_le16(len);
+ } else {
+ eeprom_hdr->v2.offset = cpu_to_le32(offset);
+ eeprom_hdr->v2.len = cpu_to_le16(len);
+ eeprom_hdr->v2.magic2 = 0xf;
+ memcpy(eeprom_hdr->v2.magic, (const char *)"LOCK", 4);
+ }
+
+ p54_tx(priv, skb);
+
+ timeout = wait_for_completion_interruptible_timeout(
+ &priv->eeprom_comp, HZ);
+ if (timeout <= 0) {
+ wiphy_err(priv->hw->wiphy,
+ "device does not respond or signal received!\n");
+ ret = -EBUSY;
+ }
+ priv->eeprom = NULL;
+ mutex_unlock(&priv->eeprom_mutex);
+ return ret;
+}
+
+int p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set)
+{
+ struct sk_buff *skb;
+ struct p54_tim *tim;
+
+ skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*tim),
+ P54_CONTROL_TYPE_TIM, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+
+ tim = (struct p54_tim *) skb_put(skb, sizeof(*tim));
+ tim->count = 1;
+ tim->entry[0] = cpu_to_le16(set ? (aid | 0x8000) : aid);
+ p54_tx(priv, skb);
+ return 0;
+}
+
+int p54_sta_unlock(struct p54_common *priv, u8 *addr)
+{
+ struct sk_buff *skb;
+ struct p54_sta_unlock *sta;
+
+ skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*sta),
+ P54_CONTROL_TYPE_PSM_STA_UNLOCK, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+
+ sta = (struct p54_sta_unlock *)skb_put(skb, sizeof(*sta));
+ memcpy(sta->addr, addr, ETH_ALEN);
+ p54_tx(priv, skb);
+ return 0;
+}
+
+int p54_tx_cancel(struct p54_common *priv, __le32 req_id)
+{
+ struct sk_buff *skb;
+ struct p54_txcancel *cancel;
+ u32 _req_id = le32_to_cpu(req_id);
+
+ if (unlikely(_req_id < priv->rx_start || _req_id > priv->rx_end))
+ return -EINVAL;
+
+ skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*cancel),
+ P54_CONTROL_TYPE_TXCANCEL, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+
+ cancel = (struct p54_txcancel *)skb_put(skb, sizeof(*cancel));
+ cancel->req_id = req_id;
+ p54_tx(priv, skb);
+ return 0;
+}
+
+int p54_setup_mac(struct p54_common *priv)
+{
+ struct sk_buff *skb;
+ struct p54_setup_mac *setup;
+ u16 mode;
+
+ skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*setup),
+ P54_CONTROL_TYPE_SETUP, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ setup = (struct p54_setup_mac *) skb_put(skb, sizeof(*setup));
+ if (!(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) {
+ switch (priv->mode) {
+ case NL80211_IFTYPE_STATION:
+ mode = P54_FILTER_TYPE_STATION;
+ break;
+ case NL80211_IFTYPE_AP:
+ mode = P54_FILTER_TYPE_AP;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ mode = P54_FILTER_TYPE_IBSS;
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ mode = P54_FILTER_TYPE_PROMISCUOUS;
+ break;
+ default:
+ mode = P54_FILTER_TYPE_HIBERNATE;
+ break;
+ }
+
+ /*
+ * "TRANSPARENT and PROMISCUOUS are mutually exclusive"
+ * STSW45X0C LMAC API - page 12
+ */
+ if (priv->filter_flags & FIF_OTHER_BSS &&
+ (mode != P54_FILTER_TYPE_PROMISCUOUS))
+ mode |= P54_FILTER_TYPE_TRANSPARENT;
+ } else {
+ mode = P54_FILTER_TYPE_HIBERNATE;
+ }
+
+ setup->mac_mode = cpu_to_le16(mode);
+ memcpy(setup->mac_addr, priv->mac_addr, ETH_ALEN);
+ memcpy(setup->bssid, priv->bssid, ETH_ALEN);
+ setup->rx_antenna = 2 & priv->rx_diversity_mask; /* automatic */
+ setup->rx_align = 0;
+ if (priv->fw_var < 0x500) {
+ setup->v1.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);
+ memset(setup->v1.rts_rates, 0, 8);
+ setup->v1.rx_addr = cpu_to_le32(priv->rx_end);
+ setup->v1.max_rx = cpu_to_le16(priv->rx_mtu);
+ setup->v1.rxhw = cpu_to_le16(priv->rxhw);
+ setup->v1.wakeup_timer = cpu_to_le16(priv->wakeup_timer);
+ setup->v1.unalloc0 = cpu_to_le16(0);
+ } else {
+ setup->v2.rx_addr = cpu_to_le32(priv->rx_end);
+ setup->v2.max_rx = cpu_to_le16(priv->rx_mtu);
+ setup->v2.rxhw = cpu_to_le16(priv->rxhw);
+ setup->v2.timer = cpu_to_le16(priv->wakeup_timer);
+ setup->v2.truncate = cpu_to_le16(48896);
+ setup->v2.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);
+ setup->v2.sbss_offset = 0;
+ setup->v2.mcast_window = 0;
+ setup->v2.rx_rssi_threshold = 0;
+ setup->v2.rx_ed_threshold = 0;
+ setup->v2.ref_clock = cpu_to_le32(644245094);
+ setup->v2.lpf_bandwidth = cpu_to_le16(65535);
+ setup->v2.osc_start_delay = cpu_to_le16(65535);
+ }
+ p54_tx(priv, skb);
+ priv->phy_idle = mode == P54_FILTER_TYPE_HIBERNATE;
+ return 0;
+}
+
+int p54_scan(struct p54_common *priv, u16 mode, u16 dwell)
+{
+ struct sk_buff *skb;
+ struct p54_hdr *hdr;
+ struct p54_scan_head *head;
+ struct p54_iq_autocal_entry *iq_autocal;
+ union p54_scan_body_union *body;
+ struct p54_scan_tail_rate *rate;
+ struct pda_rssi_cal_entry *rssi;
+ struct p54_rssi_db_entry *rssi_data;
+ unsigned int i;
+ void *entry;
+ __le16 freq = cpu_to_le16(priv->hw->conf.chandef.chan->center_freq);
+
+ skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*head) +
+ 2 + sizeof(*iq_autocal) + sizeof(*body) +
+ sizeof(*rate) + 2 * sizeof(*rssi),
+ P54_CONTROL_TYPE_SCAN, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ head = (struct p54_scan_head *) skb_put(skb, sizeof(*head));
+ memset(head->scan_params, 0, sizeof(head->scan_params));
+ head->mode = cpu_to_le16(mode);
+ head->dwell = cpu_to_le16(dwell);
+ head->freq = freq;
+
+ if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {
+ __le16 *pa_power_points = (__le16 *) skb_put(skb, 2);
+ *pa_power_points = cpu_to_le16(0x0c);
+ }
+
+ iq_autocal = (void *) skb_put(skb, sizeof(*iq_autocal));
+ for (i = 0; i < priv->iq_autocal_len; i++) {
+ if (priv->iq_autocal[i].freq != freq)
+ continue;
+
+ memcpy(iq_autocal, &priv->iq_autocal[i].params,
+ sizeof(struct p54_iq_autocal_entry));
+ break;
+ }
+ if (i == priv->iq_autocal_len)
+ goto err;
+
+ if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW)
+ body = (void *) skb_put(skb, sizeof(body->longbow));
+ else
+ body = (void *) skb_put(skb, sizeof(body->normal));
+
+ for (i = 0; i < priv->output_limit->entries; i++) {
+ __le16 *entry_freq = (void *) (priv->output_limit->data +
+ priv->output_limit->entry_size * i);
+
+ if (*entry_freq != freq)
+ continue;
+
+ if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {
+ memcpy(&body->longbow.power_limits,
+ (void *) entry_freq + sizeof(__le16),
+ priv->output_limit->entry_size);
+ } else {
+ struct pda_channel_output_limit *limits =
+ (void *) entry_freq;
+
+ body->normal.val_barker = 0x38;
+ body->normal.val_bpsk = body->normal.dup_bpsk =
+ limits->val_bpsk;
+ body->normal.val_qpsk = body->normal.dup_qpsk =
+ limits->val_qpsk;
+ body->normal.val_16qam = body->normal.dup_16qam =
+ limits->val_16qam;
+ body->normal.val_64qam = body->normal.dup_64qam =
+ limits->val_64qam;
+ }
+ break;
+ }
+ if (i == priv->output_limit->entries)
+ goto err;
+
+ entry = (void *)(priv->curve_data->data + priv->curve_data->offset);
+ for (i = 0; i < priv->curve_data->entries; i++) {
+ if (*((__le16 *)entry) != freq) {
+ entry += priv->curve_data->entry_size;
+ continue;
+ }
+
+ if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {
+ memcpy(&body->longbow.curve_data,
+ entry + sizeof(__le16),
+ priv->curve_data->entry_size);
+ } else {
+ struct p54_scan_body *chan = &body->normal;
+ struct pda_pa_curve_data *curve_data =
+ (void *) priv->curve_data->data;
+
+ entry += sizeof(__le16);
+ chan->pa_points_per_curve = 8;
+ memset(chan->curve_data, 0, sizeof(*chan->curve_data));
+ memcpy(chan->curve_data, entry,
+ sizeof(struct p54_pa_curve_data_sample) *
+ min((u8)8, curve_data->points_per_channel));
+ }
+ break;
+ }
+ if (i == priv->curve_data->entries)
+ goto err;
+
+ if ((priv->fw_var >= 0x500) && (priv->fw_var < 0x509)) {
+ rate = (void *) skb_put(skb, sizeof(*rate));
+ rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);
+ for (i = 0; i < sizeof(rate->rts_rates); i++)
+ rate->rts_rates[i] = i;
+ }
+
+ rssi = (struct pda_rssi_cal_entry *) skb_put(skb, sizeof(*rssi));
+ rssi_data = p54_rssi_find(priv, le16_to_cpu(freq));
+ rssi->mul = cpu_to_le16(rssi_data->mul);
+ rssi->add = cpu_to_le16(rssi_data->add);
+ if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {
+ /* Longbow frontend needs ever more */
+ rssi = (void *) skb_put(skb, sizeof(*rssi));
+ rssi->mul = cpu_to_le16(rssi_data->longbow_unkn);
+ rssi->add = cpu_to_le16(rssi_data->longbow_unk2);
+ }
+
+ if (priv->fw_var >= 0x509) {
+ rate = (void *) skb_put(skb, sizeof(*rate));
+ rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);
+ for (i = 0; i < sizeof(rate->rts_rates); i++)
+ rate->rts_rates[i] = i;
+ }
+
+ hdr = (struct p54_hdr *) skb->data;
+ hdr->len = cpu_to_le16(skb->len - sizeof(*hdr));
+
+ p54_tx(priv, skb);
+ priv->cur_rssi = rssi_data;
+ return 0;
+
+err:
+ wiphy_err(priv->hw->wiphy, "frequency change to channel %d failed.\n",
+ ieee80211_frequency_to_channel(
+ priv->hw->conf.chandef.chan->center_freq));
+
+ dev_kfree_skb_any(skb);
+ return -EINVAL;
+}
+
+int p54_set_leds(struct p54_common *priv)
+{
+ struct sk_buff *skb;
+ struct p54_led *led;
+
+ skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*led),
+ P54_CONTROL_TYPE_LED, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+
+ led = (struct p54_led *) skb_put(skb, sizeof(*led));
+ led->flags = cpu_to_le16(0x0003);
+ led->mask[0] = led->mask[1] = cpu_to_le16(priv->softled_state);
+ led->delay[0] = cpu_to_le16(1);
+ led->delay[1] = cpu_to_le16(0);
+ p54_tx(priv, skb);
+ return 0;
+}
+
+int p54_set_edcf(struct p54_common *priv)
+{
+ struct sk_buff *skb;
+ struct p54_edcf *edcf;
+ u8 rtd;
+
+ skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*edcf),
+ P54_CONTROL_TYPE_DCFINIT, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+
+ edcf = (struct p54_edcf *)skb_put(skb, sizeof(*edcf));
+ if (priv->use_short_slot) {
+ edcf->slottime = 9;
+ edcf->sifs = 0x10;
+ edcf->eofpad = 0x00;
+ } else {
+ edcf->slottime = 20;
+ edcf->sifs = 0x0a;
+ edcf->eofpad = 0x06;
+ }
+ /*
+ * calculate the extra round trip delay according to the
+ * formula from 802.11-2007 17.3.8.6.
+ */
+ rtd = 3 * priv->coverage_class;
+ edcf->slottime += rtd;
+ edcf->round_trip_delay = cpu_to_le16(rtd);
+ /* (see prism54/isl_oid.h for further details) */
+ edcf->frameburst = cpu_to_le16(0);
+ edcf->flags = 0;
+ memset(edcf->mapping, 0, sizeof(edcf->mapping));
+ memcpy(edcf->queue, priv->qos_params, sizeof(edcf->queue));
+ p54_tx(priv, skb);
+ return 0;
+}
+
+int p54_set_ps(struct p54_common *priv)
+{
+ struct sk_buff *skb;
+ struct p54_psm *psm;
+ unsigned int i;
+ u16 mode;
+
+ if (priv->hw->conf.flags & IEEE80211_CONF_PS &&
+ !priv->powersave_override)
+ mode = P54_PSM | P54_PSM_BEACON_TIMEOUT | P54_PSM_DTIM |
+ P54_PSM_CHECKSUM | P54_PSM_MCBC;
+ else
+ mode = P54_PSM_CAM;
+
+ skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*psm),
+ P54_CONTROL_TYPE_PSM, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ psm = (struct p54_psm *)skb_put(skb, sizeof(*psm));
+ psm->mode = cpu_to_le16(mode);
+ psm->aid = cpu_to_le16(priv->aid);
+ for (i = 0; i < ARRAY_SIZE(psm->intervals); i++) {
+ psm->intervals[i].interval =
+ cpu_to_le16(priv->hw->conf.listen_interval);
+ psm->intervals[i].periods = cpu_to_le16(1);
+ }
+
+ psm->beacon_rssi_skip_max = 200;
+ psm->rssi_delta_threshold = 0;
+ psm->nr = 1;
+ psm->exclude[0] = WLAN_EID_TIM;
+
+ p54_tx(priv, skb);
+ priv->phy_ps = mode != P54_PSM_CAM;
+ return 0;
+}
+
+int p54_init_xbow_synth(struct p54_common *priv)
+{
+ struct sk_buff *skb;
+ struct p54_xbow_synth *xbow;
+
+ skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*xbow),
+ P54_CONTROL_TYPE_XBOW_SYNTH_CFG, GFP_KERNEL);
+ if (unlikely(!skb))
+ return -ENOMEM;
+
+ xbow = (struct p54_xbow_synth *)skb_put(skb, sizeof(*xbow));
+ xbow->magic1 = cpu_to_le16(0x1);
+ xbow->magic2 = cpu_to_le16(0x2);
+ xbow->freq = cpu_to_le16(5390);
+ memset(xbow->padding, 0, sizeof(xbow->padding));
+ p54_tx(priv, skb);
+ return 0;
+}
+
+int p54_upload_key(struct p54_common *priv, u8 algo, int slot, u8 idx, u8 len,
+ u8 *addr, u8* key)
+{
+ struct sk_buff *skb;
+ struct p54_keycache *rxkey;
+
+ skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey),
+ P54_CONTROL_TYPE_RX_KEYCACHE, GFP_KERNEL);
+ if (unlikely(!skb))
+ return -ENOMEM;
+
+ rxkey = (struct p54_keycache *)skb_put(skb, sizeof(*rxkey));
+ rxkey->entry = slot;
+ rxkey->key_id = idx;
+ rxkey->key_type = algo;
+ if (addr)
+ memcpy(rxkey->mac, addr, ETH_ALEN);
+ else
+ eth_broadcast_addr(rxkey->mac);
+
+ switch (algo) {
+ case P54_CRYPTO_WEP:
+ case P54_CRYPTO_AESCCMP:
+ rxkey->key_len = min_t(u8, 16, len);
+ memcpy(rxkey->key, key, rxkey->key_len);
+ break;
+
+ case P54_CRYPTO_TKIPMICHAEL:
+ rxkey->key_len = 24;
+ memcpy(rxkey->key, key, 16);
+ memcpy(&(rxkey->key[16]), &(key
+ [NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]), 8);
+ break;
+
+ case P54_CRYPTO_NONE:
+ rxkey->key_len = 0;
+ memset(rxkey->key, 0, sizeof(rxkey->key));
+ break;
+
+ default:
+ wiphy_err(priv->hw->wiphy,
+ "invalid cryptographic algorithm: %d\n", algo);
+ dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ p54_tx(priv, skb);
+ return 0;
+}
+
+int p54_fetch_statistics(struct p54_common *priv)
+{
+ struct ieee80211_tx_info *txinfo;
+ struct p54_tx_info *p54info;
+ struct sk_buff *skb;
+
+ skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL,
+ sizeof(struct p54_statistics),
+ P54_CONTROL_TYPE_STAT_READBACK, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ /*
+ * The statistic feedback causes some extra headaches here, if it
+ * is not to crash/corrupt the firmware data structures.
+ *
+ * Unlike all other Control Get OIDs we can not use helpers like
+ * skb_put to reserve the space for the data we're requesting.
+ * Instead the extra frame length -which will hold the results later-
+ * will only be told to the p54_assign_address, so that following
+ * frames won't be placed into the allegedly empty area.
+ */
+ txinfo = IEEE80211_SKB_CB(skb);
+ p54info = (void *) txinfo->rate_driver_data;
+ p54info->extra_len = sizeof(struct p54_statistics);
+
+ p54_tx(priv, skb);
+ return 0;
+}
+
+int p54_set_groupfilter(struct p54_common *priv)
+{
+ struct p54_group_address_table *grp;
+ struct sk_buff *skb;
+ bool on = false;
+
+ skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*grp),
+ P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ grp = (struct p54_group_address_table *)skb_put(skb, sizeof(*grp));
+
+ on = !(priv->filter_flags & FIF_ALLMULTI) &&
+ (priv->mc_maclist_num > 0 &&
+ priv->mc_maclist_num <= MC_FILTER_ADDRESS_NUM);
+
+ if (on) {
+ grp->filter_enable = cpu_to_le16(1);
+ grp->num_address = cpu_to_le16(priv->mc_maclist_num);
+ memcpy(grp->mac_list, priv->mc_maclist, sizeof(grp->mac_list));
+ } else {
+ grp->filter_enable = cpu_to_le16(0);
+ grp->num_address = cpu_to_le16(0);
+ memset(grp->mac_list, 0, sizeof(grp->mac_list));
+ }
+
+ p54_tx(priv, skb);
+ return 0;
+}
--- /dev/null
+/*
+ * Common code for mac80211 Prism54 drivers
+ *
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * Based on:
+ * - the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ * - stlc45xx driver
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+
+#include <net/mac80211.h>
+#ifdef CONFIG_P54_LEDS
+#include <linux/leds.h>
+#endif /* CONFIG_P54_LEDS */
+
+#include "p54.h"
+#include "lmac.h"
+
+static void p54_update_leds(struct work_struct *work)
+{
+ struct p54_common *priv = container_of(work, struct p54_common,
+ led_work.work);
+ int err, i, tmp, blink_delay = 400;
+ bool rerun = false;
+
+ /* Don't toggle the LED, when the device is down. */
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+ return ;
+
+ for (i = 0; i < ARRAY_SIZE(priv->leds); i++)
+ if (priv->leds[i].toggled) {
+ priv->softled_state |= BIT(i);
+
+ tmp = 70 + 200 / (priv->leds[i].toggled);
+ if (tmp < blink_delay)
+ blink_delay = tmp;
+
+ if (priv->leds[i].led_dev.brightness == LED_OFF)
+ rerun = true;
+
+ priv->leds[i].toggled =
+ !!priv->leds[i].led_dev.brightness;
+ } else
+ priv->softled_state &= ~BIT(i);
+
+ err = p54_set_leds(priv);
+ if (err && net_ratelimit())
+ wiphy_err(priv->hw->wiphy,
+ "failed to update LEDs (%d).\n", err);
+
+ if (rerun)
+ ieee80211_queue_delayed_work(priv->hw, &priv->led_work,
+ msecs_to_jiffies(blink_delay));
+}
+
+static void p54_led_brightness_set(struct led_classdev *led_dev,
+ enum led_brightness brightness)
+{
+ struct p54_led_dev *led = container_of(led_dev, struct p54_led_dev,
+ led_dev);
+ struct ieee80211_hw *dev = led->hw_dev;
+ struct p54_common *priv = dev->priv;
+
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+ return ;
+
+ if ((brightness) && (led->registered)) {
+ led->toggled++;
+ ieee80211_queue_delayed_work(priv->hw, &priv->led_work, HZ/10);
+ }
+}
+
+static int p54_register_led(struct p54_common *priv,
+ unsigned int led_index,
+ char *name, const char *trigger)
+{
+ struct p54_led_dev *led = &priv->leds[led_index];
+ int err;
+
+ if (led->registered)
+ return -EEXIST;
+
+ snprintf(led->name, sizeof(led->name), "p54-%s::%s",
+ wiphy_name(priv->hw->wiphy), name);
+ led->hw_dev = priv->hw;
+ led->index = led_index;
+ led->led_dev.name = led->name;
+ led->led_dev.default_trigger = trigger;
+ led->led_dev.brightness_set = p54_led_brightness_set;
+
+ err = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_dev);
+ if (err)
+ wiphy_err(priv->hw->wiphy,
+ "Failed to register %s LED.\n", name);
+ else
+ led->registered = 1;
+
+ return err;
+}
+
+int p54_init_leds(struct p54_common *priv)
+{
+ int err;
+
+ /*
+ * TODO:
+ * Figure out if the EEPROM contains some hints about the number
+ * of available/programmable LEDs of the device.
+ */
+
+ INIT_DELAYED_WORK(&priv->led_work, p54_update_leds);
+
+ err = p54_register_led(priv, 0, "assoc",
+ ieee80211_get_assoc_led_name(priv->hw));
+ if (err)
+ return err;
+
+ err = p54_register_led(priv, 1, "tx",
+ ieee80211_get_tx_led_name(priv->hw));
+ if (err)
+ return err;
+
+ err = p54_register_led(priv, 2, "rx",
+ ieee80211_get_rx_led_name(priv->hw));
+ if (err)
+ return err;
+
+ err = p54_register_led(priv, 3, "radio",
+ ieee80211_get_radio_led_name(priv->hw));
+ if (err)
+ return err;
+
+ err = p54_set_leds(priv);
+ return err;
+}
+
+void p54_unregister_leds(struct p54_common *priv)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->leds); i++) {
+ if (priv->leds[i].registered) {
+ priv->leds[i].registered = false;
+ priv->leds[i].toggled = 0;
+ led_classdev_unregister(&priv->leds[i].led_dev);
+ }
+ }
+
+ cancel_delayed_work_sync(&priv->led_work);
+}
--- /dev/null
+/*
+ * LMAC Interface specific definitions for mac80211 Prism54 drivers
+ *
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ * Copyright (c) 2007 - 2009, Christian Lamparter <chunkeey@web.de>
+ *
+ * Based on:
+ * - the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ *
+ * - LMAC API interface header file for STLC4560 (lmac_longbow.h)
+ * Copyright (C) 2007 Conexant Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef LMAC_H
+#define LMAC_H
+
+enum p54_control_frame_types {
+ P54_CONTROL_TYPE_SETUP = 0,
+ P54_CONTROL_TYPE_SCAN,
+ P54_CONTROL_TYPE_TRAP,
+ P54_CONTROL_TYPE_DCFINIT,
+ P54_CONTROL_TYPE_RX_KEYCACHE,
+ P54_CONTROL_TYPE_TIM,
+ P54_CONTROL_TYPE_PSM,
+ P54_CONTROL_TYPE_TXCANCEL,
+ P54_CONTROL_TYPE_TXDONE,
+ P54_CONTROL_TYPE_BURST,
+ P54_CONTROL_TYPE_STAT_READBACK,
+ P54_CONTROL_TYPE_BBP,
+ P54_CONTROL_TYPE_EEPROM_READBACK,
+ P54_CONTROL_TYPE_LED,
+ P54_CONTROL_TYPE_GPIO,
+ P54_CONTROL_TYPE_TIMER,
+ P54_CONTROL_TYPE_MODULATION,
+ P54_CONTROL_TYPE_SYNTH_CONFIG,
+ P54_CONTROL_TYPE_DETECTOR_VALUE,
+ P54_CONTROL_TYPE_XBOW_SYNTH_CFG,
+ P54_CONTROL_TYPE_CCE_QUIET,
+ P54_CONTROL_TYPE_PSM_STA_UNLOCK,
+ P54_CONTROL_TYPE_PCS,
+ P54_CONTROL_TYPE_BT_BALANCER = 28,
+ P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE = 30,
+ P54_CONTROL_TYPE_ARPTABLE = 31,
+ P54_CONTROL_TYPE_BT_OPTIONS = 35,
+};
+
+#define P54_HDR_FLAG_CONTROL BIT(15)
+#define P54_HDR_FLAG_CONTROL_OPSET (BIT(15) + BIT(0))
+#define P54_HDR_FLAG_DATA_ALIGN BIT(14)
+
+#define P54_HDR_FLAG_DATA_OUT_PROMISC BIT(0)
+#define P54_HDR_FLAG_DATA_OUT_TIMESTAMP BIT(1)
+#define P54_HDR_FLAG_DATA_OUT_SEQNR BIT(2)
+#define P54_HDR_FLAG_DATA_OUT_BIT3 BIT(3)
+#define P54_HDR_FLAG_DATA_OUT_BURST BIT(4)
+#define P54_HDR_FLAG_DATA_OUT_NOCANCEL BIT(5)
+#define P54_HDR_FLAG_DATA_OUT_CLEARTIM BIT(6)
+#define P54_HDR_FLAG_DATA_OUT_HITCHHIKE BIT(7)
+#define P54_HDR_FLAG_DATA_OUT_COMPRESS BIT(8)
+#define P54_HDR_FLAG_DATA_OUT_CONCAT BIT(9)
+#define P54_HDR_FLAG_DATA_OUT_PCS_ACCEPT BIT(10)
+#define P54_HDR_FLAG_DATA_OUT_WAITEOSP BIT(11)
+
+#define P54_HDR_FLAG_DATA_IN_FCS_GOOD BIT(0)
+#define P54_HDR_FLAG_DATA_IN_MATCH_MAC BIT(1)
+#define P54_HDR_FLAG_DATA_IN_MCBC BIT(2)
+#define P54_HDR_FLAG_DATA_IN_BEACON BIT(3)
+#define P54_HDR_FLAG_DATA_IN_MATCH_BSS BIT(4)
+#define P54_HDR_FLAG_DATA_IN_BCAST_BSS BIT(5)
+#define P54_HDR_FLAG_DATA_IN_DATA BIT(6)
+#define P54_HDR_FLAG_DATA_IN_TRUNCATED BIT(7)
+#define P54_HDR_FLAG_DATA_IN_BIT8 BIT(8)
+#define P54_HDR_FLAG_DATA_IN_TRANSPARENT BIT(9)
+
+struct p54_hdr {
+ __le16 flags;
+ __le16 len;
+ __le32 req_id;
+ __le16 type; /* enum p54_control_frame_types */
+ u8 rts_tries;
+ u8 tries;
+ u8 data[0];
+} __packed;
+
+#define GET_REQ_ID(skb) \
+ (((struct p54_hdr *) ((struct sk_buff *) skb)->data)->req_id) \
+
+#define FREE_AFTER_TX(skb) \
+ ((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \
+ flags) == cpu_to_le16(P54_HDR_FLAG_CONTROL_OPSET))
+
+#define IS_DATA_FRAME(skb) \
+ (!((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \
+ flags) & cpu_to_le16(P54_HDR_FLAG_CONTROL)))
+
+#define GET_HW_QUEUE(skb) \
+ (((struct p54_tx_data *)((struct p54_hdr *) \
+ skb->data)->data)->hw_queue)
+
+/*
+ * shared interface ID definitions
+ * The interface ID is a unique identification of a specific interface.
+ * The following values are reserved: 0x0000, 0x0002, 0x0012, 0x0014, 0x0015
+ */
+#define IF_ID_ISL36356A 0x0001 /* ISL36356A <-> Firmware */
+#define IF_ID_MVC 0x0003 /* MAC Virtual Coprocessor */
+#define IF_ID_DEBUG 0x0008 /* PolDebug Interface */
+#define IF_ID_PRODUCT 0x0009
+#define IF_ID_OEM 0x000a
+#define IF_ID_PCI3877 0x000b /* 3877 <-> Host PCI */
+#define IF_ID_ISL37704C 0x000c /* ISL37704C <-> Fw */
+#define IF_ID_ISL39000 0x000f /* ISL39000 <-> Fw */
+#define IF_ID_ISL39300A 0x0010 /* ISL39300A <-> Fw */
+#define IF_ID_ISL37700_UAP 0x0016 /* ISL37700 uAP Fw <-> Fw */
+#define IF_ID_ISL39000_UAP 0x0017 /* ISL39000 uAP Fw <-> Fw */
+#define IF_ID_LMAC 0x001a /* Interface exposed by LMAC */
+
+struct exp_if {
+ __le16 role;
+ __le16 if_id;
+ __le16 variant;
+ __le16 btm_compat;
+ __le16 top_compat;
+} __packed;
+
+struct dep_if {
+ __le16 role;
+ __le16 if_id;
+ __le16 variant;
+} __packed;
+
+/* driver <-> lmac definitions */
+struct p54_eeprom_lm86 {
+ union {
+ struct {
+ __le16 offset;
+ __le16 len;
+ u8 data[0];
+ } __packed v1;
+ struct {
+ __le32 offset;
+ __le16 len;
+ u8 magic2;
+ u8 pad;
+ u8 magic[4];
+ u8 data[0];
+ } __packed v2;
+ } __packed;
+} __packed;
+
+enum p54_rx_decrypt_status {
+ P54_DECRYPT_NONE = 0,
+ P54_DECRYPT_OK,
+ P54_DECRYPT_NOKEY,
+ P54_DECRYPT_NOMICHAEL,
+ P54_DECRYPT_NOCKIPMIC,
+ P54_DECRYPT_FAIL_WEP,
+ P54_DECRYPT_FAIL_TKIP,
+ P54_DECRYPT_FAIL_MICHAEL,
+ P54_DECRYPT_FAIL_CKIPKP,
+ P54_DECRYPT_FAIL_CKIPMIC,
+ P54_DECRYPT_FAIL_AESCCMP
+};
+
+struct p54_rx_data {
+ __le16 flags;
+ __le16 len;
+ __le16 freq;
+ u8 antenna;
+ u8 rate;
+ u8 rssi;
+ u8 quality;
+ u8 decrypt_status;
+ u8 rssi_raw;
+ __le32 tsf32;
+ __le32 unalloc0;
+ u8 align[0];
+} __packed;
+
+enum p54_trap_type {
+ P54_TRAP_SCAN = 0,
+ P54_TRAP_TIMER,
+ P54_TRAP_BEACON_TX,
+ P54_TRAP_FAA_RADIO_ON,
+ P54_TRAP_FAA_RADIO_OFF,
+ P54_TRAP_RADAR,
+ P54_TRAP_NO_BEACON,
+ P54_TRAP_TBTT,
+ P54_TRAP_SCO_ENTER,
+ P54_TRAP_SCO_EXIT
+};
+
+struct p54_trap {
+ __le16 event;
+ __le16 frequency;
+} __packed;
+
+enum p54_frame_sent_status {
+ P54_TX_OK = 0,
+ P54_TX_FAILED,
+ P54_TX_PSM,
+ P54_TX_PSM_CANCELLED = 4
+};
+
+struct p54_frame_sent {
+ u8 status;
+ u8 tries;
+ u8 ack_rssi;
+ u8 quality;
+ __le16 seq;
+ u8 antenna;
+ u8 padding;
+} __packed;
+
+enum p54_tx_data_crypt {
+ P54_CRYPTO_NONE = 0,
+ P54_CRYPTO_WEP,
+ P54_CRYPTO_TKIP,
+ P54_CRYPTO_TKIPMICHAEL,
+ P54_CRYPTO_CCX_WEPMIC,
+ P54_CRYPTO_CCX_KPMIC,
+ P54_CRYPTO_CCX_KP,
+ P54_CRYPTO_AESCCMP
+};
+
+enum p54_tx_data_queue {
+ P54_QUEUE_BEACON = 0,
+ P54_QUEUE_FWSCAN = 1,
+ P54_QUEUE_MGMT = 2,
+ P54_QUEUE_CAB = 3,
+ P54_QUEUE_DATA = 4,
+
+ P54_QUEUE_AC_NUM = 4,
+ P54_QUEUE_AC_VO = 4,
+ P54_QUEUE_AC_VI = 5,
+ P54_QUEUE_AC_BE = 6,
+ P54_QUEUE_AC_BK = 7,
+
+ /* keep last */
+ P54_QUEUE_NUM = 8,
+};
+
+#define IS_QOS_QUEUE(n) (n >= P54_QUEUE_DATA)
+
+struct p54_tx_data {
+ u8 rateset[8];
+ u8 rts_rate_idx;
+ u8 crypt_offset;
+ u8 key_type;
+ u8 key_len;
+ u8 key[16];
+ u8 hw_queue;
+ u8 backlog;
+ __le16 durations[4];
+ u8 tx_antenna;
+ union {
+ struct {
+ u8 cts_rate;
+ __le16 output_power;
+ } __packed longbow;
+ struct {
+ u8 output_power;
+ u8 cts_rate;
+ u8 unalloc;
+ } __packed normal;
+ } __packed;
+ u8 unalloc2[2];
+ u8 align[0];
+} __packed;
+
+/* unit is ms */
+#define P54_TX_FRAME_LIFETIME 2000
+#define P54_TX_TIMEOUT 4000
+#define P54_STATISTICS_UPDATE 5000
+
+#define P54_FILTER_TYPE_NONE 0
+#define P54_FILTER_TYPE_STATION BIT(0)
+#define P54_FILTER_TYPE_IBSS BIT(1)
+#define P54_FILTER_TYPE_AP BIT(2)
+#define P54_FILTER_TYPE_TRANSPARENT BIT(3)
+#define P54_FILTER_TYPE_PROMISCUOUS BIT(4)
+#define P54_FILTER_TYPE_HIBERNATE BIT(5)
+#define P54_FILTER_TYPE_NOACK BIT(6)
+#define P54_FILTER_TYPE_RX_DISABLED BIT(7)
+
+struct p54_setup_mac {
+ __le16 mac_mode;
+ u8 mac_addr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ u8 rx_antenna;
+ u8 rx_align;
+ union {
+ struct {
+ __le32 basic_rate_mask;
+ u8 rts_rates[8];
+ __le32 rx_addr;
+ __le16 max_rx;
+ __le16 rxhw;
+ __le16 wakeup_timer;
+ __le16 unalloc0;
+ } __packed v1;
+ struct {
+ __le32 rx_addr;
+ __le16 max_rx;
+ __le16 rxhw;
+ __le16 timer;
+ __le16 truncate;
+ __le32 basic_rate_mask;
+ u8 sbss_offset;
+ u8 mcast_window;
+ u8 rx_rssi_threshold;
+ u8 rx_ed_threshold;
+ __le32 ref_clock;
+ __le16 lpf_bandwidth;
+ __le16 osc_start_delay;
+ } __packed v2;
+ } __packed;
+} __packed;
+
+#define P54_SETUP_V1_LEN 40
+#define P54_SETUP_V2_LEN (sizeof(struct p54_setup_mac))
+
+#define P54_SCAN_EXIT BIT(0)
+#define P54_SCAN_TRAP BIT(1)
+#define P54_SCAN_ACTIVE BIT(2)
+#define P54_SCAN_FILTER BIT(3)
+
+struct p54_scan_head {
+ __le16 mode;
+ __le16 dwell;
+ u8 scan_params[20];
+ __le16 freq;
+} __packed;
+
+struct p54_pa_curve_data_sample {
+ u8 rf_power;
+ u8 pa_detector;
+ u8 data_barker;
+ u8 data_bpsk;
+ u8 data_qpsk;
+ u8 data_16qam;
+ u8 data_64qam;
+ u8 padding;
+} __packed;
+
+struct p54_scan_body {
+ u8 pa_points_per_curve;
+ u8 val_barker;
+ u8 val_bpsk;
+ u8 val_qpsk;
+ u8 val_16qam;
+ u8 val_64qam;
+ struct p54_pa_curve_data_sample curve_data[8];
+ u8 dup_bpsk;
+ u8 dup_qpsk;
+ u8 dup_16qam;
+ u8 dup_64qam;
+} __packed;
+
+/*
+ * Warning: Longbow's structures are bogus.
+ */
+struct p54_channel_output_limit_longbow {
+ __le16 rf_power_points[12];
+} __packed;
+
+struct p54_pa_curve_data_sample_longbow {
+ __le16 rf_power;
+ __le16 pa_detector;
+ struct {
+ __le16 data[4];
+ } points[3] __packed;
+} __packed;
+
+struct p54_scan_body_longbow {
+ struct p54_channel_output_limit_longbow power_limits;
+ struct p54_pa_curve_data_sample_longbow curve_data[8];
+ __le16 unkn[6]; /* maybe more power_limits or rate_mask */
+} __packed;
+
+union p54_scan_body_union {
+ struct p54_scan_body normal;
+ struct p54_scan_body_longbow longbow;
+} __packed;
+
+struct p54_scan_tail_rate {
+ __le32 basic_rate_mask;
+ u8 rts_rates[8];
+} __packed;
+
+struct p54_led {
+ __le16 flags;
+ __le16 mask[2];
+ __le16 delay[2];
+} __packed;
+
+struct p54_edcf {
+ u8 flags;
+ u8 slottime;
+ u8 sifs;
+ u8 eofpad;
+ struct p54_edcf_queue_param queue[8];
+ u8 mapping[4];
+ __le16 frameburst;
+ __le16 round_trip_delay;
+} __packed;
+
+struct p54_statistics {
+ __le32 rx_success;
+ __le32 rx_bad_fcs;
+ __le32 rx_abort;
+ __le32 rx_abort_phy;
+ __le32 rts_success;
+ __le32 rts_fail;
+ __le32 tsf32;
+ __le32 airtime;
+ __le32 noise;
+ __le32 sample_noise[8];
+ __le32 sample_cca;
+ __le32 sample_tx;
+} __packed;
+
+struct p54_xbow_synth {
+ __le16 magic1;
+ __le16 magic2;
+ __le16 freq;
+ u32 padding[5];
+} __packed;
+
+struct p54_timer {
+ __le32 interval;
+} __packed;
+
+struct p54_keycache {
+ u8 entry;
+ u8 key_id;
+ u8 mac[ETH_ALEN];
+ u8 padding[2];
+ u8 key_type;
+ u8 key_len;
+ u8 key[24];
+} __packed;
+
+struct p54_burst {
+ u8 flags;
+ u8 queue;
+ u8 backlog;
+ u8 pad;
+ __le16 durations[32];
+} __packed;
+
+struct p54_psm_interval {
+ __le16 interval;
+ __le16 periods;
+} __packed;
+
+#define P54_PSM_CAM 0
+#define P54_PSM BIT(0)
+#define P54_PSM_DTIM BIT(1)
+#define P54_PSM_MCBC BIT(2)
+#define P54_PSM_CHECKSUM BIT(3)
+#define P54_PSM_SKIP_MORE_DATA BIT(4)
+#define P54_PSM_BEACON_TIMEOUT BIT(5)
+#define P54_PSM_HFOSLEEP BIT(6)
+#define P54_PSM_AUTOSWITCH_SLEEP BIT(7)
+#define P54_PSM_LPIT BIT(8)
+#define P54_PSM_BF_UCAST_SKIP BIT(9)
+#define P54_PSM_BF_MCAST_SKIP BIT(10)
+
+struct p54_psm {
+ __le16 mode;
+ __le16 aid;
+ struct p54_psm_interval intervals[4];
+ u8 beacon_rssi_skip_max;
+ u8 rssi_delta_threshold;
+ u8 nr;
+ u8 exclude[1];
+} __packed;
+
+#define MC_FILTER_ADDRESS_NUM 4
+
+struct p54_group_address_table {
+ __le16 filter_enable;
+ __le16 num_address;
+ u8 mac_list[MC_FILTER_ADDRESS_NUM][ETH_ALEN];
+} __packed;
+
+struct p54_txcancel {
+ __le32 req_id;
+} __packed;
+
+struct p54_sta_unlock {
+ u8 addr[ETH_ALEN];
+ u16 padding;
+} __packed;
+
+#define P54_TIM_CLEAR BIT(15)
+struct p54_tim {
+ u8 count;
+ u8 padding[3];
+ __le16 entry[8];
+} __packed;
+
+struct p54_cce_quiet {
+ __le32 period;
+} __packed;
+
+struct p54_bt_balancer {
+ __le16 prio_thresh;
+ __le16 acl_thresh;
+} __packed;
+
+struct p54_arp_table {
+ __le16 filter_enable;
+ u8 ipv4_addr[4];
+} __packed;
+
+/* LED control */
+int p54_set_leds(struct p54_common *priv);
+int p54_init_leds(struct p54_common *priv);
+void p54_unregister_leds(struct p54_common *priv);
+
+/* xmit functions */
+void p54_tx_80211(struct ieee80211_hw *dev,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb);
+int p54_tx_cancel(struct p54_common *priv, __le32 req_id);
+void p54_tx(struct p54_common *priv, struct sk_buff *skb);
+
+/* synth/phy configuration */
+int p54_init_xbow_synth(struct p54_common *priv);
+int p54_scan(struct p54_common *priv, u16 mode, u16 dwell);
+
+/* MAC */
+int p54_sta_unlock(struct p54_common *priv, u8 *addr);
+int p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set);
+int p54_setup_mac(struct p54_common *priv);
+int p54_set_ps(struct p54_common *priv);
+int p54_fetch_statistics(struct p54_common *priv);
+int p54_set_groupfilter(struct p54_common *priv);
+
+/* e/v DCF setup */
+int p54_set_edcf(struct p54_common *priv);
+
+/* cryptographic engine */
+int p54_upload_key(struct p54_common *priv, u8 algo, int slot,
+ u8 idx, u8 len, u8 *addr, u8* key);
+
+/* eeprom */
+int p54_download_eeprom(struct p54_common *priv, void *buf,
+ u16 offset, u16 len);
+struct p54_rssi_db_entry *p54_rssi_find(struct p54_common *p, const u16 freq);
+
+/* utility */
+u8 *p54_find_ie(struct sk_buff *skb, u8 ie);
+
+#endif /* LMAC_H */
--- /dev/null
+/*
+ * mac80211 glue code for mac80211 Prism54 drivers
+ *
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * Based on:
+ * - the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ * - stlc45xx driver
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+
+#include <net/mac80211.h>
+
+#include "p54.h"
+#include "lmac.h"
+
+static bool modparam_nohwcrypt;
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
+MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
+MODULE_DESCRIPTION("Softmac Prism54 common code");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("prism54common");
+
+static int p54_sta_add_remove(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct p54_common *priv = hw->priv;
+
+ /*
+ * Notify the firmware that we don't want or we don't
+ * need to buffer frames for this station anymore.
+ */
+
+ p54_sta_unlock(priv, sta->addr);
+
+ return 0;
+}
+
+static void p54_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ struct ieee80211_sta *sta)
+{
+ struct p54_common *priv = dev->priv;
+
+ switch (notify_cmd) {
+ case STA_NOTIFY_AWAKE:
+ /* update the firmware's filter table */
+ p54_sta_unlock(priv, sta->addr);
+ break;
+ default:
+ break;
+ }
+}
+
+static int p54_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
+ bool set)
+{
+ struct p54_common *priv = dev->priv;
+
+ return p54_update_beacon_tim(priv, sta->aid, set);
+}
+
+u8 *p54_find_ie(struct sk_buff *skb, u8 ie)
+{
+ struct ieee80211_mgmt *mgmt = (void *)skb->data;
+ u8 *pos, *end;
+
+ if (skb->len <= sizeof(mgmt))
+ return NULL;
+
+ pos = (u8 *)mgmt->u.beacon.variable;
+ end = skb->data + skb->len;
+ while (pos < end) {
+ if (pos + 2 + pos[1] > end)
+ return NULL;
+
+ if (pos[0] == ie)
+ return pos;
+
+ pos += 2 + pos[1];
+ }
+ return NULL;
+}
+
+static int p54_beacon_format_ie_tim(struct sk_buff *skb)
+{
+ /*
+ * the good excuse for this mess is ... the firmware.
+ * The dummy TIM MUST be at the end of the beacon frame,
+ * because it'll be overwritten!
+ */
+ u8 *tim;
+ u8 dtim_len;
+ u8 dtim_period;
+ u8 *next;
+
+ tim = p54_find_ie(skb, WLAN_EID_TIM);
+ if (!tim)
+ return 0;
+
+ dtim_len = tim[1];
+ dtim_period = tim[3];
+ next = tim + 2 + dtim_len;
+
+ if (dtim_len < 3)
+ return -EINVAL;
+
+ memmove(tim, next, skb_tail_pointer(skb) - next);
+ tim = skb_tail_pointer(skb) - (dtim_len + 2);
+
+ /* add the dummy at the end */
+ tim[0] = WLAN_EID_TIM;
+ tim[1] = 3;
+ tim[2] = 0;
+ tim[3] = dtim_period;
+ tim[4] = 0;
+
+ if (dtim_len > 3)
+ skb_trim(skb, skb->len - (dtim_len - 3));
+
+ return 0;
+}
+
+static int p54_beacon_update(struct p54_common *priv,
+ struct ieee80211_vif *vif)
+{
+ struct ieee80211_tx_control control = { };
+ struct sk_buff *beacon;
+ int ret;
+
+ beacon = ieee80211_beacon_get(priv->hw, vif);
+ if (!beacon)
+ return -ENOMEM;
+ ret = p54_beacon_format_ie_tim(beacon);
+ if (ret)
+ return ret;
+
+ /*
+ * During operation, the firmware takes care of beaconing.
+ * The driver only needs to upload a new beacon template, once
+ * the template was changed by the stack or userspace.
+ *
+ * LMAC API 3.2.2 also specifies that the driver does not need
+ * to cancel the old beacon template by hand, instead the firmware
+ * will release the previous one through the feedback mechanism.
+ */
+ p54_tx_80211(priv->hw, &control, beacon);
+ priv->tsf_high32 = 0;
+ priv->tsf_low32 = 0;
+
+ return 0;
+}
+
+static int p54_start(struct ieee80211_hw *dev)
+{
+ struct p54_common *priv = dev->priv;
+ int err;
+
+ mutex_lock(&priv->conf_mutex);
+ err = priv->open(dev);
+ if (err)
+ goto out;
+ P54_SET_QUEUE(priv->qos_params[0], 0x0002, 0x0003, 0x0007, 47);
+ P54_SET_QUEUE(priv->qos_params[1], 0x0002, 0x0007, 0x000f, 94);
+ P54_SET_QUEUE(priv->qos_params[2], 0x0003, 0x000f, 0x03ff, 0);
+ P54_SET_QUEUE(priv->qos_params[3], 0x0007, 0x000f, 0x03ff, 0);
+ err = p54_set_edcf(priv);
+ if (err)
+ goto out;
+
+ eth_broadcast_addr(priv->bssid);
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ err = p54_setup_mac(priv);
+ if (err) {
+ priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+ goto out;
+ }
+
+ ieee80211_queue_delayed_work(dev, &priv->work, 0);
+
+ priv->softled_state = 0;
+ err = p54_set_leds(priv);
+
+out:
+ mutex_unlock(&priv->conf_mutex);
+ return err;
+}
+
+static void p54_stop(struct ieee80211_hw *dev)
+{
+ struct p54_common *priv = dev->priv;
+ int i;
+
+ priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->softled_state = 0;
+ cancel_delayed_work_sync(&priv->work);
+ mutex_lock(&priv->conf_mutex);
+ p54_set_leds(priv);
+ priv->stop(dev);
+ skb_queue_purge(&priv->tx_pending);
+ skb_queue_purge(&priv->tx_queue);
+ for (i = 0; i < P54_QUEUE_NUM; i++) {
+ priv->tx_stats[i].count = 0;
+ priv->tx_stats[i].len = 0;
+ }
+
+ priv->beacon_req_id = cpu_to_le32(0);
+ priv->tsf_high32 = priv->tsf_low32 = 0;
+ mutex_unlock(&priv->conf_mutex);
+}
+
+static int p54_add_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+{
+ struct p54_common *priv = dev->priv;
+ int err;
+
+ vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
+
+ mutex_lock(&priv->conf_mutex);
+ if (priv->mode != NL80211_IFTYPE_MONITOR) {
+ mutex_unlock(&priv->conf_mutex);
+ return -EOPNOTSUPP;
+ }
+
+ priv->vif = vif;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_MESH_POINT:
+ priv->mode = vif->type;
+ break;
+ default:
+ mutex_unlock(&priv->conf_mutex);
+ return -EOPNOTSUPP;
+ }
+
+ memcpy(priv->mac_addr, vif->addr, ETH_ALEN);
+ err = p54_setup_mac(priv);
+ mutex_unlock(&priv->conf_mutex);
+ return err;
+}
+
+static void p54_remove_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+{
+ struct p54_common *priv = dev->priv;
+
+ mutex_lock(&priv->conf_mutex);
+ priv->vif = NULL;
+
+ /*
+ * LMAC API 3.2.2 states that any active beacon template must be
+ * canceled by the driver before attempting a mode transition.
+ */
+ if (le32_to_cpu(priv->beacon_req_id) != 0) {
+ p54_tx_cancel(priv, priv->beacon_req_id);
+ wait_for_completion_interruptible_timeout(&priv->beacon_comp, HZ);
+ }
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ eth_zero_addr(priv->mac_addr);
+ eth_zero_addr(priv->bssid);
+ p54_setup_mac(priv);
+ mutex_unlock(&priv->conf_mutex);
+}
+
+static int p54_wait_for_stats(struct ieee80211_hw *dev)
+{
+ struct p54_common *priv = dev->priv;
+ int ret;
+
+ priv->update_stats = true;
+ ret = p54_fetch_statistics(priv);
+ if (ret)
+ return ret;
+
+ ret = wait_for_completion_interruptible_timeout(&priv->stat_comp, HZ);
+ if (ret == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static void p54_reset_stats(struct p54_common *priv)
+{
+ struct ieee80211_channel *chan = priv->curchan;
+
+ if (chan) {
+ struct survey_info *info = &priv->survey[chan->hw_value];
+
+ /* only reset channel statistics, don't touch .filled, etc. */
+ info->time = 0;
+ info->time_busy = 0;
+ info->time_tx = 0;
+ }
+
+ priv->update_stats = true;
+ priv->survey_raw.active = 0;
+ priv->survey_raw.cca = 0;
+ priv->survey_raw.tx = 0;
+}
+
+static int p54_config(struct ieee80211_hw *dev, u32 changed)
+{
+ int ret = 0;
+ struct p54_common *priv = dev->priv;
+ struct ieee80211_conf *conf = &dev->conf;
+
+ mutex_lock(&priv->conf_mutex);
+ if (changed & IEEE80211_CONF_CHANGE_POWER)
+ priv->output_power = conf->power_level << 2;
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+ struct ieee80211_channel *oldchan;
+ WARN_ON(p54_wait_for_stats(dev));
+ oldchan = priv->curchan;
+ priv->curchan = NULL;
+ ret = p54_scan(priv, P54_SCAN_EXIT, 0);
+ if (ret) {
+ priv->curchan = oldchan;
+ goto out;
+ }
+ /*
+ * TODO: Use the LM_SCAN_TRAP to determine the current
+ * operating channel.
+ */
+ priv->curchan = priv->hw->conf.chandef.chan;
+ p54_reset_stats(priv);
+ WARN_ON(p54_fetch_statistics(priv));
+ }
+ if (changed & IEEE80211_CONF_CHANGE_PS) {
+ WARN_ON(p54_wait_for_stats(dev));
+ ret = p54_set_ps(priv);
+ if (ret)
+ goto out;
+ WARN_ON(p54_wait_for_stats(dev));
+ }
+ if (changed & IEEE80211_CONF_CHANGE_IDLE) {
+ WARN_ON(p54_wait_for_stats(dev));
+ ret = p54_setup_mac(priv);
+ if (ret)
+ goto out;
+ WARN_ON(p54_wait_for_stats(dev));
+ }
+
+out:
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+static u64 p54_prepare_multicast(struct ieee80211_hw *dev,
+ struct netdev_hw_addr_list *mc_list)
+{
+ struct p54_common *priv = dev->priv;
+ struct netdev_hw_addr *ha;
+ int i;
+
+ BUILD_BUG_ON(ARRAY_SIZE(priv->mc_maclist) !=
+ ARRAY_SIZE(((struct p54_group_address_table *)NULL)->mac_list));
+ /*
+ * The first entry is reserved for the global broadcast MAC.
+ * Otherwise the firmware will drop it and ARP will no longer work.
+ */
+ i = 1;
+ priv->mc_maclist_num = netdev_hw_addr_list_count(mc_list) + i;
+ netdev_hw_addr_list_for_each(ha, mc_list) {
+ memcpy(&priv->mc_maclist[i], ha->addr, ETH_ALEN);
+ i++;
+ if (i >= ARRAY_SIZE(priv->mc_maclist))
+ break;
+ }
+
+ return 1; /* update */
+}
+
+static void p54_configure_filter(struct ieee80211_hw *dev,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast)
+{
+ struct p54_common *priv = dev->priv;
+
+ *total_flags &= FIF_ALLMULTI | FIF_OTHER_BSS;
+
+ priv->filter_flags = *total_flags;
+
+ if (changed_flags & FIF_OTHER_BSS)
+ p54_setup_mac(priv);
+
+ if (changed_flags & FIF_ALLMULTI || multicast)
+ p54_set_groupfilter(priv);
+}
+
+static int p54_conf_tx(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct p54_common *priv = dev->priv;
+ int ret;
+
+ mutex_lock(&priv->conf_mutex);
+ if (queue < dev->queues) {
+ P54_SET_QUEUE(priv->qos_params[queue], params->aifs,
+ params->cw_min, params->cw_max, params->txop);
+ ret = p54_set_edcf(priv);
+ } else
+ ret = -EINVAL;
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+static void p54_work(struct work_struct *work)
+{
+ struct p54_common *priv = container_of(work, struct p54_common,
+ work.work);
+
+ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
+ return ;
+
+ /*
+ * TODO: walk through tx_queue and do the following tasks
+ * 1. initiate bursts.
+ * 2. cancel stuck frames / reset the device if necessary.
+ */
+
+ mutex_lock(&priv->conf_mutex);
+ WARN_ON_ONCE(p54_fetch_statistics(priv));
+ mutex_unlock(&priv->conf_mutex);
+}
+
+static int p54_get_stats(struct ieee80211_hw *dev,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct p54_common *priv = dev->priv;
+
+ memcpy(stats, &priv->stats, sizeof(*stats));
+ return 0;
+}
+
+static void p54_bss_info_changed(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed)
+{
+ struct p54_common *priv = dev->priv;
+
+ mutex_lock(&priv->conf_mutex);
+ if (changed & BSS_CHANGED_BSSID) {
+ memcpy(priv->bssid, info->bssid, ETH_ALEN);
+ p54_setup_mac(priv);
+ }
+
+ if (changed & BSS_CHANGED_BEACON) {
+ p54_scan(priv, P54_SCAN_EXIT, 0);
+ p54_setup_mac(priv);
+ p54_beacon_update(priv, vif);
+ p54_set_edcf(priv);
+ }
+
+ if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BEACON)) {
+ priv->use_short_slot = info->use_short_slot;
+ p54_set_edcf(priv);
+ }
+ if (changed & BSS_CHANGED_BASIC_RATES) {
+ if (dev->conf.chandef.chan->band == IEEE80211_BAND_5GHZ)
+ priv->basic_rate_mask = (info->basic_rates << 4);
+ else
+ priv->basic_rate_mask = info->basic_rates;
+ p54_setup_mac(priv);
+ if (priv->fw_var >= 0x500)
+ p54_scan(priv, P54_SCAN_EXIT, 0);
+ }
+ if (changed & BSS_CHANGED_ASSOC) {
+ if (info->assoc) {
+ priv->aid = info->aid;
+ priv->wakeup_timer = info->beacon_int *
+ info->dtim_period * 5;
+ p54_setup_mac(priv);
+ } else {
+ priv->wakeup_timer = 500;
+ priv->aid = 0;
+ }
+ }
+
+ mutex_unlock(&priv->conf_mutex);
+}
+
+static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct p54_common *priv = dev->priv;
+ int slot, ret = 0;
+ u8 algo = 0;
+ u8 *addr = NULL;
+
+ if (modparam_nohwcrypt)
+ return -EOPNOTSUPP;
+
+ if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT) {
+ /*
+ * Unfortunately most/all firmwares are trying to decrypt
+ * incoming management frames if a suitable key can be found.
+ * However, in doing so the data in these frames gets
+ * corrupted. So, we can't have firmware supported crypto
+ * offload in this case.
+ */
+ return -EOPNOTSUPP;
+ }
+
+ mutex_lock(&priv->conf_mutex);
+ if (cmd == SET_KEY) {
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (!(priv->privacy_caps & (BR_DESC_PRIV_CAP_MICHAEL |
+ BR_DESC_PRIV_CAP_TKIP))) {
+ ret = -EOPNOTSUPP;
+ goto out_unlock;
+ }
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ algo = P54_CRYPTO_TKIPMICHAEL;
+ break;
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP)) {
+ ret = -EOPNOTSUPP;
+ goto out_unlock;
+ }
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ algo = P54_CRYPTO_WEP;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)) {
+ ret = -EOPNOTSUPP;
+ goto out_unlock;
+ }
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ algo = P54_CRYPTO_AESCCMP;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ goto out_unlock;
+ }
+ slot = bitmap_find_free_region(priv->used_rxkeys,
+ priv->rx_keycache_size, 0);
+
+ if (slot < 0) {
+ /*
+ * The device supports the chosen algorithm, but the
+ * firmware does not provide enough key slots to store
+ * all of them.
+ * But encryption offload for outgoing frames is always
+ * possible, so we just pretend that the upload was
+ * successful and do the decryption in software.
+ */
+
+ /* mark the key as invalid. */
+ key->hw_key_idx = 0xff;
+ goto out_unlock;
+ }
+
+ key->flags |= IEEE80211_KEY_FLAG_RESERVE_TAILROOM;
+ } else {
+ slot = key->hw_key_idx;
+
+ if (slot == 0xff) {
+ /* This key was not uploaded into the rx key cache. */
+
+ goto out_unlock;
+ }
+
+ bitmap_release_region(priv->used_rxkeys, slot, 0);
+ algo = 0;
+ }
+
+ if (sta)
+ addr = sta->addr;
+
+ ret = p54_upload_key(priv, algo, slot, key->keyidx,
+ key->keylen, addr, key->key);
+ if (ret) {
+ bitmap_release_region(priv->used_rxkeys, slot, 0);
+ ret = -EOPNOTSUPP;
+ goto out_unlock;
+ }
+
+ key->hw_key_idx = slot;
+
+out_unlock:
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+static int p54_get_survey(struct ieee80211_hw *dev, int idx,
+ struct survey_info *survey)
+{
+ struct p54_common *priv = dev->priv;
+ struct ieee80211_channel *chan;
+ int err, tries;
+ bool in_use = false;
+
+ if (idx >= priv->chan_num)
+ return -ENOENT;
+
+#define MAX_TRIES 1
+ for (tries = 0; tries < MAX_TRIES; tries++) {
+ chan = priv->curchan;
+ if (chan && chan->hw_value == idx) {
+ mutex_lock(&priv->conf_mutex);
+ err = p54_wait_for_stats(dev);
+ mutex_unlock(&priv->conf_mutex);
+ if (err)
+ return err;
+
+ in_use = true;
+ }
+
+ memcpy(survey, &priv->survey[idx], sizeof(*survey));
+
+ if (in_use) {
+ /* test if the reported statistics are valid. */
+ if (survey->time != 0) {
+ survey->filled |= SURVEY_INFO_IN_USE;
+ } else {
+ /*
+ * hw/fw has not accumulated enough sample sets.
+ * Wait for 100ms, this ought to be enough to
+ * to get at least one non-null set of channel
+ * usage statistics.
+ */
+ msleep(100);
+ continue;
+ }
+ }
+ return 0;
+ }
+ return -ETIMEDOUT;
+#undef MAX_TRIES
+}
+
+static unsigned int p54_flush_count(struct p54_common *priv)
+{
+ unsigned int total = 0, i;
+
+ BUILD_BUG_ON(P54_QUEUE_NUM > ARRAY_SIZE(priv->tx_stats));
+
+ /*
+ * Because the firmware has the sole control over any frames
+ * in the P54_QUEUE_BEACON or P54_QUEUE_SCAN queues, they
+ * don't really count as pending or active.
+ */
+ for (i = P54_QUEUE_MGMT; i < P54_QUEUE_NUM; i++)
+ total += priv->tx_stats[i].len;
+ return total;
+}
+
+static void p54_flush(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+ u32 queues, bool drop)
+{
+ struct p54_common *priv = dev->priv;
+ unsigned int total, i;
+
+ /*
+ * Currently, it wouldn't really matter if we wait for one second
+ * or 15 minutes. But once someone gets around and completes the
+ * TODOs [ancel stuck frames / reset device] in p54_work, it will
+ * suddenly make sense to wait that long.
+ */
+ i = P54_STATISTICS_UPDATE * 2 / 20;
+
+ /*
+ * In this case no locking is required because as we speak the
+ * queues have already been stopped and no new frames can sneak
+ * up from behind.
+ */
+ while ((total = p54_flush_count(priv) && i--)) {
+ /* waste time */
+ msleep(20);
+ }
+
+ WARN(total, "tx flush timeout, unresponsive firmware");
+}
+
+static void p54_set_coverage_class(struct ieee80211_hw *dev,
+ s16 coverage_class)
+{
+ struct p54_common *priv = dev->priv;
+
+ mutex_lock(&priv->conf_mutex);
+ /* support all coverage class values as in 802.11-2007 Table 7-27 */
+ priv->coverage_class = clamp_t(u8, coverage_class, 0, 31);
+ p54_set_edcf(priv);
+ mutex_unlock(&priv->conf_mutex);
+}
+
+static const struct ieee80211_ops p54_ops = {
+ .tx = p54_tx_80211,
+ .start = p54_start,
+ .stop = p54_stop,
+ .add_interface = p54_add_interface,
+ .remove_interface = p54_remove_interface,
+ .set_tim = p54_set_tim,
+ .sta_notify = p54_sta_notify,
+ .sta_add = p54_sta_add_remove,
+ .sta_remove = p54_sta_add_remove,
+ .set_key = p54_set_key,
+ .config = p54_config,
+ .flush = p54_flush,
+ .bss_info_changed = p54_bss_info_changed,
+ .prepare_multicast = p54_prepare_multicast,
+ .configure_filter = p54_configure_filter,
+ .conf_tx = p54_conf_tx,
+ .get_stats = p54_get_stats,
+ .get_survey = p54_get_survey,
+ .set_coverage_class = p54_set_coverage_class,
+};
+
+struct ieee80211_hw *p54_init_common(size_t priv_data_len)
+{
+ struct ieee80211_hw *dev;
+ struct p54_common *priv;
+
+ dev = ieee80211_alloc_hw(priv_data_len, &p54_ops);
+ if (!dev)
+ return NULL;
+
+ priv = dev->priv;
+ priv->hw = dev;
+ priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->basic_rate_mask = 0x15f;
+ spin_lock_init(&priv->tx_stats_lock);
+ skb_queue_head_init(&priv->tx_queue);
+ skb_queue_head_init(&priv->tx_pending);
+ ieee80211_hw_set(dev, REPORTS_TX_ACK_STATUS);
+ ieee80211_hw_set(dev, MFP_CAPABLE);
+ ieee80211_hw_set(dev, PS_NULLFUNC_STACK);
+ ieee80211_hw_set(dev, SUPPORTS_PS);
+ ieee80211_hw_set(dev, RX_INCLUDES_FCS);
+ ieee80211_hw_set(dev, SIGNAL_DBM);
+
+ dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_MESH_POINT);
+
+ priv->beacon_req_id = cpu_to_le32(0);
+ priv->tx_stats[P54_QUEUE_BEACON].limit = 1;
+ priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1;
+ priv->tx_stats[P54_QUEUE_MGMT].limit = 3;
+ priv->tx_stats[P54_QUEUE_CAB].limit = 3;
+ priv->tx_stats[P54_QUEUE_DATA].limit = 5;
+ dev->queues = 1;
+ priv->noise = -94;
+ /*
+ * We support at most 8 tries no matter which rate they're at,
+ * we cannot support max_rates * max_rate_tries as we set it
+ * here, but setting it correctly to 4/2 or so would limit us
+ * artificially if the RC algorithm wants just two rates, so
+ * let's say 4/7, we'll redistribute it at TX time, see the
+ * comments there.
+ */
+ dev->max_rates = 4;
+ dev->max_rate_tries = 7;
+ dev->extra_tx_headroom = sizeof(struct p54_hdr) + 4 +
+ sizeof(struct p54_tx_data);
+
+ /*
+ * For now, disable PS by default because it affects
+ * link stability significantly.
+ */
+ dev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
+ mutex_init(&priv->conf_mutex);
+ mutex_init(&priv->eeprom_mutex);
+ init_completion(&priv->stat_comp);
+ init_completion(&priv->eeprom_comp);
+ init_completion(&priv->beacon_comp);
+ INIT_DELAYED_WORK(&priv->work, p54_work);
+
+ eth_broadcast_addr(priv->mc_maclist[0]);
+ priv->curchan = NULL;
+ p54_reset_stats(priv);
+ return dev;
+}
+EXPORT_SYMBOL_GPL(p54_init_common);
+
+int p54_register_common(struct ieee80211_hw *dev, struct device *pdev)
+{
+ struct p54_common __maybe_unused *priv = dev->priv;
+ int err;
+
+ err = ieee80211_register_hw(dev);
+ if (err) {
+ dev_err(pdev, "Cannot register device (%d).\n", err);
+ return err;
+ }
+ priv->registered = true;
+
+#ifdef CONFIG_P54_LEDS
+ err = p54_init_leds(priv);
+ if (err) {
+ p54_unregister_common(dev);
+ return err;
+ }
+#endif /* CONFIG_P54_LEDS */
+
+ dev_info(pdev, "is registered as '%s'\n", wiphy_name(dev->wiphy));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(p54_register_common);
+
+void p54_free_common(struct ieee80211_hw *dev)
+{
+ struct p54_common *priv = dev->priv;
+ unsigned int i;
+
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++)
+ kfree(priv->band_table[i]);
+
+ kfree(priv->iq_autocal);
+ kfree(priv->output_limit);
+ kfree(priv->curve_data);
+ kfree(priv->rssi_db);
+ kfree(priv->used_rxkeys);
+ kfree(priv->survey);
+ priv->iq_autocal = NULL;
+ priv->output_limit = NULL;
+ priv->curve_data = NULL;
+ priv->rssi_db = NULL;
+ priv->used_rxkeys = NULL;
+ priv->survey = NULL;
+ ieee80211_free_hw(dev);
+}
+EXPORT_SYMBOL_GPL(p54_free_common);
+
+void p54_unregister_common(struct ieee80211_hw *dev)
+{
+ struct p54_common *priv = dev->priv;
+
+#ifdef CONFIG_P54_LEDS
+ p54_unregister_leds(priv);
+#endif /* CONFIG_P54_LEDS */
+
+ if (priv->registered) {
+ priv->registered = false;
+ ieee80211_unregister_hw(dev);
+ }
+
+ mutex_destroy(&priv->conf_mutex);
+ mutex_destroy(&priv->eeprom_mutex);
+}
+EXPORT_SYMBOL_GPL(p54_unregister_common);
--- /dev/null
+/*
+ * Shared defines for all mac80211 Prism54 code
+ *
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ *
+ * Based on the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef P54_H
+#define P54_H
+
+#ifdef CONFIG_P54_LEDS
+#include <linux/leds.h>
+#endif /* CONFIG_P54_LEDS */
+
+#define ISL38XX_DEV_FIRMWARE_ADDR 0x20000
+
+#define BR_CODE_MIN 0x80000000
+#define BR_CODE_COMPONENT_ID 0x80000001
+#define BR_CODE_COMPONENT_VERSION 0x80000002
+#define BR_CODE_DEPENDENT_IF 0x80000003
+#define BR_CODE_EXPOSED_IF 0x80000004
+#define BR_CODE_DESCR 0x80000101
+#define BR_CODE_MAX 0x8FFFFFFF
+#define BR_CODE_END_OF_BRA 0xFF0000FF
+#define LEGACY_BR_CODE_END_OF_BRA 0xFFFFFFFF
+
+struct bootrec {
+ __le32 code;
+ __le32 len;
+ u32 data[10];
+} __packed;
+
+/* Interface role definitions */
+#define BR_INTERFACE_ROLE_SERVER 0x0000
+#define BR_INTERFACE_ROLE_CLIENT 0x8000
+
+#define BR_DESC_PRIV_CAP_WEP BIT(0)
+#define BR_DESC_PRIV_CAP_TKIP BIT(1)
+#define BR_DESC_PRIV_CAP_MICHAEL BIT(2)
+#define BR_DESC_PRIV_CAP_CCX_CP BIT(3)
+#define BR_DESC_PRIV_CAP_CCX_MIC BIT(4)
+#define BR_DESC_PRIV_CAP_AESCCMP BIT(5)
+
+struct bootrec_desc {
+ __le16 modes;
+ __le16 flags;
+ __le32 rx_start;
+ __le32 rx_end;
+ u8 headroom;
+ u8 tailroom;
+ u8 tx_queues;
+ u8 tx_depth;
+ u8 privacy_caps;
+ u8 rx_keycache_size;
+ u8 time_size;
+ u8 padding;
+ u8 rates[16];
+ u8 padding2[4];
+ __le16 rx_mtu;
+} __packed;
+
+#define FW_FMAC 0x464d4143
+#define FW_LM86 0x4c4d3836
+#define FW_LM87 0x4c4d3837
+#define FW_LM20 0x4c4d3230
+
+struct bootrec_comp_id {
+ __le32 fw_variant;
+} __packed;
+
+struct bootrec_comp_ver {
+ char fw_version[24];
+} __packed;
+
+struct bootrec_end {
+ __le16 crc;
+ u8 padding[2];
+ u8 md5[16];
+} __packed;
+
+/* provide 16 bytes for the transport back-end */
+#define P54_TX_INFO_DATA_SIZE 16
+
+/* stored in ieee80211_tx_info's rate_driver_data */
+struct p54_tx_info {
+ u32 start_addr;
+ u32 end_addr;
+ union {
+ void *data[P54_TX_INFO_DATA_SIZE / sizeof(void *)];
+ struct {
+ u32 extra_len;
+ };
+ };
+};
+
+#define P54_MAX_CTRL_FRAME_LEN 0x1000
+
+#define P54_SET_QUEUE(queue, ai_fs, cw_min, cw_max, _txop) \
+do { \
+ queue.aifs = cpu_to_le16(ai_fs); \
+ queue.cwmin = cpu_to_le16(cw_min); \
+ queue.cwmax = cpu_to_le16(cw_max); \
+ queue.txop = cpu_to_le16(_txop); \
+} while (0)
+
+struct p54_edcf_queue_param {
+ __le16 aifs;
+ __le16 cwmin;
+ __le16 cwmax;
+ __le16 txop;
+} __packed;
+
+struct p54_rssi_db_entry {
+ u16 freq;
+ s16 mul;
+ s16 add;
+ s16 longbow_unkn;
+ s16 longbow_unk2;
+};
+
+struct p54_cal_database {
+ size_t entries;
+ size_t entry_size;
+ size_t offset;
+ size_t len;
+ u8 data[0];
+};
+
+#define EEPROM_READBACK_LEN 0x3fc
+
+enum fw_state {
+ FW_STATE_OFF,
+ FW_STATE_BOOTING,
+ FW_STATE_READY,
+ FW_STATE_RESET,
+ FW_STATE_RESETTING,
+};
+
+#ifdef CONFIG_P54_LEDS
+
+#define P54_LED_MAX_NAME_LEN 31
+
+struct p54_led_dev {
+ struct ieee80211_hw *hw_dev;
+ struct led_classdev led_dev;
+ char name[P54_LED_MAX_NAME_LEN + 1];
+
+ unsigned int toggled;
+ unsigned int index;
+ unsigned int registered;
+};
+
+#endif /* CONFIG_P54_LEDS */
+
+struct p54_tx_queue_stats {
+ unsigned int len;
+ unsigned int limit;
+ unsigned int count;
+};
+
+struct p54_common {
+ struct ieee80211_hw *hw;
+ struct ieee80211_vif *vif;
+ void (*tx)(struct ieee80211_hw *dev, struct sk_buff *skb);
+ int (*open)(struct ieee80211_hw *dev);
+ void (*stop)(struct ieee80211_hw *dev);
+ struct sk_buff_head tx_pending;
+ struct sk_buff_head tx_queue;
+ struct mutex conf_mutex;
+ bool registered;
+
+ /* memory management (as seen by the firmware) */
+ u32 rx_start;
+ u32 rx_end;
+ u16 rx_mtu;
+ u8 headroom;
+ u8 tailroom;
+
+ /* firmware/hardware info */
+ unsigned int tx_hdr_len;
+ unsigned int fw_var;
+ unsigned int fw_interface;
+ u8 version;
+
+ /* (e)DCF / QOS state */
+ bool use_short_slot;
+ spinlock_t tx_stats_lock;
+ struct p54_tx_queue_stats tx_stats[8];
+ struct p54_edcf_queue_param qos_params[8];
+
+ /* Radio data */
+ u16 rxhw;
+ u8 rx_diversity_mask;
+ u8 tx_diversity_mask;
+ unsigned int output_power;
+ struct p54_rssi_db_entry *cur_rssi;
+ struct ieee80211_channel *curchan;
+ struct survey_info *survey;
+ unsigned int chan_num;
+ struct completion stat_comp;
+ bool update_stats;
+ struct {
+ unsigned int timestamp;
+ unsigned int cached_cca;
+ unsigned int cached_tx;
+ unsigned int cached_rssi;
+ u64 active;
+ u64 cca;
+ u64 tx;
+ u64 rssi;
+ } survey_raw;
+
+ int noise;
+ /* calibration, output power limit and rssi<->dBm conversation data */
+ struct pda_iq_autocal_entry *iq_autocal;
+ unsigned int iq_autocal_len;
+ struct p54_cal_database *curve_data;
+ struct p54_cal_database *output_limit;
+ struct p54_cal_database *rssi_db;
+ struct ieee80211_supported_band *band_table[IEEE80211_NUM_BANDS];
+
+ /* BBP/MAC state */
+ u8 mac_addr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ u8 mc_maclist[4][ETH_ALEN];
+ u16 wakeup_timer;
+ unsigned int filter_flags;
+ int mc_maclist_num;
+ int mode;
+ u32 tsf_low32, tsf_high32;
+ u32 basic_rate_mask;
+ u16 aid;
+ u8 coverage_class;
+ bool phy_idle;
+ bool phy_ps;
+ bool powersave_override;
+ __le32 beacon_req_id;
+ struct completion beacon_comp;
+
+ /* cryptographic engine information */
+ u8 privacy_caps;
+ u8 rx_keycache_size;
+ unsigned long *used_rxkeys;
+
+ /* LED management */
+#ifdef CONFIG_P54_LEDS
+ struct p54_led_dev leds[4];
+ struct delayed_work led_work;
+#endif /* CONFIG_P54_LEDS */
+ u16 softled_state; /* bit field of glowing LEDs */
+
+ /* statistics */
+ struct ieee80211_low_level_stats stats;
+ struct delayed_work work;
+
+ /* eeprom handling */
+ void *eeprom;
+ struct completion eeprom_comp;
+ struct mutex eeprom_mutex;
+};
+
+/* interfaces for the drivers */
+int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb);
+void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb);
+int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw);
+int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len);
+int p54_read_eeprom(struct ieee80211_hw *dev);
+
+struct ieee80211_hw *p54_init_common(size_t priv_data_len);
+int p54_register_common(struct ieee80211_hw *dev, struct device *pdev);
+void p54_free_common(struct ieee80211_hw *dev);
+
+void p54_unregister_common(struct ieee80211_hw *dev);
+
+#endif /* P54_H */
--- /dev/null
+
+/*
+ * Linux device driver for PCI based Prism54
+ *
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ * Copyright (c) 2008, Christian Lamparter <chunkeey@web.de>
+ *
+ * Based on the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jean-baptiste.note@m4x.org>, et al.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+
+#include "p54.h"
+#include "lmac.h"
+#include "p54pci.h"
+
+MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
+MODULE_DESCRIPTION("Prism54 PCI wireless driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("prism54pci");
+MODULE_FIRMWARE("isl3886pci");
+
+static const struct pci_device_id p54p_table[] = {
+ /* Intersil PRISM Duette/Prism GT Wireless LAN adapter */
+ { PCI_DEVICE(0x1260, 0x3890) },
+ /* 3COM 3CRWE154G72 Wireless LAN adapter */
+ { PCI_DEVICE(0x10b7, 0x6001) },
+ /* Intersil PRISM Indigo Wireless LAN adapter */
+ { PCI_DEVICE(0x1260, 0x3877) },
+ /* Intersil PRISM Javelin/Xbow Wireless LAN adapter */
+ { PCI_DEVICE(0x1260, 0x3886) },
+ /* Intersil PRISM Xbow Wireless LAN adapter (Symbol AP-300) */
+ { PCI_DEVICE(0x1260, 0xffff) },
+ { },
+};
+
+MODULE_DEVICE_TABLE(pci, p54p_table);
+
+static int p54p_upload_firmware(struct ieee80211_hw *dev)
+{
+ struct p54p_priv *priv = dev->priv;
+ __le32 reg;
+ int err;
+ __le32 *data;
+ u32 remains, left, device_addr;
+
+ P54P_WRITE(int_enable, cpu_to_le32(0));
+ P54P_READ(int_enable);
+ udelay(10);
+
+ reg = P54P_READ(ctrl_stat);
+ reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
+ reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT);
+ P54P_WRITE(ctrl_stat, reg);
+ P54P_READ(ctrl_stat);
+ udelay(10);
+
+ reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
+ P54P_WRITE(ctrl_stat, reg);
+ wmb();
+ udelay(10);
+
+ reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
+ P54P_WRITE(ctrl_stat, reg);
+ wmb();
+
+ /* wait for the firmware to reset properly */
+ mdelay(10);
+
+ err = p54_parse_firmware(dev, priv->firmware);
+ if (err)
+ return err;
+
+ if (priv->common.fw_interface != FW_LM86) {
+ dev_err(&priv->pdev->dev, "wrong firmware, "
+ "please get a LM86(PCI) firmware a try again.\n");
+ return -EINVAL;
+ }
+
+ data = (__le32 *) priv->firmware->data;
+ remains = priv->firmware->size;
+ device_addr = ISL38XX_DEV_FIRMWARE_ADDR;
+ while (remains) {
+ u32 i = 0;
+ left = min((u32)0x1000, remains);
+ P54P_WRITE(direct_mem_base, cpu_to_le32(device_addr));
+ P54P_READ(int_enable);
+
+ device_addr += 0x1000;
+ while (i < left) {
+ P54P_WRITE(direct_mem_win[i], *data++);
+ i += sizeof(u32);
+ }
+
+ remains -= left;
+ P54P_READ(int_enable);
+ }
+
+ reg = P54P_READ(ctrl_stat);
+ reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);
+ reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
+ reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT);
+ P54P_WRITE(ctrl_stat, reg);
+ P54P_READ(ctrl_stat);
+ udelay(10);
+
+ reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
+ P54P_WRITE(ctrl_stat, reg);
+ wmb();
+ udelay(10);
+
+ reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
+ P54P_WRITE(ctrl_stat, reg);
+ wmb();
+ udelay(10);
+
+ /* wait for the firmware to boot properly */
+ mdelay(100);
+
+ return 0;
+}
+
+static void p54p_refill_rx_ring(struct ieee80211_hw *dev,
+ int ring_index, struct p54p_desc *ring, u32 ring_limit,
+ struct sk_buff **rx_buf, u32 index)
+{
+ struct p54p_priv *priv = dev->priv;
+ struct p54p_ring_control *ring_control = priv->ring_control;
+ u32 limit, idx, i;
+
+ idx = le32_to_cpu(ring_control->host_idx[ring_index]);
+ limit = idx;
+ limit -= index;
+ limit = ring_limit - limit;
+
+ i = idx % ring_limit;
+ while (limit-- > 1) {
+ struct p54p_desc *desc = &ring[i];
+
+ if (!desc->host_addr) {
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+ skb = dev_alloc_skb(priv->common.rx_mtu + 32);
+ if (!skb)
+ break;
+
+ mapping = pci_map_single(priv->pdev,
+ skb_tail_pointer(skb),
+ priv->common.rx_mtu + 32,
+ PCI_DMA_FROMDEVICE);
+
+ if (pci_dma_mapping_error(priv->pdev, mapping)) {
+ dev_kfree_skb_any(skb);
+ dev_err(&priv->pdev->dev,
+ "RX DMA Mapping error\n");
+ break;
+ }
+
+ desc->host_addr = cpu_to_le32(mapping);
+ desc->device_addr = 0; // FIXME: necessary?
+ desc->len = cpu_to_le16(priv->common.rx_mtu + 32);
+ desc->flags = 0;
+ rx_buf[i] = skb;
+ }
+
+ i++;
+ idx++;
+ i %= ring_limit;
+ }
+
+ wmb();
+ ring_control->host_idx[ring_index] = cpu_to_le32(idx);
+}
+
+static void p54p_check_rx_ring(struct ieee80211_hw *dev, u32 *index,
+ int ring_index, struct p54p_desc *ring, u32 ring_limit,
+ struct sk_buff **rx_buf)
+{
+ struct p54p_priv *priv = dev->priv;
+ struct p54p_ring_control *ring_control = priv->ring_control;
+ struct p54p_desc *desc;
+ u32 idx, i;
+
+ i = (*index) % ring_limit;
+ (*index) = idx = le32_to_cpu(ring_control->device_idx[ring_index]);
+ idx %= ring_limit;
+ while (i != idx) {
+ u16 len;
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+ desc = &ring[i];
+ len = le16_to_cpu(desc->len);
+ skb = rx_buf[i];
+
+ if (!skb) {
+ i++;
+ i %= ring_limit;
+ continue;
+ }
+
+ if (unlikely(len > priv->common.rx_mtu)) {
+ if (net_ratelimit())
+ dev_err(&priv->pdev->dev, "rx'd frame size "
+ "exceeds length threshold.\n");
+
+ len = priv->common.rx_mtu;
+ }
+ dma_addr = le32_to_cpu(desc->host_addr);
+ pci_dma_sync_single_for_cpu(priv->pdev, dma_addr,
+ priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE);
+ skb_put(skb, len);
+
+ if (p54_rx(dev, skb)) {
+ pci_unmap_single(priv->pdev, dma_addr,
+ priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE);
+ rx_buf[i] = NULL;
+ desc->host_addr = cpu_to_le32(0);
+ } else {
+ skb_trim(skb, 0);
+ pci_dma_sync_single_for_device(priv->pdev, dma_addr,
+ priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE);
+ desc->len = cpu_to_le16(priv->common.rx_mtu + 32);
+ }
+
+ i++;
+ i %= ring_limit;
+ }
+
+ p54p_refill_rx_ring(dev, ring_index, ring, ring_limit, rx_buf, *index);
+}
+
+static void p54p_check_tx_ring(struct ieee80211_hw *dev, u32 *index,
+ int ring_index, struct p54p_desc *ring, u32 ring_limit,
+ struct sk_buff **tx_buf)
+{
+ struct p54p_priv *priv = dev->priv;
+ struct p54p_ring_control *ring_control = priv->ring_control;
+ struct p54p_desc *desc;
+ struct sk_buff *skb;
+ u32 idx, i;
+
+ i = (*index) % ring_limit;
+ (*index) = idx = le32_to_cpu(ring_control->device_idx[ring_index]);
+ idx %= ring_limit;
+
+ while (i != idx) {
+ desc = &ring[i];
+
+ skb = tx_buf[i];
+ tx_buf[i] = NULL;
+
+ pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr),
+ le16_to_cpu(desc->len), PCI_DMA_TODEVICE);
+
+ desc->host_addr = 0;
+ desc->device_addr = 0;
+ desc->len = 0;
+ desc->flags = 0;
+
+ if (skb && FREE_AFTER_TX(skb))
+ p54_free_skb(dev, skb);
+
+ i++;
+ i %= ring_limit;
+ }
+}
+
+static void p54p_tasklet(unsigned long dev_id)
+{
+ struct ieee80211_hw *dev = (struct ieee80211_hw *)dev_id;
+ struct p54p_priv *priv = dev->priv;
+ struct p54p_ring_control *ring_control = priv->ring_control;
+
+ p54p_check_tx_ring(dev, &priv->tx_idx_mgmt, 3, ring_control->tx_mgmt,
+ ARRAY_SIZE(ring_control->tx_mgmt),
+ priv->tx_buf_mgmt);
+
+ p54p_check_tx_ring(dev, &priv->tx_idx_data, 1, ring_control->tx_data,
+ ARRAY_SIZE(ring_control->tx_data),
+ priv->tx_buf_data);
+
+ p54p_check_rx_ring(dev, &priv->rx_idx_mgmt, 2, ring_control->rx_mgmt,
+ ARRAY_SIZE(ring_control->rx_mgmt), priv->rx_buf_mgmt);
+
+ p54p_check_rx_ring(dev, &priv->rx_idx_data, 0, ring_control->rx_data,
+ ARRAY_SIZE(ring_control->rx_data), priv->rx_buf_data);
+
+ wmb();
+ P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE));
+}
+
+static irqreturn_t p54p_interrupt(int irq, void *dev_id)
+{
+ struct ieee80211_hw *dev = dev_id;
+ struct p54p_priv *priv = dev->priv;
+ __le32 reg;
+
+ reg = P54P_READ(int_ident);
+ if (unlikely(reg == cpu_to_le32(0xFFFFFFFF))) {
+ goto out;
+ }
+ P54P_WRITE(int_ack, reg);
+
+ reg &= P54P_READ(int_enable);
+
+ if (reg & cpu_to_le32(ISL38XX_INT_IDENT_UPDATE))
+ tasklet_schedule(&priv->tasklet);
+ else if (reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT))
+ complete(&priv->boot_comp);
+
+out:
+ return reg ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static void p54p_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
+{
+ unsigned long flags;
+ struct p54p_priv *priv = dev->priv;
+ struct p54p_ring_control *ring_control = priv->ring_control;
+ struct p54p_desc *desc;
+ dma_addr_t mapping;
+ u32 idx, i;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ idx = le32_to_cpu(ring_control->host_idx[1]);
+ i = idx % ARRAY_SIZE(ring_control->tx_data);
+
+ mapping = pci_map_single(priv->pdev, skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(priv->pdev, mapping)) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ p54_free_skb(dev, skb);
+ dev_err(&priv->pdev->dev, "TX DMA mapping error\n");
+ return ;
+ }
+ priv->tx_buf_data[i] = skb;
+
+ desc = &ring_control->tx_data[i];
+ desc->host_addr = cpu_to_le32(mapping);
+ desc->device_addr = ((struct p54_hdr *)skb->data)->req_id;
+ desc->len = cpu_to_le16(skb->len);
+ desc->flags = 0;
+
+ wmb();
+ ring_control->host_idx[1] = cpu_to_le32(idx + 1);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE));
+ P54P_READ(dev_int);
+}
+
+static void p54p_stop(struct ieee80211_hw *dev)
+{
+ struct p54p_priv *priv = dev->priv;
+ struct p54p_ring_control *ring_control = priv->ring_control;
+ unsigned int i;
+ struct p54p_desc *desc;
+
+ P54P_WRITE(int_enable, cpu_to_le32(0));
+ P54P_READ(int_enable);
+ udelay(10);
+
+ free_irq(priv->pdev->irq, dev);
+
+ tasklet_kill(&priv->tasklet);
+
+ P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET));
+
+ for (i = 0; i < ARRAY_SIZE(priv->rx_buf_data); i++) {
+ desc = &ring_control->rx_data[i];
+ if (desc->host_addr)
+ pci_unmap_single(priv->pdev,
+ le32_to_cpu(desc->host_addr),
+ priv->common.rx_mtu + 32,
+ PCI_DMA_FROMDEVICE);
+ kfree_skb(priv->rx_buf_data[i]);
+ priv->rx_buf_data[i] = NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(priv->rx_buf_mgmt); i++) {
+ desc = &ring_control->rx_mgmt[i];
+ if (desc->host_addr)
+ pci_unmap_single(priv->pdev,
+ le32_to_cpu(desc->host_addr),
+ priv->common.rx_mtu + 32,
+ PCI_DMA_FROMDEVICE);
+ kfree_skb(priv->rx_buf_mgmt[i]);
+ priv->rx_buf_mgmt[i] = NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_buf_data); i++) {
+ desc = &ring_control->tx_data[i];
+ if (desc->host_addr)
+ pci_unmap_single(priv->pdev,
+ le32_to_cpu(desc->host_addr),
+ le16_to_cpu(desc->len),
+ PCI_DMA_TODEVICE);
+
+ p54_free_skb(dev, priv->tx_buf_data[i]);
+ priv->tx_buf_data[i] = NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_buf_mgmt); i++) {
+ desc = &ring_control->tx_mgmt[i];
+ if (desc->host_addr)
+ pci_unmap_single(priv->pdev,
+ le32_to_cpu(desc->host_addr),
+ le16_to_cpu(desc->len),
+ PCI_DMA_TODEVICE);
+
+ p54_free_skb(dev, priv->tx_buf_mgmt[i]);
+ priv->tx_buf_mgmt[i] = NULL;
+ }
+
+ memset(ring_control, 0, sizeof(*ring_control));
+}
+
+static int p54p_open(struct ieee80211_hw *dev)
+{
+ struct p54p_priv *priv = dev->priv;
+ int err;
+ long timeout;
+
+ init_completion(&priv->boot_comp);
+ err = request_irq(priv->pdev->irq, p54p_interrupt,
+ IRQF_SHARED, "p54pci", dev);
+ if (err) {
+ dev_err(&priv->pdev->dev, "failed to register IRQ handler\n");
+ return err;
+ }
+
+ memset(priv->ring_control, 0, sizeof(*priv->ring_control));
+ err = p54p_upload_firmware(dev);
+ if (err) {
+ free_irq(priv->pdev->irq, dev);
+ return err;
+ }
+ priv->rx_idx_data = priv->tx_idx_data = 0;
+ priv->rx_idx_mgmt = priv->tx_idx_mgmt = 0;
+
+ p54p_refill_rx_ring(dev, 0, priv->ring_control->rx_data,
+ ARRAY_SIZE(priv->ring_control->rx_data), priv->rx_buf_data, 0);
+
+ p54p_refill_rx_ring(dev, 2, priv->ring_control->rx_mgmt,
+ ARRAY_SIZE(priv->ring_control->rx_mgmt), priv->rx_buf_mgmt, 0);
+
+ P54P_WRITE(ring_control_base, cpu_to_le32(priv->ring_control_dma));
+ P54P_READ(ring_control_base);
+ wmb();
+ udelay(10);
+
+ P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_INIT));
+ P54P_READ(int_enable);
+ wmb();
+ udelay(10);
+
+ P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET));
+ P54P_READ(dev_int);
+
+ timeout = wait_for_completion_interruptible_timeout(
+ &priv->boot_comp, HZ);
+ if (timeout <= 0) {
+ wiphy_err(dev->wiphy, "Cannot boot firmware!\n");
+ p54p_stop(dev);
+ return timeout ? -ERESTARTSYS : -ETIMEDOUT;
+ }
+
+ P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_UPDATE));
+ P54P_READ(int_enable);
+ wmb();
+ udelay(10);
+
+ P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE));
+ P54P_READ(dev_int);
+ wmb();
+ udelay(10);
+
+ return 0;
+}
+
+static void p54p_firmware_step2(const struct firmware *fw,
+ void *context)
+{
+ struct p54p_priv *priv = context;
+ struct ieee80211_hw *dev = priv->common.hw;
+ struct pci_dev *pdev = priv->pdev;
+ int err;
+
+ if (!fw) {
+ dev_err(&pdev->dev, "Cannot find firmware (isl3886pci)\n");
+ err = -ENOENT;
+ goto out;
+ }
+
+ priv->firmware = fw;
+
+ err = p54p_open(dev);
+ if (err)
+ goto out;
+ err = p54_read_eeprom(dev);
+ p54p_stop(dev);
+ if (err)
+ goto out;
+
+ err = p54_register_common(dev, &pdev->dev);
+ if (err)
+ goto out;
+
+out:
+
+ complete(&priv->fw_loaded);
+
+ if (err) {
+ struct device *parent = pdev->dev.parent;
+
+ if (parent)
+ device_lock(parent);
+
+ /*
+ * This will indirectly result in a call to p54p_remove.
+ * Hence, we don't need to bother with freeing any
+ * allocated ressources at all.
+ */
+ device_release_driver(&pdev->dev);
+
+ if (parent)
+ device_unlock(parent);
+ }
+
+ pci_dev_put(pdev);
+}
+
+static int p54p_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct p54p_priv *priv;
+ struct ieee80211_hw *dev;
+ unsigned long mem_addr, mem_len;
+ int err;
+
+ pci_dev_get(pdev);
+ err = pci_enable_device(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "Cannot enable new PCI device\n");
+ return err;
+ }
+
+ mem_addr = pci_resource_start(pdev, 0);
+ mem_len = pci_resource_len(pdev, 0);
+ if (mem_len < sizeof(struct p54p_csr)) {
+ dev_err(&pdev->dev, "Too short PCI resources\n");
+ err = -ENODEV;
+ goto err_disable_dev;
+ }
+
+ err = pci_request_regions(pdev, "p54pci");
+ if (err) {
+ dev_err(&pdev->dev, "Cannot obtain PCI resources\n");
+ goto err_disable_dev;
+ }
+
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (!err)
+ err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (err) {
+ dev_err(&pdev->dev, "No suitable DMA available\n");
+ goto err_free_reg;
+ }
+
+ pci_set_master(pdev);
+ pci_try_set_mwi(pdev);
+
+ pci_write_config_byte(pdev, 0x40, 0);
+ pci_write_config_byte(pdev, 0x41, 0);
+
+ dev = p54_init_common(sizeof(*priv));
+ if (!dev) {
+ dev_err(&pdev->dev, "ieee80211 alloc failed\n");
+ err = -ENOMEM;
+ goto err_free_reg;
+ }
+
+ priv = dev->priv;
+ priv->pdev = pdev;
+
+ init_completion(&priv->fw_loaded);
+ SET_IEEE80211_DEV(dev, &pdev->dev);
+ pci_set_drvdata(pdev, dev);
+
+ priv->map = ioremap(mem_addr, mem_len);
+ if (!priv->map) {
+ dev_err(&pdev->dev, "Cannot map device memory\n");
+ err = -ENOMEM;
+ goto err_free_dev;
+ }
+
+ priv->ring_control = pci_alloc_consistent(pdev, sizeof(*priv->ring_control),
+ &priv->ring_control_dma);
+ if (!priv->ring_control) {
+ dev_err(&pdev->dev, "Cannot allocate rings\n");
+ err = -ENOMEM;
+ goto err_iounmap;
+ }
+ priv->common.open = p54p_open;
+ priv->common.stop = p54p_stop;
+ priv->common.tx = p54p_tx;
+
+ spin_lock_init(&priv->lock);
+ tasklet_init(&priv->tasklet, p54p_tasklet, (unsigned long)dev);
+
+ err = request_firmware_nowait(THIS_MODULE, 1, "isl3886pci",
+ &priv->pdev->dev, GFP_KERNEL,
+ priv, p54p_firmware_step2);
+ if (!err)
+ return 0;
+
+ pci_free_consistent(pdev, sizeof(*priv->ring_control),
+ priv->ring_control, priv->ring_control_dma);
+
+ err_iounmap:
+ iounmap(priv->map);
+
+ err_free_dev:
+ p54_free_common(dev);
+
+ err_free_reg:
+ pci_release_regions(pdev);
+ err_disable_dev:
+ pci_disable_device(pdev);
+ pci_dev_put(pdev);
+ return err;
+}
+
+static void p54p_remove(struct pci_dev *pdev)
+{
+ struct ieee80211_hw *dev = pci_get_drvdata(pdev);
+ struct p54p_priv *priv;
+
+ if (!dev)
+ return;
+
+ priv = dev->priv;
+ wait_for_completion(&priv->fw_loaded);
+ p54_unregister_common(dev);
+ release_firmware(priv->firmware);
+ pci_free_consistent(pdev, sizeof(*priv->ring_control),
+ priv->ring_control, priv->ring_control_dma);
+ iounmap(priv->map);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ p54_free_common(dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int p54p_suspend(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+
+ pci_save_state(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+ pci_disable_device(pdev);
+ return 0;
+}
+
+static int p54p_resume(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ int err;
+
+ err = pci_reenable_device(pdev);
+ if (err)
+ return err;
+ return pci_set_power_state(pdev, PCI_D0);
+}
+
+static SIMPLE_DEV_PM_OPS(p54pci_pm_ops, p54p_suspend, p54p_resume);
+
+#define P54P_PM_OPS (&p54pci_pm_ops)
+#else
+#define P54P_PM_OPS (NULL)
+#endif /* CONFIG_PM_SLEEP */
+
+static struct pci_driver p54p_driver = {
+ .name = "p54pci",
+ .id_table = p54p_table,
+ .probe = p54p_probe,
+ .remove = p54p_remove,
+ .driver.pm = P54P_PM_OPS,
+};
+
+module_pci_driver(p54p_driver);
--- /dev/null
+#ifndef P54PCI_H
+#define P54PCI_H
+#include <linux/interrupt.h>
+
+/*
+ * Defines for PCI based mac80211 Prism54 driver
+ *
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ *
+ * Based on the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* Device Interrupt register bits */
+#define ISL38XX_DEV_INT_RESET 0x0001
+#define ISL38XX_DEV_INT_UPDATE 0x0002
+#define ISL38XX_DEV_INT_WAKEUP 0x0008
+#define ISL38XX_DEV_INT_SLEEP 0x0010
+#define ISL38XX_DEV_INT_ABORT 0x0020
+/* these two only used in USB */
+#define ISL38XX_DEV_INT_DATA 0x0040
+#define ISL38XX_DEV_INT_MGMT 0x0080
+
+#define ISL38XX_DEV_INT_PCIUART_CTS 0x4000
+#define ISL38XX_DEV_INT_PCIUART_DR 0x8000
+
+/* Interrupt Identification/Acknowledge/Enable register bits */
+#define ISL38XX_INT_IDENT_UPDATE 0x0002
+#define ISL38XX_INT_IDENT_INIT 0x0004
+#define ISL38XX_INT_IDENT_WAKEUP 0x0008
+#define ISL38XX_INT_IDENT_SLEEP 0x0010
+#define ISL38XX_INT_IDENT_PCIUART_CTS 0x4000
+#define ISL38XX_INT_IDENT_PCIUART_DR 0x8000
+
+/* Control/Status register bits */
+#define ISL38XX_CTRL_STAT_SLEEPMODE 0x00000200
+#define ISL38XX_CTRL_STAT_CLKRUN 0x00800000
+#define ISL38XX_CTRL_STAT_RESET 0x10000000
+#define ISL38XX_CTRL_STAT_RAMBOOT 0x20000000
+#define ISL38XX_CTRL_STAT_STARTHALTED 0x40000000
+#define ISL38XX_CTRL_STAT_HOST_OVERRIDE 0x80000000
+
+struct p54p_csr {
+ __le32 dev_int;
+ u8 unused_1[12];
+ __le32 int_ident;
+ __le32 int_ack;
+ __le32 int_enable;
+ u8 unused_2[4];
+ union {
+ __le32 ring_control_base;
+ __le32 gen_purp_com[2];
+ };
+ u8 unused_3[8];
+ __le32 direct_mem_base;
+ u8 unused_4[44];
+ __le32 dma_addr;
+ __le32 dma_len;
+ __le32 dma_ctrl;
+ u8 unused_5[12];
+ __le32 ctrl_stat;
+ u8 unused_6[1924];
+ u8 cardbus_cis[0x800];
+ u8 direct_mem_win[0x1000];
+} __packed;
+
+/* usb backend only needs the register defines above */
+#ifndef P54USB_H
+struct p54p_desc {
+ __le32 host_addr;
+ __le32 device_addr;
+ __le16 len;
+ __le16 flags;
+} __packed;
+
+struct p54p_ring_control {
+ __le32 host_idx[4];
+ __le32 device_idx[4];
+ struct p54p_desc rx_data[8];
+ struct p54p_desc tx_data[32];
+ struct p54p_desc rx_mgmt[4];
+ struct p54p_desc tx_mgmt[4];
+} __packed;
+
+#define P54P_READ(r) (__force __le32)__raw_readl(&priv->map->r)
+#define P54P_WRITE(r, val) __raw_writel((__force u32)(__le32)(val), &priv->map->r)
+
+struct p54p_priv {
+ struct p54_common common;
+ struct pci_dev *pdev;
+ struct p54p_csr __iomem *map;
+ struct tasklet_struct tasklet;
+ const struct firmware *firmware;
+ spinlock_t lock;
+ struct p54p_ring_control *ring_control;
+ dma_addr_t ring_control_dma;
+ u32 rx_idx_data, tx_idx_data;
+ u32 rx_idx_mgmt, tx_idx_mgmt;
+ struct sk_buff *rx_buf_data[8];
+ struct sk_buff *rx_buf_mgmt[4];
+ struct sk_buff *tx_buf_data[32];
+ struct sk_buff *tx_buf_mgmt[4];
+ struct completion boot_comp;
+ struct completion fw_loaded;
+};
+
+#endif /* P54USB_H */
+#endif /* P54PCI_H */
--- /dev/null
+/*
+ * Copyright (C) 2008 Christian Lamparter <chunkeey@web.de>
+ * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This driver is a port from stlc45xx:
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/spi/spi.h>
+#include <linux/etherdevice.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#include "p54spi.h"
+#include "p54.h"
+
+#include "lmac.h"
+
+#ifdef CONFIG_P54_SPI_DEFAULT_EEPROM
+#include "p54spi_eeprom.h"
+#endif /* CONFIG_P54_SPI_DEFAULT_EEPROM */
+
+MODULE_FIRMWARE("3826.arm");
+
+/* gpios should be handled in board files and provided via platform data,
+ * but because it's currently impossible for p54spi to have a header file
+ * in include/linux, let's use module paramaters for now
+ */
+
+static int p54spi_gpio_power = 97;
+module_param(p54spi_gpio_power, int, 0444);
+MODULE_PARM_DESC(p54spi_gpio_power, "gpio number for power line");
+
+static int p54spi_gpio_irq = 87;
+module_param(p54spi_gpio_irq, int, 0444);
+MODULE_PARM_DESC(p54spi_gpio_irq, "gpio number for irq line");
+
+static void p54spi_spi_read(struct p54s_priv *priv, u8 address,
+ void *buf, size_t len)
+{
+ struct spi_transfer t[2];
+ struct spi_message m;
+ __le16 addr;
+
+ /* We first push the address */
+ addr = cpu_to_le16(address << 8 | SPI_ADRS_READ_BIT_15);
+
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+
+ t[0].tx_buf = &addr;
+ t[0].len = sizeof(addr);
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].rx_buf = buf;
+ t[1].len = len;
+ spi_message_add_tail(&t[1], &m);
+
+ spi_sync(priv->spi, &m);
+}
+
+
+static void p54spi_spi_write(struct p54s_priv *priv, u8 address,
+ const void *buf, size_t len)
+{
+ struct spi_transfer t[3];
+ struct spi_message m;
+ __le16 addr;
+
+ /* We first push the address */
+ addr = cpu_to_le16(address << 8);
+
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+
+ t[0].tx_buf = &addr;
+ t[0].len = sizeof(addr);
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].tx_buf = buf;
+ t[1].len = len & ~1;
+ spi_message_add_tail(&t[1], &m);
+
+ if (len % 2) {
+ __le16 last_word;
+ last_word = cpu_to_le16(((u8 *)buf)[len - 1]);
+
+ t[2].tx_buf = &last_word;
+ t[2].len = sizeof(last_word);
+ spi_message_add_tail(&t[2], &m);
+ }
+
+ spi_sync(priv->spi, &m);
+}
+
+static u32 p54spi_read32(struct p54s_priv *priv, u8 addr)
+{
+ __le32 val;
+
+ p54spi_spi_read(priv, addr, &val, sizeof(val));
+
+ return le32_to_cpu(val);
+}
+
+static inline void p54spi_write16(struct p54s_priv *priv, u8 addr, __le16 val)
+{
+ p54spi_spi_write(priv, addr, &val, sizeof(val));
+}
+
+static inline void p54spi_write32(struct p54s_priv *priv, u8 addr, __le32 val)
+{
+ p54spi_spi_write(priv, addr, &val, sizeof(val));
+}
+
+static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, u32 bits)
+{
+ int i;
+
+ for (i = 0; i < 2000; i++) {
+ u32 buffer = p54spi_read32(priv, reg);
+ if ((buffer & bits) == bits)
+ return 1;
+ }
+ return 0;
+}
+
+static int p54spi_spi_write_dma(struct p54s_priv *priv, __le32 base,
+ const void *buf, size_t len)
+{
+ if (!p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL, HOST_ALLOWED)) {
+ dev_err(&priv->spi->dev, "spi_write_dma not allowed "
+ "to DMA write.\n");
+ return -EAGAIN;
+ }
+
+ p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL,
+ cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE));
+
+ p54spi_write16(priv, SPI_ADRS_DMA_WRITE_LEN, cpu_to_le16(len));
+ p54spi_write32(priv, SPI_ADRS_DMA_WRITE_BASE, base);
+ p54spi_spi_write(priv, SPI_ADRS_DMA_DATA, buf, len);
+ return 0;
+}
+
+static int p54spi_request_firmware(struct ieee80211_hw *dev)
+{
+ struct p54s_priv *priv = dev->priv;
+ int ret;
+
+ /* FIXME: should driver use it's own struct device? */
+ ret = request_firmware(&priv->firmware, "3826.arm", &priv->spi->dev);
+
+ if (ret < 0) {
+ dev_err(&priv->spi->dev, "request_firmware() failed: %d", ret);
+ return ret;
+ }
+
+ ret = p54_parse_firmware(dev, priv->firmware);
+ if (ret) {
+ release_firmware(priv->firmware);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int p54spi_request_eeprom(struct ieee80211_hw *dev)
+{
+ struct p54s_priv *priv = dev->priv;
+ const struct firmware *eeprom;
+ int ret;
+
+ /* allow users to customize their eeprom.
+ */
+
+ ret = request_firmware_direct(&eeprom, "3826.eeprom", &priv->spi->dev);
+ if (ret < 0) {
+#ifdef CONFIG_P54_SPI_DEFAULT_EEPROM
+ dev_info(&priv->spi->dev, "loading default eeprom...\n");
+ ret = p54_parse_eeprom(dev, (void *) p54spi_eeprom,
+ sizeof(p54spi_eeprom));
+#else
+ dev_err(&priv->spi->dev, "Failed to request user eeprom\n");
+#endif /* CONFIG_P54_SPI_DEFAULT_EEPROM */
+ } else {
+ dev_info(&priv->spi->dev, "loading user eeprom...\n");
+ ret = p54_parse_eeprom(dev, (void *) eeprom->data,
+ (int)eeprom->size);
+ release_firmware(eeprom);
+ }
+ return ret;
+}
+
+static int p54spi_upload_firmware(struct ieee80211_hw *dev)
+{
+ struct p54s_priv *priv = dev->priv;
+ unsigned long fw_len, _fw_len;
+ unsigned int offset = 0;
+ int err = 0;
+ u8 *fw;
+
+ fw_len = priv->firmware->size;
+ fw = kmemdup(priv->firmware->data, fw_len, GFP_KERNEL);
+ if (!fw)
+ return -ENOMEM;
+
+ /* stop the device */
+ p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16(
+ SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET |
+ SPI_CTRL_STAT_START_HALTED));
+
+ msleep(TARGET_BOOT_SLEEP);
+
+ p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16(
+ SPI_CTRL_STAT_HOST_OVERRIDE |
+ SPI_CTRL_STAT_START_HALTED));
+
+ msleep(TARGET_BOOT_SLEEP);
+
+ while (fw_len > 0) {
+ _fw_len = min_t(long, fw_len, SPI_MAX_PACKET_SIZE);
+
+ err = p54spi_spi_write_dma(priv, cpu_to_le32(
+ ISL38XX_DEV_FIRMWARE_ADDR + offset),
+ (fw + offset), _fw_len);
+ if (err < 0)
+ goto out;
+
+ fw_len -= _fw_len;
+ offset += _fw_len;
+ }
+
+ BUG_ON(fw_len != 0);
+
+ /* enable host interrupts */
+ p54spi_write32(priv, SPI_ADRS_HOST_INT_EN,
+ cpu_to_le32(SPI_HOST_INTS_DEFAULT));
+
+ /* boot the device */
+ p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16(
+ SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET |
+ SPI_CTRL_STAT_RAM_BOOT));
+
+ msleep(TARGET_BOOT_SLEEP);
+
+ p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16(
+ SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_RAM_BOOT));
+ msleep(TARGET_BOOT_SLEEP);
+
+out:
+ kfree(fw);
+ return err;
+}
+
+static void p54spi_power_off(struct p54s_priv *priv)
+{
+ disable_irq(gpio_to_irq(p54spi_gpio_irq));
+ gpio_set_value(p54spi_gpio_power, 0);
+}
+
+static void p54spi_power_on(struct p54s_priv *priv)
+{
+ gpio_set_value(p54spi_gpio_power, 1);
+ enable_irq(gpio_to_irq(p54spi_gpio_irq));
+
+ /* need to wait a while before device can be accessed, the length
+ * is just a guess
+ */
+ msleep(10);
+}
+
+static inline void p54spi_int_ack(struct p54s_priv *priv, u32 val)
+{
+ p54spi_write32(priv, SPI_ADRS_HOST_INT_ACK, cpu_to_le32(val));
+}
+
+static int p54spi_wakeup(struct p54s_priv *priv)
+{
+ /* wake the chip */
+ p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS,
+ cpu_to_le32(SPI_TARGET_INT_WAKEUP));
+
+ /* And wait for the READY interrupt */
+ if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS,
+ SPI_HOST_INT_READY)) {
+ dev_err(&priv->spi->dev, "INT_READY timeout\n");
+ return -EBUSY;
+ }
+
+ p54spi_int_ack(priv, SPI_HOST_INT_READY);
+ return 0;
+}
+
+static inline void p54spi_sleep(struct p54s_priv *priv)
+{
+ p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS,
+ cpu_to_le32(SPI_TARGET_INT_SLEEP));
+}
+
+static void p54spi_int_ready(struct p54s_priv *priv)
+{
+ p54spi_write32(priv, SPI_ADRS_HOST_INT_EN, cpu_to_le32(
+ SPI_HOST_INT_UPDATE | SPI_HOST_INT_SW_UPDATE));
+
+ switch (priv->fw_state) {
+ case FW_STATE_BOOTING:
+ priv->fw_state = FW_STATE_READY;
+ complete(&priv->fw_comp);
+ break;
+ case FW_STATE_RESETTING:
+ priv->fw_state = FW_STATE_READY;
+ /* TODO: reinitialize state */
+ break;
+ default:
+ break;
+ }
+}
+
+static int p54spi_rx(struct p54s_priv *priv)
+{
+ struct sk_buff *skb;
+ u16 len;
+ u16 rx_head[2];
+#define READAHEAD_SZ (sizeof(rx_head)-sizeof(u16))
+
+ if (p54spi_wakeup(priv) < 0)
+ return -EBUSY;
+
+ /* Read data size and first data word in one SPI transaction
+ * This is workaround for firmware/DMA bug,
+ * when first data word gets lost under high load.
+ */
+ p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, rx_head, sizeof(rx_head));
+ len = rx_head[0];
+
+ if (len == 0) {
+ p54spi_sleep(priv);
+ dev_err(&priv->spi->dev, "rx request of zero bytes\n");
+ return 0;
+ }
+
+ /* Firmware may insert up to 4 padding bytes after the lmac header,
+ * but it does not amend the size of SPI data transfer.
+ * Such packets has correct data size in header, thus referencing
+ * past the end of allocated skb. Reserve extra 4 bytes for this case
+ */
+ skb = dev_alloc_skb(len + 4);
+ if (!skb) {
+ p54spi_sleep(priv);
+ dev_err(&priv->spi->dev, "could not alloc skb");
+ return -ENOMEM;
+ }
+
+ if (len <= READAHEAD_SZ) {
+ memcpy(skb_put(skb, len), rx_head + 1, len);
+ } else {
+ memcpy(skb_put(skb, READAHEAD_SZ), rx_head + 1, READAHEAD_SZ);
+ p54spi_spi_read(priv, SPI_ADRS_DMA_DATA,
+ skb_put(skb, len - READAHEAD_SZ),
+ len - READAHEAD_SZ);
+ }
+ p54spi_sleep(priv);
+ /* Put additional bytes to compensate for the possible
+ * alignment-caused truncation
+ */
+ skb_put(skb, 4);
+
+ if (p54_rx(priv->hw, skb) == 0)
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+
+static irqreturn_t p54spi_interrupt(int irq, void *config)
+{
+ struct spi_device *spi = config;
+ struct p54s_priv *priv = spi_get_drvdata(spi);
+
+ ieee80211_queue_work(priv->hw, &priv->work);
+
+ return IRQ_HANDLED;
+}
+
+static int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb)
+{
+ struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
+ int ret = 0;
+
+ if (p54spi_wakeup(priv) < 0)
+ return -EBUSY;
+
+ ret = p54spi_spi_write_dma(priv, hdr->req_id, skb->data, skb->len);
+ if (ret < 0)
+ goto out;
+
+ if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS,
+ SPI_HOST_INT_WR_READY)) {
+ dev_err(&priv->spi->dev, "WR_READY timeout\n");
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ p54spi_int_ack(priv, SPI_HOST_INT_WR_READY);
+
+ if (FREE_AFTER_TX(skb))
+ p54_free_skb(priv->hw, skb);
+out:
+ p54spi_sleep(priv);
+ return ret;
+}
+
+static int p54spi_wq_tx(struct p54s_priv *priv)
+{
+ struct p54s_tx_info *entry;
+ struct sk_buff *skb;
+ struct ieee80211_tx_info *info;
+ struct p54_tx_info *minfo;
+ struct p54s_tx_info *dinfo;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&priv->tx_lock, flags);
+
+ while (!list_empty(&priv->tx_pending)) {
+ entry = list_entry(priv->tx_pending.next,
+ struct p54s_tx_info, tx_list);
+
+ list_del_init(&entry->tx_list);
+
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+ dinfo = container_of((void *) entry, struct p54s_tx_info,
+ tx_list);
+ minfo = container_of((void *) dinfo, struct p54_tx_info,
+ data);
+ info = container_of((void *) minfo, struct ieee80211_tx_info,
+ rate_driver_data);
+ skb = container_of((void *) info, struct sk_buff, cb);
+
+ ret = p54spi_tx_frame(priv, skb);
+
+ if (ret < 0) {
+ p54_free_skb(priv->hw, skb);
+ return ret;
+ }
+
+ spin_lock_irqsave(&priv->tx_lock, flags);
+ }
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+ return ret;
+}
+
+static void p54spi_op_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
+{
+ struct p54s_priv *priv = dev->priv;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct p54_tx_info *mi = (struct p54_tx_info *) info->rate_driver_data;
+ struct p54s_tx_info *di = (struct p54s_tx_info *) mi->data;
+ unsigned long flags;
+
+ BUILD_BUG_ON(sizeof(*di) > sizeof((mi->data)));
+
+ spin_lock_irqsave(&priv->tx_lock, flags);
+ list_add_tail(&di->tx_list, &priv->tx_pending);
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+ ieee80211_queue_work(priv->hw, &priv->work);
+}
+
+static void p54spi_work(struct work_struct *work)
+{
+ struct p54s_priv *priv = container_of(work, struct p54s_priv, work);
+ u32 ints;
+ int ret;
+
+ mutex_lock(&priv->mutex);
+
+ if (priv->fw_state == FW_STATE_OFF)
+ goto out;
+
+ ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS);
+
+ if (ints & SPI_HOST_INT_READY) {
+ p54spi_int_ready(priv);
+ p54spi_int_ack(priv, SPI_HOST_INT_READY);
+ }
+
+ if (priv->fw_state != FW_STATE_READY)
+ goto out;
+
+ if (ints & SPI_HOST_INT_UPDATE) {
+ p54spi_int_ack(priv, SPI_HOST_INT_UPDATE);
+ ret = p54spi_rx(priv);
+ if (ret < 0)
+ goto out;
+ }
+ if (ints & SPI_HOST_INT_SW_UPDATE) {
+ p54spi_int_ack(priv, SPI_HOST_INT_SW_UPDATE);
+ ret = p54spi_rx(priv);
+ if (ret < 0)
+ goto out;
+ }
+
+ ret = p54spi_wq_tx(priv);
+out:
+ mutex_unlock(&priv->mutex);
+}
+
+static int p54spi_op_start(struct ieee80211_hw *dev)
+{
+ struct p54s_priv *priv = dev->priv;
+ unsigned long timeout;
+ int ret = 0;
+
+ if (mutex_lock_interruptible(&priv->mutex)) {
+ ret = -EINTR;
+ goto out;
+ }
+
+ priv->fw_state = FW_STATE_BOOTING;
+
+ p54spi_power_on(priv);
+
+ ret = p54spi_upload_firmware(dev);
+ if (ret < 0) {
+ p54spi_power_off(priv);
+ goto out_unlock;
+ }
+
+ mutex_unlock(&priv->mutex);
+
+ timeout = msecs_to_jiffies(2000);
+ timeout = wait_for_completion_interruptible_timeout(&priv->fw_comp,
+ timeout);
+ if (!timeout) {
+ dev_err(&priv->spi->dev, "firmware boot failed");
+ p54spi_power_off(priv);
+ ret = -1;
+ goto out;
+ }
+
+ if (mutex_lock_interruptible(&priv->mutex)) {
+ ret = -EINTR;
+ p54spi_power_off(priv);
+ goto out;
+ }
+
+ WARN_ON(priv->fw_state != FW_STATE_READY);
+
+out_unlock:
+ mutex_unlock(&priv->mutex);
+
+out:
+ return ret;
+}
+
+static void p54spi_op_stop(struct ieee80211_hw *dev)
+{
+ struct p54s_priv *priv = dev->priv;
+ unsigned long flags;
+
+ mutex_lock(&priv->mutex);
+ WARN_ON(priv->fw_state != FW_STATE_READY);
+
+ p54spi_power_off(priv);
+ spin_lock_irqsave(&priv->tx_lock, flags);
+ INIT_LIST_HEAD(&priv->tx_pending);
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+ priv->fw_state = FW_STATE_OFF;
+ mutex_unlock(&priv->mutex);
+
+ cancel_work_sync(&priv->work);
+}
+
+static int p54spi_probe(struct spi_device *spi)
+{
+ struct p54s_priv *priv = NULL;
+ struct ieee80211_hw *hw;
+ int ret = -EINVAL;
+
+ hw = p54_init_common(sizeof(*priv));
+ if (!hw) {
+ dev_err(&spi->dev, "could not alloc ieee80211_hw");
+ return -ENOMEM;
+ }
+
+ priv = hw->priv;
+ priv->hw = hw;
+ spi_set_drvdata(spi, priv);
+ priv->spi = spi;
+
+ spi->bits_per_word = 16;
+ spi->max_speed_hz = 24000000;
+
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(&priv->spi->dev, "spi_setup failed");
+ goto err_free;
+ }
+
+ ret = gpio_request(p54spi_gpio_power, "p54spi power");
+ if (ret < 0) {
+ dev_err(&priv->spi->dev, "power GPIO request failed: %d", ret);
+ goto err_free;
+ }
+
+ ret = gpio_request(p54spi_gpio_irq, "p54spi irq");
+ if (ret < 0) {
+ dev_err(&priv->spi->dev, "irq GPIO request failed: %d", ret);
+ goto err_free_gpio_power;
+ }
+
+ gpio_direction_output(p54spi_gpio_power, 0);
+ gpio_direction_input(p54spi_gpio_irq);
+
+ ret = request_irq(gpio_to_irq(p54spi_gpio_irq),
+ p54spi_interrupt, 0, "p54spi",
+ priv->spi);
+ if (ret < 0) {
+ dev_err(&priv->spi->dev, "request_irq() failed");
+ goto err_free_gpio_irq;
+ }
+
+ irq_set_irq_type(gpio_to_irq(p54spi_gpio_irq), IRQ_TYPE_EDGE_RISING);
+
+ disable_irq(gpio_to_irq(p54spi_gpio_irq));
+
+ INIT_WORK(&priv->work, p54spi_work);
+ init_completion(&priv->fw_comp);
+ INIT_LIST_HEAD(&priv->tx_pending);
+ mutex_init(&priv->mutex);
+ spin_lock_init(&priv->tx_lock);
+ SET_IEEE80211_DEV(hw, &spi->dev);
+ priv->common.open = p54spi_op_start;
+ priv->common.stop = p54spi_op_stop;
+ priv->common.tx = p54spi_op_tx;
+
+ ret = p54spi_request_firmware(hw);
+ if (ret < 0)
+ goto err_free_common;
+
+ ret = p54spi_request_eeprom(hw);
+ if (ret)
+ goto err_free_common;
+
+ ret = p54_register_common(hw, &priv->spi->dev);
+ if (ret)
+ goto err_free_common;
+
+ return 0;
+
+err_free_common:
+ free_irq(gpio_to_irq(p54spi_gpio_irq), spi);
+err_free_gpio_irq:
+ gpio_free(p54spi_gpio_irq);
+err_free_gpio_power:
+ gpio_free(p54spi_gpio_power);
+err_free:
+ p54_free_common(priv->hw);
+ return ret;
+}
+
+static int p54spi_remove(struct spi_device *spi)
+{
+ struct p54s_priv *priv = spi_get_drvdata(spi);
+
+ p54_unregister_common(priv->hw);
+
+ free_irq(gpio_to_irq(p54spi_gpio_irq), spi);
+
+ gpio_free(p54spi_gpio_power);
+ gpio_free(p54spi_gpio_irq);
+ release_firmware(priv->firmware);
+
+ mutex_destroy(&priv->mutex);
+
+ p54_free_common(priv->hw);
+
+ return 0;
+}
+
+
+static struct spi_driver p54spi_driver = {
+ .driver = {
+ .name = "p54spi",
+ },
+
+ .probe = p54spi_probe,
+ .remove = p54spi_remove,
+};
+
+module_spi_driver(p54spi_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>");
+MODULE_ALIAS("spi:cx3110x");
+MODULE_ALIAS("spi:p54spi");
+MODULE_ALIAS("spi:stlc45xx");
--- /dev/null
+/*
+ * Copyright (C) 2008 Christian Lamparter <chunkeey@web.de>
+ *
+ * This driver is a port from stlc45xx:
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef P54SPI_H
+#define P54SPI_H
+
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <net/mac80211.h>
+
+#include "p54.h"
+
+/* Bit 15 is read/write bit; ON = READ, OFF = WRITE */
+#define SPI_ADRS_READ_BIT_15 0x8000
+
+#define SPI_ADRS_ARM_INTERRUPTS 0x00
+#define SPI_ADRS_ARM_INT_EN 0x04
+
+#define SPI_ADRS_HOST_INTERRUPTS 0x08
+#define SPI_ADRS_HOST_INT_EN 0x0c
+#define SPI_ADRS_HOST_INT_ACK 0x10
+
+#define SPI_ADRS_GEN_PURP_1 0x14
+#define SPI_ADRS_GEN_PURP_2 0x18
+
+#define SPI_ADRS_DEV_CTRL_STAT 0x26 /* high word */
+
+#define SPI_ADRS_DMA_DATA 0x28
+
+#define SPI_ADRS_DMA_WRITE_CTRL 0x2c
+#define SPI_ADRS_DMA_WRITE_LEN 0x2e
+#define SPI_ADRS_DMA_WRITE_BASE 0x30
+
+#define SPI_ADRS_DMA_READ_CTRL 0x34
+#define SPI_ADRS_DMA_READ_LEN 0x36
+#define SPI_ADRS_DMA_READ_BASE 0x38
+
+#define SPI_CTRL_STAT_HOST_OVERRIDE 0x8000
+#define SPI_CTRL_STAT_START_HALTED 0x4000
+#define SPI_CTRL_STAT_RAM_BOOT 0x2000
+#define SPI_CTRL_STAT_HOST_RESET 0x1000
+#define SPI_CTRL_STAT_HOST_CPU_EN 0x0800
+
+#define SPI_DMA_WRITE_CTRL_ENABLE 0x0001
+#define SPI_DMA_READ_CTRL_ENABLE 0x0001
+#define HOST_ALLOWED (1 << 7)
+
+#define SPI_TIMEOUT 100 /* msec */
+
+#define SPI_MAX_TX_PACKETS 32
+
+#define SPI_MAX_PACKET_SIZE 32767
+
+#define SPI_TARGET_INT_WAKEUP 0x00000001
+#define SPI_TARGET_INT_SLEEP 0x00000002
+#define SPI_TARGET_INT_RDDONE 0x00000004
+
+#define SPI_TARGET_INT_CTS 0x00004000
+#define SPI_TARGET_INT_DR 0x00008000
+
+#define SPI_HOST_INT_READY 0x00000001
+#define SPI_HOST_INT_WR_READY 0x00000002
+#define SPI_HOST_INT_SW_UPDATE 0x00000004
+#define SPI_HOST_INT_UPDATE 0x10000000
+
+/* clear to send */
+#define SPI_HOST_INT_CR 0x00004000
+
+/* data ready */
+#define SPI_HOST_INT_DR 0x00008000
+
+#define SPI_HOST_INTS_DEFAULT \
+ (SPI_HOST_INT_READY | SPI_HOST_INT_UPDATE | SPI_HOST_INT_SW_UPDATE)
+
+#define TARGET_BOOT_SLEEP 50
+
+struct p54s_dma_regs {
+ __le16 cmd;
+ __le16 len;
+ __le32 addr;
+} __packed;
+
+struct p54s_tx_info {
+ struct list_head tx_list;
+};
+
+struct p54s_priv {
+ /* p54_common has to be the first entry */
+ struct p54_common common;
+ struct ieee80211_hw *hw;
+ struct spi_device *spi;
+
+ struct work_struct work;
+
+ struct mutex mutex;
+ struct completion fw_comp;
+
+ spinlock_t tx_lock;
+
+ /* protected by tx_lock */
+ struct list_head tx_pending;
+
+ enum fw_state fw_state;
+ const struct firmware *firmware;
+};
+
+#endif /* P54SPI_H */
--- /dev/null
+/*
+ * Copyright (C) 2003 Conexant Americas Inc. All Rights Reserved.
+ * Copyright (C) 2004, 2005, 2006 Nokia Corporation
+ * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2008 Christian Lamparter <chunkeey@web.de>
+ *
+ * based on:
+ * - cx3110x's pda.h from Nokia
+ * - cx3110-transfer.log by Johannes Berg
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef P54SPI_EEPROM_H
+#define P54SPI_EEPROM_H
+
+static unsigned char p54spi_eeprom[] = {
+
+/* struct eeprom_pda_wrap */
+0x47, 0x4d, 0x55, 0xaa, /* magic */
+0x00, 0x00, /* pad */
+0x00, 0x00, /* eeprom_pda_data_wrap length */
+0x00, 0x00, 0x00, 0x00, /* arm opcode */
+
+/* bogus MAC address */
+0x04, 0x00, 0x01, 0x01, /* PDR_MAC_ADDRESS */
+ 0x00, 0x02, 0xee, 0xc0, 0xff, 0xee,
+
+/* struct bootrec_exp_if */
+0x06, 0x00, 0x01, 0x10, /* PDR_INTERFACE_LIST */
+ 0x00, 0x00, /* role */
+ 0x0f, 0x00, /* if_id */
+ 0x85, 0x00, /* variant = Longbow RF, 2GHz */
+ 0x01, 0x00, /* btm_compat */
+ 0x1f, 0x00, /* top_compat */
+
+0x03, 0x00, 0x02, 0x10, /* PDR_HARDWARE_PLATFORM_COMPONENT_ID */
+ 0x03, 0x20, 0x00, 0x43,
+
+/* struct pda_country[6] */
+0x0d, 0x00, 0x07, 0x10, /* PDR_COUNTRY_LIST */
+ 0x10, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00,
+ 0x31, 0x00, 0x00, 0x00,
+ 0x32, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00,
+
+/* struct pda_country */
+0x03, 0x00, 0x08, 0x10, /* PDR_DEFAULT_COUNTRY */
+ 0x30, 0x00, 0x00, 0x00, /* ETSI */
+
+0x03, 0x00, 0x00, 0x11, /* PDR_ANTENNA_GAIN */
+ 0x08, 0x08, 0x08, 0x08,
+
+0x0a, 0x00, 0xff, 0xca, /* PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2 */
+ 0x01, 0x00, 0x0a, 0x00,
+ 0x00, 0x00, 0x0a, 0x00,
+ 0x85, 0x09, 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00,
+
+/* struct pda_custom_wrapper */
+0x10, 0x06, 0x5d, 0xb0, /* PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM */
+ 0x0d, 0x00, 0xee, 0x00, /* 13 entries, 238 bytes per entry */
+ 0x00, 0x00, 0x16, 0x0c, /* no offset, 3094 total len */
+ /* 2412 MHz */
+ 0x6c, 0x09,
+ 0x10, 0x01, 0x9a, 0x84,
+ 0xaa, 0x8a, 0xaa, 0x8a, 0xaa, 0x8a, 0xaa, 0x8a,
+ 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6,
+ 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6,
+ 0xf0, 0x00, 0x94, 0x6c,
+ 0x99, 0x82, 0x99, 0x82, 0x99, 0x82, 0x99, 0x82,
+ 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae,
+ 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae,
+ 0xd0, 0x00, 0xaa, 0x5a,
+ 0x88, 0x7a, 0x88, 0x7a, 0x88, 0x7a, 0x88, 0x7a,
+ 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6,
+ 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6,
+ 0xa0, 0x00, 0xf3, 0x47,
+ 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e,
+ 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a,
+ 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a,
+ 0x50, 0x00, 0x59, 0x36,
+ 0x43, 0x5a, 0x43, 0x5a, 0x43, 0x5a, 0x43, 0x5a,
+ 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85,
+ 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85,
+ 0x00, 0x00, 0xe4, 0x2d,
+ 0x18, 0x46, 0x18, 0x46, 0x18, 0x46, 0x18, 0x46,
+ 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71,
+ 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
+
+ /* 2417 MHz */
+ 0x71, 0x09,
+ 0x10, 0x01, 0xb9, 0x83,
+ 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a,
+ 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6,
+ 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6,
+ 0xf0, 0x00, 0x2e, 0x6c,
+ 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, 0x68, 0x82,
+ 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad,
+ 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad,
+ 0xd0, 0x00, 0x8d, 0x5a,
+ 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a,
+ 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5,
+ 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5,
+ 0xa0, 0x00, 0x0a, 0x48,
+ 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e,
+ 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99,
+ 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99,
+ 0x50, 0x00, 0x7c, 0x36,
+ 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59,
+ 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85,
+ 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85,
+ 0x00, 0x00, 0xf5, 0x2d,
+ 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45,
+ 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71,
+ 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
+
+ /* 2422 MHz */
+ 0x76, 0x09,
+ 0x10, 0x01, 0xb9, 0x83,
+ 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a,
+ 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6,
+ 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6,
+ 0xf0, 0x00, 0x2e, 0x6c,
+ 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, 0x68, 0x82,
+ 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad,
+ 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad,
+ 0xd0, 0x00, 0x8d, 0x5a,
+ 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a,
+ 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5,
+ 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5,
+ 0xa0, 0x00, 0x0a, 0x48,
+ 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e,
+ 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99,
+ 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99,
+ 0x50, 0x00, 0x7c, 0x36,
+ 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59,
+ 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85,
+ 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85,
+ 0x00, 0x00, 0xf5, 0x2d,
+ 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45,
+ 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71,
+ 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
+
+ /* 2427 MHz */
+ 0x7b, 0x09,
+ 0x10, 0x01, 0x48, 0x83,
+ 0x67, 0x8a, 0x67, 0x8a, 0x67, 0x8a, 0x67, 0x8a,
+ 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5,
+ 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5,
+ 0xf0, 0x00, 0xfb, 0x6b,
+ 0x50, 0x82, 0x50, 0x82, 0x50, 0x82, 0x50, 0x82,
+ 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad,
+ 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad,
+ 0xd0, 0x00, 0x7e, 0x5a,
+ 0x38, 0x7a, 0x38, 0x7a, 0x38, 0x7a, 0x38, 0x7a,
+ 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5,
+ 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5,
+ 0xa0, 0x00, 0x15, 0x48,
+ 0x14, 0x6e, 0x14, 0x6e, 0x14, 0x6e, 0x14, 0x6e,
+ 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99,
+ 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99,
+ 0x50, 0x00, 0x8e, 0x36,
+ 0xd9, 0x59, 0xd9, 0x59, 0xd9, 0x59, 0xd9, 0x59,
+ 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85,
+ 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85,
+ 0x00, 0x00, 0xfe, 0x2d,
+ 0x9d, 0x45, 0x9d, 0x45, 0x9d, 0x45, 0x9d, 0x45,
+ 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71,
+ 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
+
+ /* 2432 MHz */
+ 0x80, 0x09,
+ 0x10, 0x01, 0xd7, 0x82,
+ 0x51, 0x8a, 0x51, 0x8a, 0x51, 0x8a, 0x51, 0x8a,
+ 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5,
+ 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5,
+ 0xf0, 0x00, 0xc8, 0x6b,
+ 0x37, 0x82, 0x37, 0x82, 0x37, 0x82, 0x37, 0x82,
+ 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad,
+ 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad,
+ 0xd0, 0x00, 0x6f, 0x5a,
+ 0x1d, 0x7a, 0x1d, 0x7a, 0x1d, 0x7a, 0x1d, 0x7a,
+ 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5,
+ 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5,
+ 0xa0, 0x00, 0x20, 0x48,
+ 0xf6, 0x6d, 0xf6, 0x6d, 0xf6, 0x6d, 0xf6, 0x6d,
+ 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, 0x88, 0x99,
+ 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, 0x88, 0x99,
+ 0x50, 0x00, 0x9f, 0x36,
+ 0xb5, 0x59, 0xb5, 0x59, 0xb5, 0x59, 0xb5, 0x59,
+ 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, 0x47, 0x85,
+ 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, 0x47, 0x85,
+ 0x00, 0x00, 0x06, 0x2e,
+ 0x74, 0x45, 0x74, 0x45, 0x74, 0x45, 0x74, 0x45,
+ 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, 0x06, 0x71,
+ 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, 0x06, 0x71,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
+
+ /* 2437 MHz */
+ 0x85, 0x09,
+ 0x10, 0x01, 0x67, 0x82,
+ 0x3a, 0x8a, 0x3a, 0x8a, 0x3a, 0x8a, 0x3a, 0x8a,
+ 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5,
+ 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5,
+ 0xf0, 0x00, 0x95, 0x6b,
+ 0x1f, 0x82, 0x1f, 0x82, 0x1f, 0x82, 0x1f, 0x82,
+ 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad,
+ 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad,
+ 0xd0, 0x00, 0x61, 0x5a,
+ 0x02, 0x7a, 0x02, 0x7a, 0x02, 0x7a, 0x02, 0x7a,
+ 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5,
+ 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5,
+ 0xa0, 0x00, 0x2c, 0x48,
+ 0xd8, 0x6d, 0xd8, 0x6d, 0xd8, 0x6d, 0xd8, 0x6d,
+ 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99,
+ 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99,
+ 0x50, 0x00, 0xb1, 0x36,
+ 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, 0x92, 0x59,
+ 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85,
+ 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85,
+ 0x00, 0x00, 0x0f, 0x2e,
+ 0x4b, 0x45, 0x4b, 0x45, 0x4b, 0x45, 0x4b, 0x45,
+ 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70,
+ 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
+
+ /* 2442 MHz */
+ 0x8a, 0x09,
+ 0x10, 0x01, 0xf6, 0x81,
+ 0x24, 0x8a, 0x24, 0x8a, 0x24, 0x8a, 0x24, 0x8a,
+ 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5,
+ 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5,
+ 0xf0, 0x00, 0x62, 0x6b,
+ 0x06, 0x82, 0x06, 0x82, 0x06, 0x82, 0x06, 0x82,
+ 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, 0x98, 0xad,
+ 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, 0x98, 0xad,
+ 0xd0, 0x00, 0x52, 0x5a,
+ 0xe7, 0x79, 0xe7, 0x79, 0xe7, 0x79, 0xe7, 0x79,
+ 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5,
+ 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5,
+ 0xa0, 0x00, 0x37, 0x48,
+ 0xba, 0x6d, 0xba, 0x6d, 0xba, 0x6d, 0xba, 0x6d,
+ 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99,
+ 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99,
+ 0x50, 0x00, 0xc2, 0x36,
+ 0x6e, 0x59, 0x6e, 0x59, 0x6e, 0x59, 0x6e, 0x59,
+ 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, 0x00, 0x85,
+ 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, 0x00, 0x85,
+ 0x00, 0x00, 0x17, 0x2e,
+ 0x22, 0x45, 0x22, 0x45, 0x22, 0x45, 0x22, 0x45,
+ 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70,
+ 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
+
+ /* 2447 MHz */
+ 0x8f, 0x09,
+ 0x10, 0x01, 0x75, 0x83,
+ 0x61, 0x8a, 0x61, 0x8a, 0x61, 0x8a, 0x61, 0x8a,
+ 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5,
+ 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5,
+ 0xf0, 0x00, 0x4b, 0x6c,
+ 0x3f, 0x82, 0x3f, 0x82, 0x3f, 0x82, 0x3f, 0x82,
+ 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad,
+ 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad,
+ 0xd0, 0x00, 0xda, 0x5a,
+ 0x1c, 0x7a, 0x1c, 0x7a, 0x1c, 0x7a, 0x1c, 0x7a,
+ 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5,
+ 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5,
+ 0xa0, 0x00, 0x6d, 0x48,
+ 0xe9, 0x6d, 0xe9, 0x6d, 0xe9, 0x6d, 0xe9, 0x6d,
+ 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99,
+ 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99,
+ 0x50, 0x00, 0xc6, 0x36,
+ 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, 0x92, 0x59,
+ 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85,
+ 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85,
+ 0x00, 0x00, 0x15, 0x2e,
+ 0x3c, 0x45, 0x3c, 0x45, 0x3c, 0x45, 0x3c, 0x45,
+ 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, 0xce, 0x70,
+ 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, 0xce, 0x70,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
+
+ /* 2452 MHz */
+ 0x94, 0x09,
+ 0x10, 0x01, 0xf4, 0x84,
+ 0x9e, 0x8a, 0x9e, 0x8a, 0x9e, 0x8a, 0x9e, 0x8a,
+ 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6,
+ 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6,
+ 0xf0, 0x00, 0x34, 0x6d,
+ 0x77, 0x82, 0x77, 0x82, 0x77, 0x82, 0x77, 0x82,
+ 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, 0x09, 0xae,
+ 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, 0x09, 0xae,
+ 0xd0, 0x00, 0x62, 0x5b,
+ 0x50, 0x7a, 0x50, 0x7a, 0x50, 0x7a, 0x50, 0x7a,
+ 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5,
+ 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5,
+ 0xa0, 0x00, 0xa2, 0x48,
+ 0x17, 0x6e, 0x17, 0x6e, 0x17, 0x6e, 0x17, 0x6e,
+ 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99,
+ 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99,
+ 0x50, 0x00, 0xc9, 0x36,
+ 0xb7, 0x59, 0xb7, 0x59, 0xb7, 0x59, 0xb7, 0x59,
+ 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, 0x49, 0x85,
+ 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, 0x49, 0x85,
+ 0x00, 0x00, 0x12, 0x2e,
+ 0x57, 0x45, 0x57, 0x45, 0x57, 0x45, 0x57, 0x45,
+ 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70,
+ 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
+
+ /* 2452 MHz */
+ 0x99, 0x09,
+ 0x10, 0x01, 0x74, 0x86,
+ 0xdb, 0x8a, 0xdb, 0x8a, 0xdb, 0x8a, 0xdb, 0x8a,
+ 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6,
+ 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6,
+ 0xf0, 0x00, 0x1e, 0x6e,
+ 0xb0, 0x82, 0xb0, 0x82, 0xb0, 0x82, 0xb0, 0x82,
+ 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, 0x42, 0xae,
+ 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, 0x42, 0xae,
+ 0xd0, 0x00, 0xeb, 0x5b,
+ 0x85, 0x7a, 0x85, 0x7a, 0x85, 0x7a, 0x85, 0x7a,
+ 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6,
+ 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6,
+ 0xa0, 0x00, 0xd8, 0x48,
+ 0x46, 0x6e, 0x46, 0x6e, 0x46, 0x6e, 0x46, 0x6e,
+ 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99,
+ 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99,
+ 0x50, 0x00, 0xcd, 0x36,
+ 0xdb, 0x59, 0xdb, 0x59, 0xdb, 0x59, 0xdb, 0x59,
+ 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85,
+ 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85,
+ 0x00, 0x00, 0x10, 0x2e,
+ 0x71, 0x45, 0x71, 0x45, 0x71, 0x45, 0x71, 0x45,
+ 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, 0x03, 0x71,
+ 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, 0x03, 0x71,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
+
+ /* 2557 MHz */
+ 0x9e, 0x09,
+ 0x10, 0x01, 0xf3, 0x87,
+ 0x17, 0x8b, 0x17, 0x8b, 0x17, 0x8b, 0x17, 0x8b,
+ 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6,
+ 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6,
+ 0xf0, 0x00, 0x07, 0x6f,
+ 0xe9, 0x82, 0xe9, 0x82, 0xe9, 0x82, 0xe9, 0x82,
+ 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae,
+ 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae,
+ 0xd0, 0x00, 0x73, 0x5c,
+ 0xba, 0x7a, 0xba, 0x7a, 0xba, 0x7a, 0xba, 0x7a,
+ 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6,
+ 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6,
+ 0xa0, 0x00, 0x0d, 0x49,
+ 0x74, 0x6e, 0x74, 0x6e, 0x74, 0x6e, 0x74, 0x6e,
+ 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a,
+ 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a,
+ 0x50, 0x00, 0xd1, 0x36,
+ 0xff, 0x59, 0xff, 0x59, 0xff, 0x59, 0xff, 0x59,
+ 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, 0x91, 0x85,
+ 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, 0x91, 0x85,
+ 0x00, 0x00, 0x0e, 0x2e,
+ 0x8b, 0x45, 0x8b, 0x45, 0x8b, 0x45, 0x8b, 0x45,
+ 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71,
+ 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
+
+ /* 2562 MHz */
+ 0xa3, 0x09,
+ 0x10, 0x01, 0x72, 0x89,
+ 0x54, 0x8b, 0x54, 0x8b, 0x54, 0x8b, 0x54, 0x8b,
+ 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6,
+ 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6,
+ 0xf0, 0x00, 0xf0, 0x6f,
+ 0x21, 0x83, 0x21, 0x83, 0x21, 0x83, 0x21, 0x83,
+ 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae,
+ 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae,
+ 0xd0, 0x00, 0xfb, 0x5c,
+ 0xee, 0x7a, 0xee, 0x7a, 0xee, 0x7a, 0xee, 0x7a,
+ 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6,
+ 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6,
+ 0xa0, 0x00, 0x43, 0x49,
+ 0xa3, 0x6e, 0xa3, 0x6e, 0xa3, 0x6e, 0xa3, 0x6e,
+ 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a,
+ 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a,
+ 0x50, 0x00, 0xd4, 0x36,
+ 0x24, 0x5a, 0x24, 0x5a, 0x24, 0x5a, 0x24, 0x5a,
+ 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85,
+ 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85,
+ 0x00, 0x00, 0x0b, 0x2e,
+ 0xa6, 0x45, 0xa6, 0x45, 0xa6, 0x45, 0xa6, 0x45,
+ 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, 0x38, 0x71,
+ 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, 0x38, 0x71,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
+
+ /* 2572 MHz */
+ 0xa8, 0x09,
+ 0x10, 0x01, 0xf1, 0x8a,
+ 0x91, 0x8b, 0x91, 0x8b, 0x91, 0x8b, 0x91, 0x8b,
+ 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7,
+ 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7,
+ 0xf0, 0x00, 0xd9, 0x70,
+ 0x5a, 0x83, 0x5a, 0x83, 0x5a, 0x83, 0x5a, 0x83,
+ 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, 0xec, 0xae,
+ 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, 0xec, 0xae,
+ 0xd0, 0x00, 0x83, 0x5d,
+ 0x23, 0x7b, 0x23, 0x7b, 0x23, 0x7b, 0x23, 0x7b,
+ 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6,
+ 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6,
+ 0xa0, 0x00, 0x78, 0x49,
+ 0xd1, 0x6e, 0xd1, 0x6e, 0xd1, 0x6e, 0xd1, 0x6e,
+ 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a,
+ 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a,
+ 0x50, 0x00, 0xd8, 0x36,
+ 0x48, 0x5a, 0x48, 0x5a, 0x48, 0x5a, 0x48, 0x5a,
+ 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, 0xda, 0x85,
+ 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, 0xda, 0x85,
+ 0x00, 0x00, 0x09, 0x2e,
+ 0xc0, 0x45, 0xc0, 0x45, 0xc0, 0x45, 0xc0, 0x45,
+ 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, 0x52, 0x71,
+ 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, 0x52, 0x71,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
+
+/*
+ * Not really sure if this is actually the power_limit database,
+ * it looks a bit "related" to PDR_PRISM_ZIF_TX_IQ_CALIBRATION
+ */
+/* struct pda_custom_wrapper */
+0xae, 0x00, 0xef, 0xbe, /* PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM */
+ 0x0d, 0x00, 0x1a, 0x00, /* 13 entries, 26 bytes per entry */
+ 0x00, 0x00, 0x52, 0x01, /* no offset, 338 bytes total */
+
+ /* 2412 MHz */
+ 0x6c, 0x09,
+ 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
+ 0xe0, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xe0, 0x00,
+ 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+
+ /* 2417 MHz */
+ 0x71, 0x09,
+ 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
+ 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
+ 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+
+ /* 2422 MHz */
+ 0x76, 0x09,
+ 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
+ 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
+ 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+
+ /* 2427 MHz */
+ 0x7b, 0x09,
+ 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
+ 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
+ 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+
+ /* 2432 MHz */
+ 0x80, 0x09,
+ 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
+ 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
+ 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+
+ /* 2437 MHz */
+ 0x85, 0x09,
+ 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
+ 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
+ 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+
+ /* 2442 MHz */
+ 0x8a, 0x09,
+ 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
+ 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
+ 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+
+ /* 2447 MHz */
+ 0x8f, 0x09,
+ 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
+ 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
+ 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+
+ /* 2452 MHz */
+ 0x94, 0x09,
+ 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
+ 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
+ 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+
+ /* 2457 MHz */
+ 0x99, 0x09,
+ 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
+ 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
+ 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+
+ /* 2462 MHz */
+ 0x9e, 0x09,
+ 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
+ 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
+ 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+
+ /* 2467 MHz */
+ 0xa3, 0x09,
+ 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
+ 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
+ 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+
+ /* 2472 MHz */
+ 0xa8, 0x09,
+ 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
+ 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
+ 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+
+/* struct pda_iq_autocal_entry[13] */
+0x42, 0x00, 0x06, 0x19, /* PDR_PRISM_ZIF_TX_IQ_CALIBRATION */
+ /* 2412 MHz */
+ 0x6c, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00,
+ /* 2417 MHz */
+ 0x71, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00,
+ /* 2422 MHz */
+ 0x76, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00,
+ /* 2427 MHz */
+ 0x7b, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00,
+ /* 2432 MHz */
+ 0x80, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00,
+ /* 2437 MHz */
+ 0x85, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00,
+ /* 2442 MHz */
+ 0x8a, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00,
+ /* 2447 MHz */
+ 0x8f, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00,
+ /* 2452 MHz */
+ 0x94, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00,
+ /* 2457 MHz */
+ 0x99, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01,
+ /* 2462 MHz */
+ 0x9e, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01,
+ /* 2467 MHz */
+ 0xa3, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01,
+ /* 2472 MHz */
+ 0xa8, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01,
+
+0x02, 0x00, 0x00, 0x00, /* PDR_END */
+ 0xb6, 0x04,
+};
+
+#endif /* P54SPI_EEPROM_H */
+
--- /dev/null
+
+/*
+ * Linux device driver for USB based Prism54
+ *
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ *
+ * Based on the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/usb.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+
+#include "p54.h"
+#include "lmac.h"
+#include "p54usb.h"
+
+MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
+MODULE_DESCRIPTION("Prism54 USB wireless driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("prism54usb");
+MODULE_FIRMWARE("isl3886usb");
+MODULE_FIRMWARE("isl3887usb");
+
+/*
+ * Note:
+ *
+ * Always update our wiki's device list (located at:
+ * http://wireless.kernel.org/en/users/Drivers/p54/devices ),
+ * whenever you add a new device.
+ */
+
+static struct usb_device_id p54u_table[] = {
+ /* Version 1 devices (pci chip + net2280) */
+ {USB_DEVICE(0x0411, 0x0050)}, /* Buffalo WLI2-USB2-G54 */
+ {USB_DEVICE(0x045e, 0x00c2)}, /* Microsoft MN-710 */
+ {USB_DEVICE(0x0506, 0x0a11)}, /* 3COM 3CRWE254G72 */
+ {USB_DEVICE(0x0675, 0x0530)}, /* DrayTek Vigor 530 */
+ {USB_DEVICE(0x06b9, 0x0120)}, /* Thomson SpeedTouch 120g */
+ {USB_DEVICE(0x0707, 0xee06)}, /* SMC 2862W-G */
+ {USB_DEVICE(0x07aa, 0x001c)}, /* Corega CG-WLUSB2GT */
+ {USB_DEVICE(0x083a, 0x4501)}, /* Accton 802.11g WN4501 USB */
+ {USB_DEVICE(0x083a, 0x4502)}, /* Siemens Gigaset USB Adapter */
+ {USB_DEVICE(0x083a, 0x5501)}, /* Phillips CPWUA054 */
+ {USB_DEVICE(0x0846, 0x4200)}, /* Netgear WG121 */
+ {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */
+ {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */
+ {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */
+ {USB_DEVICE(0x0bf8, 0x1007)}, /* Fujitsu E-5400 USB */
+ {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */
+ {USB_DEVICE(0x0db0, 0x6826)}, /* MSI UB54G (MS-6826) */
+ {USB_DEVICE(0x107b, 0x55f2)}, /* Gateway WGU-210 (Gemtek) */
+ {USB_DEVICE(0x124a, 0x4023)}, /* Shuttle PN15, Airvast WM168g, IOGear GWU513 */
+ {USB_DEVICE(0x1435, 0x0210)}, /* Inventel UR054G */
+ {USB_DEVICE(0x15a9, 0x0002)}, /* Gemtek WUBI-100GW 802.11g */
+ {USB_DEVICE(0x1630, 0x0005)}, /* 2Wire 802.11g USB (v1) / Z-Com */
+ {USB_DEVICE(0x182d, 0x096b)}, /* Sitecom WL-107 */
+ {USB_DEVICE(0x1915, 0x2234)}, /* Linksys WUSB54G OEM */
+ {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */
+ {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */
+ {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */
+ {USB_DEVICE(0x2001, 0x3762)}, /* Conceptronic C54U */
+ {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */
+ {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */
+
+ /* Version 2 devices (3887) */
+ {USB_DEVICE(0x0471, 0x1230)}, /* Philips CPWUA054/00 */
+ {USB_DEVICE(0x050d, 0x7050)}, /* Belkin F5D7050 ver 1000 */
+ {USB_DEVICE(0x0572, 0x2000)}, /* Cohiba Proto board */
+ {USB_DEVICE(0x0572, 0x2002)}, /* Cohiba Proto board */
+ {USB_DEVICE(0x06a9, 0x000e)}, /* Westell 802.11g USB (A90-211WG-01) */
+ {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */
+ {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */
+ {USB_DEVICE(0x07aa, 0x0020)}, /* Corega WLUSB2GTST USB */
+ {USB_DEVICE(0x0803, 0x4310)}, /* Zoom 4410a */
+ {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */
+ {USB_DEVICE(0x083a, 0x4531)}, /* T-Com Sinus 154 data II */
+ {USB_DEVICE(0x083a, 0xc501)}, /* Zoom Wireless-G 4410 */
+ {USB_DEVICE(0x083a, 0xf503)}, /* Accton FD7050E ver 1010ec */
+ {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */
+ {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */
+ {USB_DEVICE(0x0915, 0x2002)}, /* Cohiba Proto board */
+ {USB_DEVICE(0x0baf, 0x0118)}, /* U.S. Robotics U5 802.11g Adapter*/
+ {USB_DEVICE(0x0bf8, 0x1009)}, /* FUJITSU E-5400 USB D1700*/
+ /* {USB_DEVICE(0x0cde, 0x0006)}, * Medion MD40900 already listed above,
+ * just noting it here for clarity */
+ {USB_DEVICE(0x0cde, 0x0008)}, /* Sagem XG703A */
+ {USB_DEVICE(0x0cde, 0x0015)}, /* Zcomax XG-705A */
+ {USB_DEVICE(0x0d8e, 0x3762)}, /* DLink DWL-G120 Cohiba */
+ {USB_DEVICE(0x124a, 0x4025)}, /* IOGear GWU513 (GW3887IK chip) */
+ {USB_DEVICE(0x1260, 0xee22)}, /* SMC 2862W-G version 2 */
+ {USB_DEVICE(0x13b1, 0x000a)}, /* Linksys WUSB54G ver 2 */
+ {USB_DEVICE(0x13B1, 0x000C)}, /* Linksys WUSB54AG */
+ {USB_DEVICE(0x1413, 0x5400)}, /* Telsey 802.11g USB2.0 Adapter */
+ {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */
+ /* {USB_DEVICE(0x15a9, 0x0002)}, * Also SparkLAN WL-682 with 3887 */
+ {USB_DEVICE(0x1668, 0x1050)}, /* Actiontec 802UIG-1 */
+ {USB_DEVICE(0x1740, 0x1000)}, /* Senao NUB-350 */
+ {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */
+ {USB_DEVICE(0x2001, 0x3705)}, /* D-Link DWL-G120 rev C1 */
+ {USB_DEVICE(0x413c, 0x5513)}, /* Dell WLA3310 USB Wireless Adapter */
+ {USB_DEVICE(0x413c, 0x8102)}, /* Spinnaker DUT */
+ {USB_DEVICE(0x413c, 0x8104)}, /* Cohiba Proto board */
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, p54u_table);
+
+static const struct {
+ u32 intf;
+ enum p54u_hw_type type;
+ const char *fw;
+ char hw[20];
+} p54u_fwlist[__NUM_P54U_HWTYPES] = {
+ {
+ .type = P54U_NET2280,
+ .intf = FW_LM86,
+ .fw = "isl3886usb",
+ .hw = "ISL3886 + net2280",
+ },
+ {
+ .type = P54U_3887,
+ .intf = FW_LM87,
+ .fw = "isl3887usb",
+ .hw = "ISL3887",
+ },
+};
+
+static void p54u_rx_cb(struct urb *urb)
+{
+ struct sk_buff *skb = (struct sk_buff *) urb->context;
+ struct p54u_rx_info *info = (struct p54u_rx_info *)skb->cb;
+ struct ieee80211_hw *dev = info->dev;
+ struct p54u_priv *priv = dev->priv;
+
+ skb_unlink(skb, &priv->rx_queue);
+
+ if (unlikely(urb->status)) {
+ dev_kfree_skb_irq(skb);
+ return;
+ }
+
+ skb_put(skb, urb->actual_length);
+
+ if (priv->hw_type == P54U_NET2280)
+ skb_pull(skb, priv->common.tx_hdr_len);
+ if (priv->common.fw_interface == FW_LM87) {
+ skb_pull(skb, 4);
+ skb_put(skb, 4);
+ }
+
+ if (p54_rx(dev, skb)) {
+ skb = dev_alloc_skb(priv->common.rx_mtu + 32);
+ if (unlikely(!skb)) {
+ /* TODO check rx queue length and refill *somewhere* */
+ return;
+ }
+
+ info = (struct p54u_rx_info *) skb->cb;
+ info->urb = urb;
+ info->dev = dev;
+ urb->transfer_buffer = skb_tail_pointer(skb);
+ urb->context = skb;
+ } else {
+ if (priv->hw_type == P54U_NET2280)
+ skb_push(skb, priv->common.tx_hdr_len);
+ if (priv->common.fw_interface == FW_LM87) {
+ skb_push(skb, 4);
+ skb_put(skb, 4);
+ }
+ skb_reset_tail_pointer(skb);
+ skb_trim(skb, 0);
+ urb->transfer_buffer = skb_tail_pointer(skb);
+ }
+ skb_queue_tail(&priv->rx_queue, skb);
+ usb_anchor_urb(urb, &priv->submitted);
+ if (usb_submit_urb(urb, GFP_ATOMIC)) {
+ skb_unlink(skb, &priv->rx_queue);
+ usb_unanchor_urb(urb);
+ dev_kfree_skb_irq(skb);
+ }
+}
+
+static void p54u_tx_cb(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct ieee80211_hw *dev =
+ usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
+
+ p54_free_skb(dev, skb);
+}
+
+static void p54u_tx_dummy_cb(struct urb *urb) { }
+
+static void p54u_free_urbs(struct ieee80211_hw *dev)
+{
+ struct p54u_priv *priv = dev->priv;
+ usb_kill_anchored_urbs(&priv->submitted);
+}
+
+static void p54u_stop(struct ieee80211_hw *dev)
+{
+ /*
+ * TODO: figure out how to reliably stop the 3887 and net2280 so
+ * the hardware is still usable next time we want to start it.
+ * until then, we just stop listening to the hardware..
+ */
+ p54u_free_urbs(dev);
+}
+
+static int p54u_init_urbs(struct ieee80211_hw *dev)
+{
+ struct p54u_priv *priv = dev->priv;
+ struct urb *entry = NULL;
+ struct sk_buff *skb;
+ struct p54u_rx_info *info;
+ int ret = 0;
+
+ while (skb_queue_len(&priv->rx_queue) < 32) {
+ skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL);
+ if (!skb) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ entry = usb_alloc_urb(0, GFP_KERNEL);
+ if (!entry) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ usb_fill_bulk_urb(entry, priv->udev,
+ usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA),
+ skb_tail_pointer(skb),
+ priv->common.rx_mtu + 32, p54u_rx_cb, skb);
+ info = (struct p54u_rx_info *) skb->cb;
+ info->urb = entry;
+ info->dev = dev;
+ skb_queue_tail(&priv->rx_queue, skb);
+
+ usb_anchor_urb(entry, &priv->submitted);
+ ret = usb_submit_urb(entry, GFP_KERNEL);
+ if (ret) {
+ skb_unlink(skb, &priv->rx_queue);
+ usb_unanchor_urb(entry);
+ goto err;
+ }
+ usb_free_urb(entry);
+ entry = NULL;
+ }
+
+ return 0;
+
+ err:
+ usb_free_urb(entry);
+ kfree_skb(skb);
+ p54u_free_urbs(dev);
+ return ret;
+}
+
+static int p54u_open(struct ieee80211_hw *dev)
+{
+ /*
+ * TODO: Because we don't know how to reliably stop the 3887 and
+ * the isl3886+net2280, other than brutally cut off all
+ * communications. We have to reinitialize the urbs on every start.
+ */
+ return p54u_init_urbs(dev);
+}
+
+static __le32 p54u_lm87_chksum(const __le32 *data, size_t length)
+{
+ u32 chk = 0;
+
+ length >>= 2;
+ while (length--) {
+ chk ^= le32_to_cpu(*data++);
+ chk = (chk >> 5) ^ (chk << 3);
+ }
+
+ return cpu_to_le32(chk);
+}
+
+static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb)
+{
+ struct p54u_priv *priv = dev->priv;
+ struct urb *data_urb;
+ struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr);
+
+ data_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!data_urb) {
+ p54_free_skb(dev, skb);
+ return;
+ }
+
+ hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len);
+ hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id;
+
+ usb_fill_bulk_urb(data_urb, priv->udev,
+ usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
+ hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ?
+ p54u_tx_cb : p54u_tx_dummy_cb, skb);
+ data_urb->transfer_flags |= URB_ZERO_PACKET;
+
+ usb_anchor_urb(data_urb, &priv->submitted);
+ if (usb_submit_urb(data_urb, GFP_ATOMIC)) {
+ usb_unanchor_urb(data_urb);
+ p54_free_skb(dev, skb);
+ }
+ usb_free_urb(data_urb);
+}
+
+static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb)
+{
+ struct p54u_priv *priv = dev->priv;
+ struct urb *int_urb = NULL, *data_urb = NULL;
+ struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr);
+ struct net2280_reg_write *reg = NULL;
+ int err = -ENOMEM;
+
+ reg = kmalloc(sizeof(*reg), GFP_ATOMIC);
+ if (!reg)
+ goto out;
+
+ int_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!int_urb)
+ goto out;
+
+ data_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!data_urb)
+ goto out;
+
+ reg->port = cpu_to_le16(NET2280_DEV_U32);
+ reg->addr = cpu_to_le32(P54U_DEV_BASE);
+ reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA);
+
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->len = cpu_to_le16(skb->len);
+ hdr->device_addr = ((struct p54_hdr *) skb->data)->req_id;
+
+ usb_fill_bulk_urb(int_urb, priv->udev,
+ usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg),
+ p54u_tx_dummy_cb, dev);
+
+ /*
+ * URB_FREE_BUFFER triggers a code path in the USB subsystem that will
+ * free what is inside the transfer_buffer after the last reference to
+ * the int_urb is dropped.
+ */
+ int_urb->transfer_flags |= URB_FREE_BUFFER | URB_ZERO_PACKET;
+ reg = NULL;
+
+ usb_fill_bulk_urb(data_urb, priv->udev,
+ usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
+ hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ?
+ p54u_tx_cb : p54u_tx_dummy_cb, skb);
+ data_urb->transfer_flags |= URB_ZERO_PACKET;
+
+ usb_anchor_urb(int_urb, &priv->submitted);
+ err = usb_submit_urb(int_urb, GFP_ATOMIC);
+ if (err) {
+ usb_unanchor_urb(int_urb);
+ goto out;
+ }
+
+ usb_anchor_urb(data_urb, &priv->submitted);
+ err = usb_submit_urb(data_urb, GFP_ATOMIC);
+ if (err) {
+ usb_unanchor_urb(data_urb);
+ goto out;
+ }
+out:
+ usb_free_urb(int_urb);
+ usb_free_urb(data_urb);
+
+ if (err) {
+ kfree(reg);
+ p54_free_skb(dev, skb);
+ }
+}
+
+static int p54u_write(struct p54u_priv *priv,
+ struct net2280_reg_write *buf,
+ enum net2280_op_type type,
+ __le32 addr, __le32 val)
+{
+ unsigned int ep;
+ int alen;
+
+ if (type & 0x0800)
+ ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV);
+ else
+ ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_BRG);
+
+ buf->port = cpu_to_le16(type);
+ buf->addr = addr;
+ buf->val = val;
+
+ return usb_bulk_msg(priv->udev, ep, buf, sizeof(*buf), &alen, 1000);
+}
+
+static int p54u_read(struct p54u_priv *priv, void *buf,
+ enum net2280_op_type type,
+ __le32 addr, __le32 *val)
+{
+ struct net2280_reg_read *read = buf;
+ __le32 *reg = buf;
+ unsigned int ep;
+ int alen, err;
+
+ if (type & 0x0800)
+ ep = P54U_PIPE_DEV;
+ else
+ ep = P54U_PIPE_BRG;
+
+ read->port = cpu_to_le16(type);
+ read->addr = addr;
+
+ err = usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep),
+ read, sizeof(*read), &alen, 1000);
+ if (err)
+ return err;
+
+ err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, ep),
+ reg, sizeof(*reg), &alen, 1000);
+ if (err)
+ return err;
+
+ *val = *reg;
+ return 0;
+}
+
+static int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep,
+ void *data, size_t len)
+{
+ int alen;
+ return usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep),
+ data, len, &alen, 2000);
+}
+
+static int p54u_device_reset(struct ieee80211_hw *dev)
+{
+ struct p54u_priv *priv = dev->priv;
+ int ret, lock = (priv->intf->condition != USB_INTERFACE_BINDING);
+
+ if (lock) {
+ ret = usb_lock_device_for_reset(priv->udev, priv->intf);
+ if (ret < 0) {
+ dev_err(&priv->udev->dev, "(p54usb) unable to lock "
+ "device for reset (%d)!\n", ret);
+ return ret;
+ }
+ }
+
+ ret = usb_reset_device(priv->udev);
+ if (lock)
+ usb_unlock_device(priv->udev);
+
+ if (ret)
+ dev_err(&priv->udev->dev, "(p54usb) unable to reset "
+ "device (%d)!\n", ret);
+
+ return ret;
+}
+
+static const char p54u_romboot_3887[] = "~~~~";
+static int p54u_firmware_reset_3887(struct ieee80211_hw *dev)
+{
+ struct p54u_priv *priv = dev->priv;
+ u8 *buf;
+ int ret;
+
+ buf = kmemdup(p54u_romboot_3887, 4, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ ret = p54u_bulk_msg(priv, P54U_PIPE_DATA,
+ buf, 4);
+ kfree(buf);
+ if (ret)
+ dev_err(&priv->udev->dev, "(p54usb) unable to jump to "
+ "boot ROM (%d)!\n", ret);
+
+ return ret;
+}
+
+static const char p54u_firmware_upload_3887[] = "<\r";
+static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
+{
+ struct p54u_priv *priv = dev->priv;
+ int err, alen;
+ u8 carry = 0;
+ u8 *buf, *tmp;
+ const u8 *data;
+ unsigned int left, remains, block_size;
+ struct x2_header *hdr;
+ unsigned long timeout;
+
+ err = p54u_firmware_reset_3887(dev);
+ if (err)
+ return err;
+
+ tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ left = block_size = min_t(size_t, P54U_FW_BLOCK, priv->fw->size);
+ strcpy(buf, p54u_firmware_upload_3887);
+ left -= strlen(p54u_firmware_upload_3887);
+ tmp += strlen(p54u_firmware_upload_3887);
+
+ data = priv->fw->data;
+ remains = priv->fw->size;
+
+ hdr = (struct x2_header *)(buf + strlen(p54u_firmware_upload_3887));
+ memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE);
+ hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR);
+ hdr->fw_length = cpu_to_le32(priv->fw->size);
+ hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr,
+ sizeof(u32)*2));
+ left -= sizeof(*hdr);
+ tmp += sizeof(*hdr);
+
+ while (remains) {
+ while (left--) {
+ if (carry) {
+ *tmp++ = carry;
+ carry = 0;
+ remains--;
+ continue;
+ }
+ switch (*data) {
+ case '~':
+ *tmp++ = '}';
+ carry = '^';
+ break;
+ case '}':
+ *tmp++ = '}';
+ carry = ']';
+ break;
+ default:
+ *tmp++ = *data;
+ remains--;
+ break;
+ }
+ data++;
+ }
+
+ err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size);
+ if (err) {
+ dev_err(&priv->udev->dev, "(p54usb) firmware "
+ "upload failed!\n");
+ goto err_upload_failed;
+ }
+
+ tmp = buf;
+ left = block_size = min((unsigned int)P54U_FW_BLOCK, remains);
+ }
+
+ *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, priv->fw->data,
+ priv->fw->size));
+ err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32));
+ if (err) {
+ dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
+ goto err_upload_failed;
+ }
+ timeout = jiffies + msecs_to_jiffies(1000);
+ while (!(err = usb_bulk_msg(priv->udev,
+ usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) {
+ if (alen > 2 && !memcmp(buf, "OK", 2))
+ break;
+
+ if (alen > 5 && !memcmp(buf, "ERROR", 5)) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(&priv->udev->dev, "(p54usb) firmware boot "
+ "timed out!\n");
+ err = -ETIMEDOUT;
+ break;
+ }
+ }
+ if (err) {
+ dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
+ goto err_upload_failed;
+ }
+
+ buf[0] = 'g';
+ buf[1] = '\r';
+ err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2);
+ if (err) {
+ dev_err(&priv->udev->dev, "(p54usb) firmware boot failed!\n");
+ goto err_upload_failed;
+ }
+
+ timeout = jiffies + msecs_to_jiffies(1000);
+ while (!(err = usb_bulk_msg(priv->udev,
+ usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) {
+ if (alen > 0 && buf[0] == 'g')
+ break;
+
+ if (time_after(jiffies, timeout)) {
+ err = -ETIMEDOUT;
+ break;
+ }
+ }
+ if (err)
+ goto err_upload_failed;
+
+err_upload_failed:
+ kfree(buf);
+ return err;
+}
+
+static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
+{
+ struct p54u_priv *priv = dev->priv;
+ const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE;
+ int err, alen;
+ void *buf;
+ __le32 reg;
+ unsigned int remains, offset;
+ const u8 *data;
+
+ buf = kmalloc(512, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+#define P54U_WRITE(type, addr, data) \
+ do {\
+ err = p54u_write(priv, buf, type,\
+ cpu_to_le32((u32)(unsigned long)addr), data);\
+ if (err) \
+ goto fail;\
+ } while (0)
+
+#define P54U_READ(type, addr) \
+ do {\
+ err = p54u_read(priv, buf, type,\
+ cpu_to_le32((u32)(unsigned long)addr), ®);\
+ if (err)\
+ goto fail;\
+ } while (0)
+
+ /* power down net2280 bridge */
+ P54U_READ(NET2280_BRG_U32, NET2280_GPIOCTL);
+ reg |= cpu_to_le32(P54U_BRG_POWER_DOWN);
+ reg &= cpu_to_le32(~P54U_BRG_POWER_UP);
+ P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg);
+
+ mdelay(100);
+
+ /* power up bridge */
+ reg |= cpu_to_le32(P54U_BRG_POWER_UP);
+ reg &= cpu_to_le32(~P54U_BRG_POWER_DOWN);
+ P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg);
+
+ mdelay(100);
+
+ P54U_WRITE(NET2280_BRG_U32, NET2280_DEVINIT,
+ cpu_to_le32(NET2280_CLK_30Mhz |
+ NET2280_PCI_ENABLE |
+ NET2280_PCI_SOFT_RESET));
+
+ mdelay(20);
+
+ P54U_WRITE(NET2280_BRG_CFG_U16, PCI_COMMAND,
+ cpu_to_le32(PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER));
+
+ P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_0,
+ cpu_to_le32(NET2280_BASE));
+
+ P54U_READ(NET2280_BRG_CFG_U16, PCI_STATUS);
+ reg |= cpu_to_le32(PCI_STATUS_REC_MASTER_ABORT);
+ P54U_WRITE(NET2280_BRG_CFG_U16, PCI_STATUS, reg);
+
+ // TODO: we really need this?
+ P54U_READ(NET2280_BRG_U32, NET2280_RELNUM);
+
+ P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_RSP,
+ cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE));
+ P54U_WRITE(NET2280_BRG_U32, NET2280_EPC_RSP,
+ cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE));
+
+ P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_2,
+ cpu_to_le32(NET2280_BASE2));
+
+ /* finally done setting up the bridge */
+
+ P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | PCI_COMMAND,
+ cpu_to_le32(PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER));
+
+ P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | 0x40 /* TRDY timeout */, 0);
+ P54U_WRITE(NET2280_DEV_CFG_U32, 0x10000 | PCI_BASE_ADDRESS_0,
+ cpu_to_le32(P54U_DEV_BASE));
+
+ P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0);
+ P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
+ cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
+
+ /* do romboot */
+ P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, 0);
+
+ P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat);
+ reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
+ reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT);
+ reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);
+ P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
+
+ mdelay(20);
+
+ reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
+ P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
+
+ mdelay(20);
+
+ reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
+ P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
+
+ mdelay(100);
+
+ P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
+ P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
+
+ /* finally, we can upload firmware now! */
+ remains = priv->fw->size;
+ data = priv->fw->data;
+ offset = ISL38XX_DEV_FIRMWARE_ADDR;
+
+ while (remains) {
+ unsigned int block_len = min(remains, (unsigned int)512);
+ memcpy(buf, data, block_len);
+
+ err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len);
+ if (err) {
+ dev_err(&priv->udev->dev, "(p54usb) firmware block "
+ "upload failed\n");
+ goto fail;
+ }
+
+ P54U_WRITE(NET2280_DEV_U32, &devreg->direct_mem_base,
+ cpu_to_le32(0xc0000f00));
+
+ P54U_WRITE(NET2280_DEV_U32,
+ 0x0020 | (unsigned long)&devreg->direct_mem_win, 0);
+ P54U_WRITE(NET2280_DEV_U32,
+ 0x0020 | (unsigned long)&devreg->direct_mem_win,
+ cpu_to_le32(1));
+
+ P54U_WRITE(NET2280_DEV_U32,
+ 0x0024 | (unsigned long)&devreg->direct_mem_win,
+ cpu_to_le32(block_len));
+ P54U_WRITE(NET2280_DEV_U32,
+ 0x0028 | (unsigned long)&devreg->direct_mem_win,
+ cpu_to_le32(offset));
+
+ P54U_WRITE(NET2280_DEV_U32, &devreg->dma_addr,
+ cpu_to_le32(NET2280_EPA_FIFO_PCI_ADDR));
+ P54U_WRITE(NET2280_DEV_U32, &devreg->dma_len,
+ cpu_to_le32(block_len >> 2));
+ P54U_WRITE(NET2280_DEV_U32, &devreg->dma_ctrl,
+ cpu_to_le32(ISL38XX_DMA_MASTER_CONTROL_TRIGGER));
+
+ mdelay(10);
+
+ P54U_READ(NET2280_DEV_U32,
+ 0x002C | (unsigned long)&devreg->direct_mem_win);
+ if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) ||
+ !(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) {
+ dev_err(&priv->udev->dev, "(p54usb) firmware DMA "
+ "transfer failed\n");
+ goto fail;
+ }
+
+ P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_STAT,
+ cpu_to_le32(NET2280_FIFO_FLUSH));
+
+ remains -= block_len;
+ data += block_len;
+ offset += block_len;
+ }
+
+ /* do ramboot */
+ P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat);
+ reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
+ reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);
+ reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT);
+ P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
+
+ mdelay(20);
+
+ reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
+ P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
+
+ reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
+ P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
+
+ mdelay(100);
+
+ P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
+ P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
+
+ /* start up the firmware */
+ P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable,
+ cpu_to_le32(ISL38XX_INT_IDENT_INIT));
+
+ P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
+ cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
+
+ P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1,
+ cpu_to_le32(NET2280_PCI_INTA_INTERRUPT_ENABLE |
+ NET2280_USB_INTERRUPT_ENABLE));
+
+ P54U_WRITE(NET2280_DEV_U32, &devreg->dev_int,
+ cpu_to_le32(ISL38XX_DEV_INT_RESET));
+
+ err = usb_interrupt_msg(priv->udev,
+ usb_rcvbulkpipe(priv->udev, P54U_PIPE_INT),
+ buf, sizeof(__le32), &alen, 1000);
+ if (err || alen != sizeof(__le32))
+ goto fail;
+
+ P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
+ P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
+
+ if (!(reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT)))
+ err = -EINVAL;
+
+ P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0);
+ P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
+ cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
+
+#undef P54U_WRITE
+#undef P54U_READ
+
+fail:
+ kfree(buf);
+ return err;
+}
+
+static int p54_find_type(struct p54u_priv *priv)
+{
+ int i;
+
+ for (i = 0; i < __NUM_P54U_HWTYPES; i++)
+ if (p54u_fwlist[i].type == priv->hw_type)
+ break;
+ if (i == __NUM_P54U_HWTYPES)
+ return -EOPNOTSUPP;
+
+ return i;
+}
+
+static int p54u_start_ops(struct p54u_priv *priv)
+{
+ struct ieee80211_hw *dev = priv->common.hw;
+ int ret;
+
+ ret = p54_parse_firmware(dev, priv->fw);
+ if (ret)
+ goto err_out;
+
+ ret = p54_find_type(priv);
+ if (ret < 0)
+ goto err_out;
+
+ if (priv->common.fw_interface != p54u_fwlist[ret].intf) {
+ dev_err(&priv->udev->dev, "wrong firmware, please get "
+ "a firmware for \"%s\" and try again.\n",
+ p54u_fwlist[ret].hw);
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ ret = priv->upload_fw(dev);
+ if (ret)
+ goto err_out;
+
+ ret = p54u_open(dev);
+ if (ret)
+ goto err_out;
+
+ ret = p54_read_eeprom(dev);
+ if (ret)
+ goto err_stop;
+
+ p54u_stop(dev);
+
+ ret = p54_register_common(dev, &priv->udev->dev);
+ if (ret)
+ goto err_stop;
+
+ return 0;
+
+err_stop:
+ p54u_stop(dev);
+
+err_out:
+ /*
+ * p54u_disconnect will do the rest of the
+ * cleanup
+ */
+ return ret;
+}
+
+static void p54u_load_firmware_cb(const struct firmware *firmware,
+ void *context)
+{
+ struct p54u_priv *priv = context;
+ struct usb_device *udev = priv->udev;
+ int err;
+
+ complete(&priv->fw_wait_load);
+ if (firmware) {
+ priv->fw = firmware;
+ err = p54u_start_ops(priv);
+ } else {
+ err = -ENOENT;
+ dev_err(&udev->dev, "Firmware not found.\n");
+ }
+
+ if (err) {
+ struct device *parent = priv->udev->dev.parent;
+
+ dev_err(&udev->dev, "failed to initialize device (%d)\n", err);
+
+ if (parent)
+ device_lock(parent);
+
+ device_release_driver(&udev->dev);
+ /*
+ * At this point p54u_disconnect has already freed
+ * the "priv" context. Do not use it anymore!
+ */
+ priv = NULL;
+
+ if (parent)
+ device_unlock(parent);
+ }
+
+ usb_put_dev(udev);
+}
+
+static int p54u_load_firmware(struct ieee80211_hw *dev,
+ struct usb_interface *intf)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct p54u_priv *priv = dev->priv;
+ struct device *device = &udev->dev;
+ int err, i;
+
+ BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES);
+
+ init_completion(&priv->fw_wait_load);
+ i = p54_find_type(priv);
+ if (i < 0)
+ return i;
+
+ dev_info(&priv->udev->dev, "Loading firmware file %s\n",
+ p54u_fwlist[i].fw);
+
+ usb_get_dev(udev);
+ err = request_firmware_nowait(THIS_MODULE, 1, p54u_fwlist[i].fw,
+ device, GFP_KERNEL, priv,
+ p54u_load_firmware_cb);
+ if (err) {
+ dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
+ "(%d)!\n", p54u_fwlist[i].fw, err);
+ usb_put_dev(udev);
+ }
+
+ return err;
+}
+
+static int p54u_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct ieee80211_hw *dev;
+ struct p54u_priv *priv;
+ int err;
+ unsigned int i, recognized_pipes;
+
+ dev = p54_init_common(sizeof(*priv));
+
+ if (!dev) {
+ dev_err(&udev->dev, "(p54usb) ieee80211 alloc failed\n");
+ return -ENOMEM;
+ }
+
+ priv = dev->priv;
+ priv->hw_type = P54U_INVALID_HW;
+
+ SET_IEEE80211_DEV(dev, &intf->dev);
+ usb_set_intfdata(intf, dev);
+ priv->udev = udev;
+ priv->intf = intf;
+ skb_queue_head_init(&priv->rx_queue);
+ init_usb_anchor(&priv->submitted);
+
+ usb_get_dev(udev);
+
+ /* really lazy and simple way of figuring out if we're a 3887 */
+ /* TODO: should just stick the identification in the device table */
+ i = intf->altsetting->desc.bNumEndpoints;
+ recognized_pipes = 0;
+ while (i--) {
+ switch (intf->altsetting->endpoint[i].desc.bEndpointAddress) {
+ case P54U_PIPE_DATA:
+ case P54U_PIPE_MGMT:
+ case P54U_PIPE_BRG:
+ case P54U_PIPE_DEV:
+ case P54U_PIPE_DATA | USB_DIR_IN:
+ case P54U_PIPE_MGMT | USB_DIR_IN:
+ case P54U_PIPE_BRG | USB_DIR_IN:
+ case P54U_PIPE_DEV | USB_DIR_IN:
+ case P54U_PIPE_INT | USB_DIR_IN:
+ recognized_pipes++;
+ }
+ }
+ priv->common.open = p54u_open;
+ priv->common.stop = p54u_stop;
+ if (recognized_pipes < P54U_PIPE_NUMBER) {
+#ifdef CONFIG_PM
+ /* ISL3887 needs a full reset on resume */
+ udev->reset_resume = 1;
+#endif /* CONFIG_PM */
+ err = p54u_device_reset(dev);
+
+ priv->hw_type = P54U_3887;
+ dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr);
+ priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr);
+ priv->common.tx = p54u_tx_lm87;
+ priv->upload_fw = p54u_upload_firmware_3887;
+ } else {
+ priv->hw_type = P54U_NET2280;
+ dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr);
+ priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr);
+ priv->common.tx = p54u_tx_net2280;
+ priv->upload_fw = p54u_upload_firmware_net2280;
+ }
+ err = p54u_load_firmware(dev, intf);
+ if (err) {
+ usb_put_dev(udev);
+ p54_free_common(dev);
+ }
+ return err;
+}
+
+static void p54u_disconnect(struct usb_interface *intf)
+{
+ struct ieee80211_hw *dev = usb_get_intfdata(intf);
+ struct p54u_priv *priv;
+
+ if (!dev)
+ return;
+
+ priv = dev->priv;
+ wait_for_completion(&priv->fw_wait_load);
+ p54_unregister_common(dev);
+
+ usb_put_dev(interface_to_usbdev(intf));
+ release_firmware(priv->fw);
+ p54_free_common(dev);
+}
+
+static int p54u_pre_reset(struct usb_interface *intf)
+{
+ struct ieee80211_hw *dev = usb_get_intfdata(intf);
+
+ if (!dev)
+ return -ENODEV;
+
+ p54u_stop(dev);
+ return 0;
+}
+
+static int p54u_resume(struct usb_interface *intf)
+{
+ struct ieee80211_hw *dev = usb_get_intfdata(intf);
+ struct p54u_priv *priv;
+
+ if (!dev)
+ return -ENODEV;
+
+ priv = dev->priv;
+ if (unlikely(!(priv->upload_fw && priv->fw)))
+ return 0;
+
+ return priv->upload_fw(dev);
+}
+
+static int p54u_post_reset(struct usb_interface *intf)
+{
+ struct ieee80211_hw *dev = usb_get_intfdata(intf);
+ struct p54u_priv *priv;
+ int err;
+
+ err = p54u_resume(intf);
+ if (err)
+ return err;
+
+ /* reinitialize old device state */
+ priv = dev->priv;
+ if (priv->common.mode != NL80211_IFTYPE_UNSPECIFIED)
+ ieee80211_restart_hw(dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int p54u_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ return p54u_pre_reset(intf);
+}
+
+#endif /* CONFIG_PM */
+
+static struct usb_driver p54u_driver = {
+ .name = "p54usb",
+ .id_table = p54u_table,
+ .probe = p54u_probe,
+ .disconnect = p54u_disconnect,
+ .pre_reset = p54u_pre_reset,
+ .post_reset = p54u_post_reset,
+#ifdef CONFIG_PM
+ .suspend = p54u_suspend,
+ .resume = p54u_resume,
+ .reset_resume = p54u_resume,
+#endif /* CONFIG_PM */
+ .soft_unbind = 1,
+ .disable_hub_initiated_lpm = 1,
+};
+
+module_usb_driver(p54u_driver);
--- /dev/null
+#ifndef P54USB_H
+#define P54USB_H
+
+/*
+ * Defines for USB based mac80211 Prism54 driver
+ *
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ *
+ * Based on the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* for isl3886 register definitions used on ver 1 devices */
+#include "p54pci.h"
+#include <linux/usb/net2280.h>
+
+/* pci */
+#define NET2280_BASE 0x10000000
+#define NET2280_BASE2 0x20000000
+
+/* gpio */
+#define P54U_BRG_POWER_UP (1 << GPIO0_DATA)
+#define P54U_BRG_POWER_DOWN (1 << GPIO1_DATA)
+
+/* devinit */
+#define NET2280_CLK_4Mhz (15 << LOCAL_CLOCK_FREQUENCY)
+#define NET2280_CLK_30Mhz (2 << LOCAL_CLOCK_FREQUENCY)
+#define NET2280_CLK_60Mhz (1 << LOCAL_CLOCK_FREQUENCY)
+#define NET2280_CLK_STOP (0 << LOCAL_CLOCK_FREQUENCY)
+#define NET2280_PCI_ENABLE (1 << PCI_ENABLE)
+#define NET2280_PCI_SOFT_RESET (1 << PCI_SOFT_RESET)
+
+/* endpoints */
+#define NET2280_CLEAR_NAK_OUT_PACKETS_MODE (1 << CLEAR_NAK_OUT_PACKETS_MODE)
+#define NET2280_FIFO_FLUSH (1 << FIFO_FLUSH)
+
+/* irq */
+#define NET2280_USB_INTERRUPT_ENABLE (1 << USB_INTERRUPT_ENABLE)
+#define NET2280_PCI_INTA_INTERRUPT (1 << PCI_INTA_INTERRUPT)
+#define NET2280_PCI_INTA_INTERRUPT_ENABLE (1 << PCI_INTA_INTERRUPT_ENABLE)
+
+/* registers */
+#define NET2280_DEVINIT 0x00
+#define NET2280_USBIRQENB1 0x24
+#define NET2280_IRQSTAT1 0x2c
+#define NET2280_FIFOCTL 0x38
+#define NET2280_GPIOCTL 0x50
+#define NET2280_RELNUM 0x88
+#define NET2280_EPA_RSP 0x324
+#define NET2280_EPA_STAT 0x32c
+#define NET2280_EPB_STAT 0x34c
+#define NET2280_EPC_RSP 0x364
+#define NET2280_EPC_STAT 0x36c
+#define NET2280_EPD_STAT 0x38c
+
+#define NET2280_EPA_CFG 0x320
+#define NET2280_EPB_CFG 0x340
+#define NET2280_EPC_CFG 0x360
+#define NET2280_EPD_CFG 0x380
+#define NET2280_EPE_CFG 0x3A0
+#define NET2280_EPF_CFG 0x3C0
+#define P54U_DEV_BASE 0x40000000
+
+struct net2280_tx_hdr {
+ __le32 device_addr;
+ __le16 len;
+ __le16 follower; /* ? */
+ u8 padding[8];
+} __packed;
+
+struct lm87_tx_hdr {
+ __le32 device_addr;
+ __le32 chksum;
+} __packed;
+
+/* Some flags for the isl hardware registers controlling DMA inside the
+ * chip */
+#define ISL38XX_DMA_STATUS_DONE 0x00000001
+#define ISL38XX_DMA_STATUS_READY 0x00000002
+#define NET2280_EPA_FIFO_PCI_ADDR 0x20000000
+#define ISL38XX_DMA_MASTER_CONTROL_TRIGGER 0x00000004
+
+enum net2280_op_type {
+ NET2280_BRG_U32 = 0x001F,
+ NET2280_BRG_CFG_U32 = 0x000F,
+ NET2280_BRG_CFG_U16 = 0x0003,
+ NET2280_DEV_U32 = 0x080F,
+ NET2280_DEV_CFG_U32 = 0x088F,
+ NET2280_DEV_CFG_U16 = 0x0883
+};
+
+struct net2280_reg_write {
+ __le16 port;
+ __le32 addr;
+ __le32 val;
+} __packed;
+
+struct net2280_reg_read {
+ __le16 port;
+ __le32 addr;
+} __packed;
+
+#define P54U_FW_BLOCK 2048
+
+#define X2_SIGNATURE "x2 "
+#define X2_SIGNATURE_SIZE 4
+
+struct x2_header {
+ u8 signature[X2_SIGNATURE_SIZE];
+ __le32 fw_load_addr;
+ __le32 fw_length;
+ __le32 crc;
+} __packed;
+
+/* pipes 3 and 4 are not used by the driver */
+#define P54U_PIPE_NUMBER 9
+
+enum p54u_pipe_addr {
+ P54U_PIPE_DATA = 0x01,
+ P54U_PIPE_MGMT = 0x02,
+ P54U_PIPE_3 = 0x03,
+ P54U_PIPE_4 = 0x04,
+ P54U_PIPE_BRG = 0x0d,
+ P54U_PIPE_DEV = 0x0e,
+ P54U_PIPE_INT = 0x0f
+};
+
+struct p54u_rx_info {
+ struct urb *urb;
+ struct ieee80211_hw *dev;
+};
+
+enum p54u_hw_type {
+ P54U_INVALID_HW,
+ P54U_NET2280,
+ P54U_3887,
+
+ /* keep last */
+ __NUM_P54U_HWTYPES,
+};
+
+struct p54u_priv {
+ struct p54_common common;
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ int (*upload_fw)(struct ieee80211_hw *dev);
+
+ enum p54u_hw_type hw_type;
+ spinlock_t lock;
+ struct sk_buff_head rx_queue;
+ struct usb_anchor submitted;
+ const struct firmware *fw;
+
+ /* asynchronous firmware callback */
+ struct completion fw_wait_load;
+};
+
+#endif /* P54USB_H */
--- /dev/null
+/*
+ * Common code for mac80211 Prism54 drivers
+ *
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * Based on:
+ * - the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ * - stlc45xx driver
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/export.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <asm/div64.h>
+
+#include <net/mac80211.h>
+
+#include "p54.h"
+#include "lmac.h"
+
+#ifdef P54_MM_DEBUG
+static void p54_dump_tx_queue(struct p54_common *priv)
+{
+ unsigned long flags;
+ struct ieee80211_tx_info *info;
+ struct p54_tx_info *range;
+ struct sk_buff *skb;
+ struct p54_hdr *hdr;
+ unsigned int i = 0;
+ u32 prev_addr;
+ u32 largest_hole = 0, free;
+
+ spin_lock_irqsave(&priv->tx_queue.lock, flags);
+ wiphy_debug(priv->hw->wiphy, "/ --- tx queue dump (%d entries) ---\n",
+ skb_queue_len(&priv->tx_queue));
+
+ prev_addr = priv->rx_start;
+ skb_queue_walk(&priv->tx_queue, skb) {
+ info = IEEE80211_SKB_CB(skb);
+ range = (void *) info->rate_driver_data;
+ hdr = (void *) skb->data;
+
+ free = range->start_addr - prev_addr;
+ wiphy_debug(priv->hw->wiphy,
+ "| [%02d] => [skb:%p skb_len:0x%04x "
+ "hdr:{flags:%02x len:%04x req_id:%04x type:%02x} "
+ "mem:{start:%04x end:%04x, free:%d}]\n",
+ i++, skb, skb->len,
+ le16_to_cpu(hdr->flags), le16_to_cpu(hdr->len),
+ le32_to_cpu(hdr->req_id), le16_to_cpu(hdr->type),
+ range->start_addr, range->end_addr, free);
+
+ prev_addr = range->end_addr;
+ largest_hole = max(largest_hole, free);
+ }
+ free = priv->rx_end - prev_addr;
+ largest_hole = max(largest_hole, free);
+ wiphy_debug(priv->hw->wiphy,
+ "\\ --- [free: %d], largest free block: %d ---\n",
+ free, largest_hole);
+ spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+}
+#endif /* P54_MM_DEBUG */
+
+/*
+ * So, the firmware is somewhat stupid and doesn't know what places in its
+ * memory incoming data should go to. By poking around in the firmware, we
+ * can find some unused memory to upload our packets to. However, data that we
+ * want the card to TX needs to stay intact until the card has told us that
+ * it is done with it. This function finds empty places we can upload to and
+ * marks allocated areas as reserved if necessary. p54_find_and_unlink_skb or
+ * p54_free_skb frees allocated areas.
+ */
+static int p54_assign_address(struct p54_common *priv, struct sk_buff *skb)
+{
+ struct sk_buff *entry, *target_skb = NULL;
+ struct ieee80211_tx_info *info;
+ struct p54_tx_info *range;
+ struct p54_hdr *data = (void *) skb->data;
+ unsigned long flags;
+ u32 last_addr = priv->rx_start;
+ u32 target_addr = priv->rx_start;
+ u16 len = priv->headroom + skb->len + priv->tailroom + 3;
+
+ info = IEEE80211_SKB_CB(skb);
+ range = (void *) info->rate_driver_data;
+ len = (range->extra_len + len) & ~0x3;
+
+ spin_lock_irqsave(&priv->tx_queue.lock, flags);
+ if (unlikely(skb_queue_len(&priv->tx_queue) == 32)) {
+ /*
+ * The tx_queue is now really full.
+ *
+ * TODO: check if the device has crashed and reset it.
+ */
+ spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+ return -EBUSY;
+ }
+
+ skb_queue_walk(&priv->tx_queue, entry) {
+ u32 hole_size;
+ info = IEEE80211_SKB_CB(entry);
+ range = (void *) info->rate_driver_data;
+ hole_size = range->start_addr - last_addr;
+
+ if (!target_skb && hole_size >= len) {
+ target_skb = entry->prev;
+ hole_size -= len;
+ target_addr = last_addr;
+ break;
+ }
+ last_addr = range->end_addr;
+ }
+ if (unlikely(!target_skb)) {
+ if (priv->rx_end - last_addr >= len) {
+ target_skb = priv->tx_queue.prev;
+ if (!skb_queue_empty(&priv->tx_queue)) {
+ info = IEEE80211_SKB_CB(target_skb);
+ range = (void *)info->rate_driver_data;
+ target_addr = range->end_addr;
+ }
+ } else {
+ spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+ return -ENOSPC;
+ }
+ }
+
+ info = IEEE80211_SKB_CB(skb);
+ range = (void *) info->rate_driver_data;
+ range->start_addr = target_addr;
+ range->end_addr = target_addr + len;
+ data->req_id = cpu_to_le32(target_addr + priv->headroom);
+ if (IS_DATA_FRAME(skb) &&
+ unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON))
+ priv->beacon_req_id = data->req_id;
+
+ __skb_queue_after(&priv->tx_queue, target_skb, skb);
+ spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+ return 0;
+}
+
+static void p54_tx_pending(struct p54_common *priv)
+{
+ struct sk_buff *skb;
+ int ret;
+
+ skb = skb_dequeue(&priv->tx_pending);
+ if (unlikely(!skb))
+ return ;
+
+ ret = p54_assign_address(priv, skb);
+ if (unlikely(ret))
+ skb_queue_head(&priv->tx_pending, skb);
+ else
+ priv->tx(priv->hw, skb);
+}
+
+static void p54_wake_queues(struct p54_common *priv)
+{
+ unsigned long flags;
+ unsigned int i;
+
+ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
+ return ;
+
+ p54_tx_pending(priv);
+
+ spin_lock_irqsave(&priv->tx_stats_lock, flags);
+ for (i = 0; i < priv->hw->queues; i++) {
+ if (priv->tx_stats[i + P54_QUEUE_DATA].len <
+ priv->tx_stats[i + P54_QUEUE_DATA].limit)
+ ieee80211_wake_queue(priv->hw, i);
+ }
+ spin_unlock_irqrestore(&priv->tx_stats_lock, flags);
+}
+
+static int p54_tx_qos_accounting_alloc(struct p54_common *priv,
+ struct sk_buff *skb,
+ const u16 p54_queue)
+{
+ struct p54_tx_queue_stats *queue;
+ unsigned long flags;
+
+ if (WARN_ON(p54_queue >= P54_QUEUE_NUM))
+ return -EINVAL;
+
+ queue = &priv->tx_stats[p54_queue];
+
+ spin_lock_irqsave(&priv->tx_stats_lock, flags);
+ if (unlikely(queue->len >= queue->limit && IS_QOS_QUEUE(p54_queue))) {
+ spin_unlock_irqrestore(&priv->tx_stats_lock, flags);
+ return -ENOSPC;
+ }
+
+ queue->len++;
+ queue->count++;
+
+ if (unlikely(queue->len == queue->limit && IS_QOS_QUEUE(p54_queue))) {
+ u16 ac_queue = p54_queue - P54_QUEUE_DATA;
+ ieee80211_stop_queue(priv->hw, ac_queue);
+ }
+
+ spin_unlock_irqrestore(&priv->tx_stats_lock, flags);
+ return 0;
+}
+
+static void p54_tx_qos_accounting_free(struct p54_common *priv,
+ struct sk_buff *skb)
+{
+ if (IS_DATA_FRAME(skb)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->tx_stats_lock, flags);
+ priv->tx_stats[GET_HW_QUEUE(skb)].len--;
+ spin_unlock_irqrestore(&priv->tx_stats_lock, flags);
+
+ if (unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON)) {
+ if (priv->beacon_req_id == GET_REQ_ID(skb)) {
+ /* this is the active beacon set anymore */
+ priv->beacon_req_id = 0;
+ }
+ complete(&priv->beacon_comp);
+ }
+ }
+ p54_wake_queues(priv);
+}
+
+void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb)
+{
+ struct p54_common *priv = dev->priv;
+ if (unlikely(!skb))
+ return ;
+
+ skb_unlink(skb, &priv->tx_queue);
+ p54_tx_qos_accounting_free(priv, skb);
+ ieee80211_free_txskb(dev, skb);
+}
+EXPORT_SYMBOL_GPL(p54_free_skb);
+
+static struct sk_buff *p54_find_and_unlink_skb(struct p54_common *priv,
+ const __le32 req_id)
+{
+ struct sk_buff *entry;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->tx_queue.lock, flags);
+ skb_queue_walk(&priv->tx_queue, entry) {
+ struct p54_hdr *hdr = (struct p54_hdr *) entry->data;
+
+ if (hdr->req_id == req_id) {
+ __skb_unlink(entry, &priv->tx_queue);
+ spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+ p54_tx_qos_accounting_free(priv, entry);
+ return entry;
+ }
+ }
+ spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+ return NULL;
+}
+
+void p54_tx(struct p54_common *priv, struct sk_buff *skb)
+{
+ skb_queue_tail(&priv->tx_pending, skb);
+ p54_tx_pending(priv);
+}
+
+static int p54_rssi_to_dbm(struct p54_common *priv, int rssi)
+{
+ if (priv->rxhw != 5) {
+ return ((rssi * priv->cur_rssi->mul) / 64 +
+ priv->cur_rssi->add) / 4;
+ } else {
+ /*
+ * TODO: find the correct formula
+ */
+ return rssi / 2 - 110;
+ }
+}
+
+/*
+ * Even if the firmware is capable of dealing with incoming traffic,
+ * while dozing, we have to prepared in case mac80211 uses PS-POLL
+ * to retrieve outstanding frames from our AP.
+ * (see comment in net/mac80211/mlme.c @ line 1993)
+ */
+static void p54_pspoll_workaround(struct p54_common *priv, struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (void *) skb->data;
+ struct ieee80211_tim_ie *tim_ie;
+ u8 *tim;
+ u8 tim_len;
+ bool new_psm;
+
+ /* only beacons have a TIM IE */
+ if (!ieee80211_is_beacon(hdr->frame_control))
+ return;
+
+ if (!priv->aid)
+ return;
+
+ /* only consider beacons from the associated BSSID */
+ if (!ether_addr_equal_64bits(hdr->addr3, priv->bssid))
+ return;
+
+ tim = p54_find_ie(skb, WLAN_EID_TIM);
+ if (!tim)
+ return;
+
+ tim_len = tim[1];
+ tim_ie = (struct ieee80211_tim_ie *) &tim[2];
+
+ new_psm = ieee80211_check_tim(tim_ie, tim_len, priv->aid);
+ if (new_psm != priv->powersave_override) {
+ priv->powersave_override = new_psm;
+ p54_set_ps(priv);
+ }
+}
+
+static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb)
+{
+ struct p54_rx_data *hdr = (struct p54_rx_data *) skb->data;
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+ u16 freq = le16_to_cpu(hdr->freq);
+ size_t header_len = sizeof(*hdr);
+ u32 tsf32;
+ u8 rate = hdr->rate & 0xf;
+
+ /*
+ * If the device is in a unspecified state we have to
+ * ignore all data frames. Else we could end up with a
+ * nasty crash.
+ */
+ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
+ return 0;
+
+ if (!(hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_IN_FCS_GOOD)))
+ return 0;
+
+ if (hdr->decrypt_status == P54_DECRYPT_OK)
+ rx_status->flag |= RX_FLAG_DECRYPTED;
+ if ((hdr->decrypt_status == P54_DECRYPT_FAIL_MICHAEL) ||
+ (hdr->decrypt_status == P54_DECRYPT_FAIL_TKIP))
+ rx_status->flag |= RX_FLAG_MMIC_ERROR;
+
+ rx_status->signal = p54_rssi_to_dbm(priv, hdr->rssi);
+ if (hdr->rate & 0x10)
+ rx_status->flag |= RX_FLAG_SHORTPRE;
+ if (priv->hw->conf.chandef.chan->band == IEEE80211_BAND_5GHZ)
+ rx_status->rate_idx = (rate < 4) ? 0 : rate - 4;
+ else
+ rx_status->rate_idx = rate;
+
+ rx_status->freq = freq;
+ rx_status->band = priv->hw->conf.chandef.chan->band;
+ rx_status->antenna = hdr->antenna;
+
+ tsf32 = le32_to_cpu(hdr->tsf32);
+ if (tsf32 < priv->tsf_low32)
+ priv->tsf_high32++;
+ rx_status->mactime = ((u64)priv->tsf_high32) << 32 | tsf32;
+ priv->tsf_low32 = tsf32;
+
+ /* LMAC API Page 10/29 - s_lm_data_in - clock
+ * "usec accurate timestamp of hardware clock
+ * at end of frame (before OFDM SIFS EOF padding"
+ */
+ rx_status->flag |= RX_FLAG_MACTIME_END;
+
+ if (hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN))
+ header_len += hdr->align[0];
+
+ skb_pull(skb, header_len);
+ skb_trim(skb, le16_to_cpu(hdr->len));
+ if (unlikely(priv->hw->conf.flags & IEEE80211_CONF_PS))
+ p54_pspoll_workaround(priv, skb);
+
+ ieee80211_rx_irqsafe(priv->hw, skb);
+
+ ieee80211_queue_delayed_work(priv->hw, &priv->work,
+ msecs_to_jiffies(P54_STATISTICS_UPDATE));
+
+ return -1;
+}
+
+static void p54_rx_frame_sent(struct p54_common *priv, struct sk_buff *skb)
+{
+ struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
+ struct p54_frame_sent *payload = (struct p54_frame_sent *) hdr->data;
+ struct ieee80211_tx_info *info;
+ struct p54_hdr *entry_hdr;
+ struct p54_tx_data *entry_data;
+ struct sk_buff *entry;
+ unsigned int pad = 0, frame_len;
+ int count, idx;
+
+ entry = p54_find_and_unlink_skb(priv, hdr->req_id);
+ if (unlikely(!entry))
+ return ;
+
+ frame_len = entry->len;
+ info = IEEE80211_SKB_CB(entry);
+ entry_hdr = (struct p54_hdr *) entry->data;
+ entry_data = (struct p54_tx_data *) entry_hdr->data;
+ priv->stats.dot11ACKFailureCount += payload->tries - 1;
+
+ /*
+ * Frames in P54_QUEUE_FWSCAN and P54_QUEUE_BEACON are
+ * generated by the driver. Therefore tx_status is bogus
+ * and we don't want to confuse the mac80211 stack.
+ */
+ if (unlikely(entry_data->hw_queue < P54_QUEUE_FWSCAN)) {
+ dev_kfree_skb_any(entry);
+ return ;
+ }
+
+ /*
+ * Clear manually, ieee80211_tx_info_clear_status would
+ * clear the counts too and we need them.
+ */
+ memset(&info->status.ack_signal, 0,
+ sizeof(struct ieee80211_tx_info) -
+ offsetof(struct ieee80211_tx_info, status.ack_signal));
+ BUILD_BUG_ON(offsetof(struct ieee80211_tx_info,
+ status.ack_signal) != 20);
+
+ if (entry_hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN))
+ pad = entry_data->align[0];
+
+ /* walk through the rates array and adjust the counts */
+ count = payload->tries;
+ for (idx = 0; idx < 4; idx++) {
+ if (count >= info->status.rates[idx].count) {
+ count -= info->status.rates[idx].count;
+ } else if (count > 0) {
+ info->status.rates[idx].count = count;
+ count = 0;
+ } else {
+ info->status.rates[idx].idx = -1;
+ info->status.rates[idx].count = 0;
+ }
+ }
+
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
+ !(payload->status & P54_TX_FAILED))
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ if (payload->status & P54_TX_PSM_CANCELLED)
+ info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
+ info->status.ack_signal = p54_rssi_to_dbm(priv,
+ (int)payload->ack_rssi);
+
+ /* Undo all changes to the frame. */
+ switch (entry_data->key_type) {
+ case P54_CRYPTO_TKIPMICHAEL: {
+ u8 *iv = (u8 *)(entry_data->align + pad +
+ entry_data->crypt_offset);
+
+ /* Restore the original TKIP IV. */
+ iv[2] = iv[0];
+ iv[0] = iv[1];
+ iv[1] = (iv[0] | 0x20) & 0x7f; /* WEPSeed - 8.3.2.2 */
+
+ frame_len -= 12; /* remove TKIP_MMIC + TKIP_ICV */
+ break;
+ }
+ case P54_CRYPTO_AESCCMP:
+ frame_len -= 8; /* remove CCMP_MIC */
+ break;
+ case P54_CRYPTO_WEP:
+ frame_len -= 4; /* remove WEP_ICV */
+ break;
+ }
+
+ skb_trim(entry, frame_len);
+ skb_pull(entry, sizeof(*hdr) + pad + sizeof(*entry_data));
+ ieee80211_tx_status_irqsafe(priv->hw, entry);
+}
+
+static void p54_rx_eeprom_readback(struct p54_common *priv,
+ struct sk_buff *skb)
+{
+ struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
+ struct p54_eeprom_lm86 *eeprom = (struct p54_eeprom_lm86 *) hdr->data;
+ struct sk_buff *tmp;
+
+ if (!priv->eeprom)
+ return ;
+
+ if (priv->fw_var >= 0x509) {
+ memcpy(priv->eeprom, eeprom->v2.data,
+ le16_to_cpu(eeprom->v2.len));
+ } else {
+ memcpy(priv->eeprom, eeprom->v1.data,
+ le16_to_cpu(eeprom->v1.len));
+ }
+
+ priv->eeprom = NULL;
+ tmp = p54_find_and_unlink_skb(priv, hdr->req_id);
+ dev_kfree_skb_any(tmp);
+ complete(&priv->eeprom_comp);
+}
+
+static void p54_rx_stats(struct p54_common *priv, struct sk_buff *skb)
+{
+ struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
+ struct p54_statistics *stats = (struct p54_statistics *) hdr->data;
+ struct sk_buff *tmp;
+ struct ieee80211_channel *chan;
+ unsigned int i, rssi, tx, cca, dtime, dtotal, dcca, dtx, drssi, unit;
+ u32 tsf32;
+
+ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
+ return ;
+
+ tsf32 = le32_to_cpu(stats->tsf32);
+ if (tsf32 < priv->tsf_low32)
+ priv->tsf_high32++;
+ priv->tsf_low32 = tsf32;
+
+ priv->stats.dot11RTSFailureCount = le32_to_cpu(stats->rts_fail);
+ priv->stats.dot11RTSSuccessCount = le32_to_cpu(stats->rts_success);
+ priv->stats.dot11FCSErrorCount = le32_to_cpu(stats->rx_bad_fcs);
+
+ priv->noise = p54_rssi_to_dbm(priv, le32_to_cpu(stats->noise));
+
+ /*
+ * STSW450X LMAC API page 26 - 3.8 Statistics
+ * "The exact measurement period can be derived from the
+ * timestamp member".
+ */
+ dtime = tsf32 - priv->survey_raw.timestamp;
+
+ /*
+ * STSW450X LMAC API page 26 - 3.8.1 Noise histogram
+ * The LMAC samples RSSI, CCA and transmit state at regular
+ * periods (typically 8 times per 1k [as in 1024] usec).
+ */
+ cca = le32_to_cpu(stats->sample_cca);
+ tx = le32_to_cpu(stats->sample_tx);
+ rssi = 0;
+ for (i = 0; i < ARRAY_SIZE(stats->sample_noise); i++)
+ rssi += le32_to_cpu(stats->sample_noise[i]);
+
+ dcca = cca - priv->survey_raw.cached_cca;
+ drssi = rssi - priv->survey_raw.cached_rssi;
+ dtx = tx - priv->survey_raw.cached_tx;
+ dtotal = dcca + drssi + dtx;
+
+ /*
+ * update statistics when more than a second is over since the
+ * last call, or when a update is badly needed.
+ */
+ if (dtotal && (priv->update_stats || dtime >= USEC_PER_SEC) &&
+ dtime >= dtotal) {
+ priv->survey_raw.timestamp = tsf32;
+ priv->update_stats = false;
+ unit = dtime / dtotal;
+
+ if (dcca) {
+ priv->survey_raw.cca += dcca * unit;
+ priv->survey_raw.cached_cca = cca;
+ }
+ if (dtx) {
+ priv->survey_raw.tx += dtx * unit;
+ priv->survey_raw.cached_tx = tx;
+ }
+ if (drssi) {
+ priv->survey_raw.rssi += drssi * unit;
+ priv->survey_raw.cached_rssi = rssi;
+ }
+
+ /* 1024 usec / 8 times = 128 usec / time */
+ if (!(priv->phy_ps || priv->phy_idle))
+ priv->survey_raw.active += dtotal * unit;
+ else
+ priv->survey_raw.active += (dcca + dtx) * unit;
+ }
+
+ chan = priv->curchan;
+ if (chan) {
+ struct survey_info *survey = &priv->survey[chan->hw_value];
+ survey->noise = clamp(priv->noise, -128, 127);
+ survey->time = priv->survey_raw.active;
+ survey->time_tx = priv->survey_raw.tx;
+ survey->time_busy = priv->survey_raw.tx +
+ priv->survey_raw.cca;
+ do_div(survey->time, 1024);
+ do_div(survey->time_tx, 1024);
+ do_div(survey->time_busy, 1024);
+ }
+
+ tmp = p54_find_and_unlink_skb(priv, hdr->req_id);
+ dev_kfree_skb_any(tmp);
+ complete(&priv->stat_comp);
+}
+
+static void p54_rx_trap(struct p54_common *priv, struct sk_buff *skb)
+{
+ struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
+ struct p54_trap *trap = (struct p54_trap *) hdr->data;
+ u16 event = le16_to_cpu(trap->event);
+ u16 freq = le16_to_cpu(trap->frequency);
+
+ switch (event) {
+ case P54_TRAP_BEACON_TX:
+ break;
+ case P54_TRAP_RADAR:
+ wiphy_info(priv->hw->wiphy, "radar (freq:%d MHz)\n", freq);
+ break;
+ case P54_TRAP_NO_BEACON:
+ if (priv->vif)
+ ieee80211_beacon_loss(priv->vif);
+ break;
+ case P54_TRAP_SCAN:
+ break;
+ case P54_TRAP_TBTT:
+ break;
+ case P54_TRAP_TIMER:
+ break;
+ case P54_TRAP_FAA_RADIO_OFF:
+ wiphy_rfkill_set_hw_state(priv->hw->wiphy, true);
+ break;
+ case P54_TRAP_FAA_RADIO_ON:
+ wiphy_rfkill_set_hw_state(priv->hw->wiphy, false);
+ break;
+ default:
+ wiphy_info(priv->hw->wiphy, "received event:%x freq:%d\n",
+ event, freq);
+ break;
+ }
+}
+
+static int p54_rx_control(struct p54_common *priv, struct sk_buff *skb)
+{
+ struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
+
+ switch (le16_to_cpu(hdr->type)) {
+ case P54_CONTROL_TYPE_TXDONE:
+ p54_rx_frame_sent(priv, skb);
+ break;
+ case P54_CONTROL_TYPE_TRAP:
+ p54_rx_trap(priv, skb);
+ break;
+ case P54_CONTROL_TYPE_BBP:
+ break;
+ case P54_CONTROL_TYPE_STAT_READBACK:
+ p54_rx_stats(priv, skb);
+ break;
+ case P54_CONTROL_TYPE_EEPROM_READBACK:
+ p54_rx_eeprom_readback(priv, skb);
+ break;
+ default:
+ wiphy_debug(priv->hw->wiphy,
+ "not handling 0x%02x type control frame\n",
+ le16_to_cpu(hdr->type));
+ break;
+ }
+ return 0;
+}
+
+/* returns zero if skb can be reused */
+int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb)
+{
+ struct p54_common *priv = dev->priv;
+ u16 type = le16_to_cpu(*((__le16 *)skb->data));
+
+ if (type & P54_HDR_FLAG_CONTROL)
+ return p54_rx_control(priv, skb);
+ else
+ return p54_rx_data(priv, skb);
+}
+EXPORT_SYMBOL_GPL(p54_rx);
+
+static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_sta *sta,
+ u8 *queue, u32 *extra_len, u16 *flags, u16 *aid,
+ bool *burst_possible)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ *burst_possible = true;
+ else
+ *burst_possible = false;
+
+ if (!(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ))
+ *flags |= P54_HDR_FLAG_DATA_OUT_SEQNR;
+
+ if (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)
+ *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL;
+
+ if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
+ *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL;
+
+ *queue = skb_get_queue_mapping(skb) + P54_QUEUE_DATA;
+
+ switch (priv->mode) {
+ case NL80211_IFTYPE_MONITOR:
+ /*
+ * We have to set P54_HDR_FLAG_DATA_OUT_PROMISC for
+ * every frame in promiscuous/monitor mode.
+ * see STSW45x0C LMAC API - page 12.
+ */
+ *aid = 0;
+ *flags |= P54_HDR_FLAG_DATA_OUT_PROMISC;
+ break;
+ case NL80211_IFTYPE_STATION:
+ *aid = 1;
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
+ *aid = 0;
+ *queue = P54_QUEUE_CAB;
+ return;
+ }
+
+ if (unlikely(ieee80211_is_mgmt(hdr->frame_control))) {
+ if (ieee80211_is_probe_resp(hdr->frame_control)) {
+ *aid = 0;
+ *flags |= P54_HDR_FLAG_DATA_OUT_TIMESTAMP |
+ P54_HDR_FLAG_DATA_OUT_NOCANCEL;
+ return;
+ } else if (ieee80211_is_beacon(hdr->frame_control)) {
+ *aid = 0;
+
+ if (info->flags & IEEE80211_TX_CTL_INJECTED) {
+ /*
+ * Injecting beacons on top of a AP is
+ * not a good idea... nevertheless,
+ * it should be doable.
+ */
+
+ return;
+ }
+
+ *flags |= P54_HDR_FLAG_DATA_OUT_TIMESTAMP;
+ *queue = P54_QUEUE_BEACON;
+ *extra_len = IEEE80211_MAX_TIM_LEN;
+ return;
+ }
+ }
+
+ if (sta)
+ *aid = sta->aid;
+ break;
+ }
+}
+
+static u8 p54_convert_algo(u32 cipher)
+{
+ switch (cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ return P54_CRYPTO_WEP;
+ case WLAN_CIPHER_SUITE_TKIP:
+ return P54_CRYPTO_TKIPMICHAEL;
+ case WLAN_CIPHER_SUITE_CCMP:
+ return P54_CRYPTO_AESCCMP;
+ default:
+ return 0;
+ }
+}
+
+void p54_tx_80211(struct ieee80211_hw *dev,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct p54_common *priv = dev->priv;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct p54_tx_info *p54info;
+ struct p54_hdr *hdr;
+ struct p54_tx_data *txhdr;
+ unsigned int padding, len, extra_len = 0;
+ int i, j, ridx;
+ u16 hdr_flags = 0, aid = 0;
+ u8 rate, queue = 0, crypt_offset = 0;
+ u8 cts_rate = 0x20;
+ u8 rc_flags;
+ u8 calculated_tries[4];
+ u8 nrates = 0, nremaining = 8;
+ bool burst_allowed = false;
+
+ p54_tx_80211_header(priv, skb, info, control->sta, &queue, &extra_len,
+ &hdr_flags, &aid, &burst_allowed);
+
+ if (p54_tx_qos_accounting_alloc(priv, skb, queue)) {
+ ieee80211_free_txskb(dev, skb);
+ return;
+ }
+
+ padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3;
+ len = skb->len;
+
+ if (info->control.hw_key) {
+ crypt_offset = ieee80211_get_hdrlen_from_skb(skb);
+ if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ u8 *iv = (u8 *)(skb->data + crypt_offset);
+ /*
+ * The firmware excepts that the IV has to have
+ * this special format
+ */
+ iv[1] = iv[0];
+ iv[0] = iv[2];
+ iv[2] = 0;
+ }
+ }
+
+ txhdr = (struct p54_tx_data *) skb_push(skb, sizeof(*txhdr) + padding);
+ hdr = (struct p54_hdr *) skb_push(skb, sizeof(*hdr));
+
+ if (padding)
+ hdr_flags |= P54_HDR_FLAG_DATA_ALIGN;
+ hdr->type = cpu_to_le16(aid);
+ hdr->rts_tries = info->control.rates[0].count;
+
+ /*
+ * we register the rates in perfect order, and
+ * RTS/CTS won't happen on 5 GHz
+ */
+ cts_rate = info->control.rts_cts_rate_idx;
+
+ memset(&txhdr->rateset, 0, sizeof(txhdr->rateset));
+
+ /* see how many rates got used */
+ for (i = 0; i < dev->max_rates; i++) {
+ if (info->control.rates[i].idx < 0)
+ break;
+ nrates++;
+ }
+
+ /* limit tries to 8/nrates per rate */
+ for (i = 0; i < nrates; i++) {
+ /*
+ * The magic expression here is equivalent to 8/nrates for
+ * all values that matter, but avoids division and jumps.
+ * Note that nrates can only take the values 1 through 4.
+ */
+ calculated_tries[i] = min_t(int, ((15 >> nrates) | 1) + 1,
+ info->control.rates[i].count);
+ nremaining -= calculated_tries[i];
+ }
+
+ /* if there are tries left, distribute from back to front */
+ for (i = nrates - 1; nremaining > 0 && i >= 0; i--) {
+ int tmp = info->control.rates[i].count - calculated_tries[i];
+
+ if (tmp <= 0)
+ continue;
+ /* RC requested more tries at this rate */
+
+ tmp = min_t(int, tmp, nremaining);
+ calculated_tries[i] += tmp;
+ nremaining -= tmp;
+ }
+
+ ridx = 0;
+ for (i = 0; i < nrates && ridx < 8; i++) {
+ /* we register the rates in perfect order */
+ rate = info->control.rates[i].idx;
+ if (info->band == IEEE80211_BAND_5GHZ)
+ rate += 4;
+
+ /* store the count we actually calculated for TX status */
+ info->control.rates[i].count = calculated_tries[i];
+
+ rc_flags = info->control.rates[i].flags;
+ if (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) {
+ rate |= 0x10;
+ cts_rate |= 0x10;
+ }
+ if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
+ burst_allowed = false;
+ rate |= 0x40;
+ } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
+ rate |= 0x20;
+ burst_allowed = false;
+ }
+ for (j = 0; j < calculated_tries[i] && ridx < 8; j++) {
+ txhdr->rateset[ridx] = rate;
+ ridx++;
+ }
+ }
+
+ if (burst_allowed)
+ hdr_flags |= P54_HDR_FLAG_DATA_OUT_BURST;
+
+ /* TODO: enable bursting */
+ hdr->flags = cpu_to_le16(hdr_flags);
+ hdr->tries = ridx;
+ txhdr->rts_rate_idx = 0;
+ if (info->control.hw_key) {
+ txhdr->key_type = p54_convert_algo(info->control.hw_key->cipher);
+ txhdr->key_len = min((u8)16, info->control.hw_key->keylen);
+ memcpy(txhdr->key, info->control.hw_key->key, txhdr->key_len);
+ if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ /* reserve space for the MIC key */
+ len += 8;
+ memcpy(skb_put(skb, 8), &(info->control.hw_key->key
+ [NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]), 8);
+ }
+ /* reserve some space for ICV */
+ len += info->control.hw_key->icv_len;
+ memset(skb_put(skb, info->control.hw_key->icv_len), 0,
+ info->control.hw_key->icv_len);
+ } else {
+ txhdr->key_type = 0;
+ txhdr->key_len = 0;
+ }
+ txhdr->crypt_offset = crypt_offset;
+ txhdr->hw_queue = queue;
+ txhdr->backlog = priv->tx_stats[queue].len - 1;
+ memset(txhdr->durations, 0, sizeof(txhdr->durations));
+ txhdr->tx_antenna = 2 & priv->tx_diversity_mask;
+ if (priv->rxhw == 5) {
+ txhdr->longbow.cts_rate = cts_rate;
+ txhdr->longbow.output_power = cpu_to_le16(priv->output_power);
+ } else {
+ txhdr->normal.output_power = priv->output_power;
+ txhdr->normal.cts_rate = cts_rate;
+ }
+ if (padding)
+ txhdr->align[0] = padding;
+
+ hdr->len = cpu_to_le16(len);
+ /* modifies skb->cb and with it info, so must be last! */
+ p54info = (void *) info->rate_driver_data;
+ p54info->extra_len = extra_len;
+
+ p54_tx(priv, skb);
+}
+++ /dev/null
-config P54_COMMON
- tristate "Softmac Prism54 support"
- depends on MAC80211
- select FW_LOADER
- select CRC_CCITT
- ---help---
- This is common code for isl38xx/stlc45xx based modules.
- This module does nothing by itself - the USB/PCI/SPI front-ends
- also need to be enabled in order to support any devices.
-
- These devices require softmac firmware which can be found at
- <http://wireless.kernel.org/en/users/Drivers/p54>
-
- If you choose to build a module, it'll be called p54common.
-
-config P54_USB
- tristate "Prism54 USB support"
- depends on P54_COMMON && USB
- select CRC32
- ---help---
- This driver is for USB isl38xx based wireless cards.
-
- These devices require softmac firmware which can be found at
- <http://wireless.kernel.org/en/users/Drivers/p54>
-
- If you choose to build a module, it'll be called p54usb.
-
-config P54_PCI
- tristate "Prism54 PCI support"
- depends on P54_COMMON && PCI
- ---help---
- This driver is for PCI isl38xx based wireless cards.
- This driver supports most devices that are supported by the
- fullmac prism54 driver plus many devices which are not
- supported by the fullmac driver/firmware.
-
- This driver requires softmac firmware which can be found at
- <http://wireless.kernel.org/en/users/Drivers/p54>
-
- If you choose to build a module, it'll be called p54pci.
-
-config P54_SPI
- tristate "Prism54 SPI (stlc45xx) support"
- depends on P54_COMMON && SPI_MASTER
- ---help---
- This driver is for stlc4550 or stlc4560 based wireless chips
- such as Nokia's N800/N810 Portable Internet Tablet.
-
- If you choose to build a module, it'll be called p54spi.
-
-config P54_SPI_DEFAULT_EEPROM
- bool "Include fallback EEPROM blob"
- depends on P54_SPI
- default n
- ---help---
- Unlike the PCI or USB devices, the SPI variants don't have
- a dedicated EEPROM chip to store all device specific values
- for calibration, country and interface settings.
-
- The driver will try to load the image "3826.eeprom", if the
- file is put at the right place. (usually /lib/firmware.)
-
- Only if this request fails, this option will provide a
- backup set of generic values to get the device working.
-
- Enabling this option adds about 4k to p54spi.
-
-config P54_LEDS
- bool
- depends on P54_COMMON && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = P54_COMMON)
- default y
+++ /dev/null
-p54common-objs := eeprom.o fwio.o txrx.o main.o
-p54common-$(CONFIG_P54_LEDS) += led.o
-
-obj-$(CONFIG_P54_COMMON) += p54common.o
-obj-$(CONFIG_P54_USB) += p54usb.o
-obj-$(CONFIG_P54_PCI) += p54pci.o
-obj-$(CONFIG_P54_SPI) += p54spi.o
+++ /dev/null
-/*
- * EEPROM parser code for mac80211 Prism54 drivers
- *
- * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
- * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
- * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
- *
- * Based on:
- * - the islsm (softmac prism54) driver, which is:
- * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
- * - stlc45xx driver
- * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/firmware.h>
-#include <linux/etherdevice.h>
-#include <linux/sort.h>
-#include <linux/slab.h>
-
-#include <net/mac80211.h>
-#include <linux/crc-ccitt.h>
-#include <linux/export.h>
-
-#include "p54.h"
-#include "eeprom.h"
-#include "lmac.h"
-
-static struct ieee80211_rate p54_bgrates[] = {
- { .bitrate = 10, .hw_value = 0, },
- { .bitrate = 20, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
- { .bitrate = 55, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
- { .bitrate = 110, .hw_value = 3, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
- { .bitrate = 60, .hw_value = 4, },
- { .bitrate = 90, .hw_value = 5, },
- { .bitrate = 120, .hw_value = 6, },
- { .bitrate = 180, .hw_value = 7, },
- { .bitrate = 240, .hw_value = 8, },
- { .bitrate = 360, .hw_value = 9, },
- { .bitrate = 480, .hw_value = 10, },
- { .bitrate = 540, .hw_value = 11, },
-};
-
-static struct ieee80211_rate p54_arates[] = {
- { .bitrate = 60, .hw_value = 4, },
- { .bitrate = 90, .hw_value = 5, },
- { .bitrate = 120, .hw_value = 6, },
- { .bitrate = 180, .hw_value = 7, },
- { .bitrate = 240, .hw_value = 8, },
- { .bitrate = 360, .hw_value = 9, },
- { .bitrate = 480, .hw_value = 10, },
- { .bitrate = 540, .hw_value = 11, },
-};
-
-static struct p54_rssi_db_entry p54_rssi_default = {
- /*
- * The defaults are taken from usb-logs of the
- * vendor driver. So, they should be safe to
- * use in case we can't get a match from the
- * rssi <-> dBm conversion database.
- */
- .mul = 130,
- .add = -398,
-};
-
-#define CHAN_HAS_CAL BIT(0)
-#define CHAN_HAS_LIMIT BIT(1)
-#define CHAN_HAS_CURVE BIT(2)
-#define CHAN_HAS_ALL (CHAN_HAS_CAL | CHAN_HAS_LIMIT | CHAN_HAS_CURVE)
-
-struct p54_channel_entry {
- u16 freq;
- u16 data;
- int index;
- int max_power;
- enum ieee80211_band band;
-};
-
-struct p54_channel_list {
- struct p54_channel_entry *channels;
- size_t entries;
- size_t max_entries;
- size_t band_channel_num[IEEE80211_NUM_BANDS];
-};
-
-static int p54_get_band_from_freq(u16 freq)
-{
- /* FIXME: sync these values with the 802.11 spec */
-
- if ((freq >= 2412) && (freq <= 2484))
- return IEEE80211_BAND_2GHZ;
-
- if ((freq >= 4920) && (freq <= 5825))
- return IEEE80211_BAND_5GHZ;
-
- return -1;
-}
-
-static int same_band(u16 freq, u16 freq2)
-{
- return p54_get_band_from_freq(freq) == p54_get_band_from_freq(freq2);
-}
-
-static int p54_compare_channels(const void *_a,
- const void *_b)
-{
- const struct p54_channel_entry *a = _a;
- const struct p54_channel_entry *b = _b;
-
- return a->freq - b->freq;
-}
-
-static int p54_compare_rssichan(const void *_a,
- const void *_b)
-{
- const struct p54_rssi_db_entry *a = _a;
- const struct p54_rssi_db_entry *b = _b;
-
- return a->freq - b->freq;
-}
-
-static int p54_fill_band_bitrates(struct ieee80211_hw *dev,
- struct ieee80211_supported_band *band_entry,
- enum ieee80211_band band)
-{
- /* TODO: generate rate array dynamically */
-
- switch (band) {
- case IEEE80211_BAND_2GHZ:
- band_entry->bitrates = p54_bgrates;
- band_entry->n_bitrates = ARRAY_SIZE(p54_bgrates);
- break;
- case IEEE80211_BAND_5GHZ:
- band_entry->bitrates = p54_arates;
- band_entry->n_bitrates = ARRAY_SIZE(p54_arates);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int p54_generate_band(struct ieee80211_hw *dev,
- struct p54_channel_list *list,
- unsigned int *chan_num,
- enum ieee80211_band band)
-{
- struct p54_common *priv = dev->priv;
- struct ieee80211_supported_band *tmp, *old;
- unsigned int i, j;
- int ret = -ENOMEM;
-
- if ((!list->entries) || (!list->band_channel_num[band]))
- return -EINVAL;
-
- tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
- if (!tmp)
- goto err_out;
-
- tmp->channels = kzalloc(sizeof(struct ieee80211_channel) *
- list->band_channel_num[band], GFP_KERNEL);
- if (!tmp->channels)
- goto err_out;
-
- ret = p54_fill_band_bitrates(dev, tmp, band);
- if (ret)
- goto err_out;
-
- for (i = 0, j = 0; (j < list->band_channel_num[band]) &&
- (i < list->entries); i++) {
- struct p54_channel_entry *chan = &list->channels[i];
- struct ieee80211_channel *dest = &tmp->channels[j];
-
- if (chan->band != band)
- continue;
-
- if (chan->data != CHAN_HAS_ALL) {
- wiphy_err(dev->wiphy, "%s%s%s is/are missing for "
- "channel:%d [%d MHz].\n",
- (chan->data & CHAN_HAS_CAL ? "" :
- " [iqauto calibration data]"),
- (chan->data & CHAN_HAS_LIMIT ? "" :
- " [output power limits]"),
- (chan->data & CHAN_HAS_CURVE ? "" :
- " [curve data]"),
- chan->index, chan->freq);
- continue;
- }
-
- dest->band = chan->band;
- dest->center_freq = chan->freq;
- dest->max_power = chan->max_power;
- priv->survey[*chan_num].channel = &tmp->channels[j];
- priv->survey[*chan_num].filled = SURVEY_INFO_NOISE_DBM |
- SURVEY_INFO_TIME |
- SURVEY_INFO_TIME_BUSY |
- SURVEY_INFO_TIME_TX;
- dest->hw_value = (*chan_num);
- j++;
- (*chan_num)++;
- }
-
- if (j == 0) {
- wiphy_err(dev->wiphy, "Disabling totally damaged %d GHz band\n",
- (band == IEEE80211_BAND_2GHZ) ? 2 : 5);
-
- ret = -ENODATA;
- goto err_out;
- }
-
- tmp->n_channels = j;
- old = priv->band_table[band];
- priv->band_table[band] = tmp;
- if (old) {
- kfree(old->channels);
- kfree(old);
- }
-
- return 0;
-
-err_out:
- if (tmp) {
- kfree(tmp->channels);
- kfree(tmp);
- }
-
- return ret;
-}
-
-static struct p54_channel_entry *p54_update_channel_param(struct p54_channel_list *list,
- u16 freq, u16 data)
-{
- int i;
- struct p54_channel_entry *entry = NULL;
-
- /*
- * usually all lists in the eeprom are mostly sorted.
- * so it's very likely that the entry we are looking for
- * is right at the end of the list
- */
- for (i = list->entries; i >= 0; i--) {
- if (freq == list->channels[i].freq) {
- entry = &list->channels[i];
- break;
- }
- }
-
- if ((i < 0) && (list->entries < list->max_entries)) {
- /* entry does not exist yet. Initialize a new one. */
- int band = p54_get_band_from_freq(freq);
-
- /*
- * filter out frequencies which don't belong into
- * any supported band.
- */
- if (band >= 0) {
- i = list->entries++;
- list->band_channel_num[band]++;
-
- entry = &list->channels[i];
- entry->freq = freq;
- entry->band = band;
- entry->index = ieee80211_frequency_to_channel(freq);
- entry->max_power = 0;
- entry->data = 0;
- }
- }
-
- if (entry)
- entry->data |= data;
-
- return entry;
-}
-
-static int p54_get_maxpower(struct p54_common *priv, void *data)
-{
- switch (priv->rxhw & PDR_SYNTH_FRONTEND_MASK) {
- case PDR_SYNTH_FRONTEND_LONGBOW: {
- struct pda_channel_output_limit_longbow *pda = data;
- int j;
- u16 rawpower = 0;
- pda = data;
- for (j = 0; j < ARRAY_SIZE(pda->point); j++) {
- struct pda_channel_output_limit_point_longbow *point =
- &pda->point[j];
- rawpower = max_t(u16,
- rawpower, le16_to_cpu(point->val_qpsk));
- rawpower = max_t(u16,
- rawpower, le16_to_cpu(point->val_bpsk));
- rawpower = max_t(u16,
- rawpower, le16_to_cpu(point->val_16qam));
- rawpower = max_t(u16,
- rawpower, le16_to_cpu(point->val_64qam));
- }
- /* longbow seems to use 1/16 dBm units */
- return rawpower / 16;
- }
-
- case PDR_SYNTH_FRONTEND_DUETTE3:
- case PDR_SYNTH_FRONTEND_DUETTE2:
- case PDR_SYNTH_FRONTEND_FRISBEE:
- case PDR_SYNTH_FRONTEND_XBOW: {
- struct pda_channel_output_limit *pda = data;
- u8 rawpower = 0;
- rawpower = max(rawpower, pda->val_qpsk);
- rawpower = max(rawpower, pda->val_bpsk);
- rawpower = max(rawpower, pda->val_16qam);
- rawpower = max(rawpower, pda->val_64qam);
- /* raw values are in 1/4 dBm units */
- return rawpower / 4;
- }
-
- default:
- return 20;
- }
-}
-
-static int p54_generate_channel_lists(struct ieee80211_hw *dev)
-{
- struct p54_common *priv = dev->priv;
- struct p54_channel_list *list;
- unsigned int i, j, k, max_channel_num;
- int ret = 0;
- u16 freq;
-
- if ((priv->iq_autocal_len != priv->curve_data->entries) ||
- (priv->iq_autocal_len != priv->output_limit->entries))
- wiphy_err(dev->wiphy,
- "Unsupported or damaged EEPROM detected. "
- "You may not be able to use all channels.\n");
-
- max_channel_num = max_t(unsigned int, priv->output_limit->entries,
- priv->iq_autocal_len);
- max_channel_num = max_t(unsigned int, max_channel_num,
- priv->curve_data->entries);
-
- list = kzalloc(sizeof(*list), GFP_KERNEL);
- if (!list) {
- ret = -ENOMEM;
- goto free;
- }
- priv->chan_num = max_channel_num;
- priv->survey = kzalloc(sizeof(struct survey_info) * max_channel_num,
- GFP_KERNEL);
- if (!priv->survey) {
- ret = -ENOMEM;
- goto free;
- }
-
- list->max_entries = max_channel_num;
- list->channels = kzalloc(sizeof(struct p54_channel_entry) *
- max_channel_num, GFP_KERNEL);
- if (!list->channels) {
- ret = -ENOMEM;
- goto free;
- }
-
- for (i = 0; i < max_channel_num; i++) {
- if (i < priv->iq_autocal_len) {
- freq = le16_to_cpu(priv->iq_autocal[i].freq);
- p54_update_channel_param(list, freq, CHAN_HAS_CAL);
- }
-
- if (i < priv->output_limit->entries) {
- struct p54_channel_entry *tmp;
-
- void *data = (void *) ((unsigned long) i *
- priv->output_limit->entry_size +
- priv->output_limit->offset +
- priv->output_limit->data);
-
- freq = le16_to_cpup((__le16 *) data);
- tmp = p54_update_channel_param(list, freq,
- CHAN_HAS_LIMIT);
- if (tmp) {
- tmp->max_power = p54_get_maxpower(priv, data);
- }
- }
-
- if (i < priv->curve_data->entries) {
- freq = le16_to_cpup((__le16 *) (i *
- priv->curve_data->entry_size +
- priv->curve_data->offset +
- priv->curve_data->data));
-
- p54_update_channel_param(list, freq, CHAN_HAS_CURVE);
- }
- }
-
- /* sort the channel list by frequency */
- sort(list->channels, list->entries, sizeof(struct p54_channel_entry),
- p54_compare_channels, NULL);
-
- k = 0;
- for (i = 0, j = 0; i < IEEE80211_NUM_BANDS; i++) {
- if (p54_generate_band(dev, list, &k, i) == 0)
- j++;
- }
- if (j == 0) {
- /* no useable band available. */
- ret = -EINVAL;
- }
-
-free:
- if (list) {
- kfree(list->channels);
- kfree(list);
- }
- if (ret) {
- kfree(priv->survey);
- priv->survey = NULL;
- }
-
- return ret;
-}
-
-static int p54_convert_rev0(struct ieee80211_hw *dev,
- struct pda_pa_curve_data *curve_data)
-{
- struct p54_common *priv = dev->priv;
- struct p54_pa_curve_data_sample *dst;
- struct pda_pa_curve_data_sample_rev0 *src;
- size_t cd_len = sizeof(*curve_data) +
- (curve_data->points_per_channel*sizeof(*dst) + 2) *
- curve_data->channels;
- unsigned int i, j;
- void *source, *target;
-
- priv->curve_data = kmalloc(sizeof(*priv->curve_data) + cd_len,
- GFP_KERNEL);
- if (!priv->curve_data)
- return -ENOMEM;
-
- priv->curve_data->entries = curve_data->channels;
- priv->curve_data->entry_size = sizeof(__le16) +
- sizeof(*dst) * curve_data->points_per_channel;
- priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data);
- priv->curve_data->len = cd_len;
- memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data));
- source = curve_data->data;
- target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data;
- for (i = 0; i < curve_data->channels; i++) {
- __le16 *freq = source;
- source += sizeof(__le16);
- *((__le16 *)target) = *freq;
- target += sizeof(__le16);
- for (j = 0; j < curve_data->points_per_channel; j++) {
- dst = target;
- src = source;
-
- dst->rf_power = src->rf_power;
- dst->pa_detector = src->pa_detector;
- dst->data_64qam = src->pcv;
- /* "invent" the points for the other modulations */
-#define SUB(x, y) (u8)(((x) - (y)) > (x) ? 0 : (x) - (y))
- dst->data_16qam = SUB(src->pcv, 12);
- dst->data_qpsk = SUB(dst->data_16qam, 12);
- dst->data_bpsk = SUB(dst->data_qpsk, 12);
- dst->data_barker = SUB(dst->data_bpsk, 14);
-#undef SUB
- target += sizeof(*dst);
- source += sizeof(*src);
- }
- }
-
- return 0;
-}
-
-static int p54_convert_rev1(struct ieee80211_hw *dev,
- struct pda_pa_curve_data *curve_data)
-{
- struct p54_common *priv = dev->priv;
- struct p54_pa_curve_data_sample *dst;
- struct pda_pa_curve_data_sample_rev1 *src;
- size_t cd_len = sizeof(*curve_data) +
- (curve_data->points_per_channel*sizeof(*dst) + 2) *
- curve_data->channels;
- unsigned int i, j;
- void *source, *target;
-
- priv->curve_data = kzalloc(cd_len + sizeof(*priv->curve_data),
- GFP_KERNEL);
- if (!priv->curve_data)
- return -ENOMEM;
-
- priv->curve_data->entries = curve_data->channels;
- priv->curve_data->entry_size = sizeof(__le16) +
- sizeof(*dst) * curve_data->points_per_channel;
- priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data);
- priv->curve_data->len = cd_len;
- memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data));
- source = curve_data->data;
- target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data;
- for (i = 0; i < curve_data->channels; i++) {
- __le16 *freq = source;
- source += sizeof(__le16);
- *((__le16 *)target) = *freq;
- target += sizeof(__le16);
- for (j = 0; j < curve_data->points_per_channel; j++) {
- memcpy(target, source, sizeof(*src));
-
- target += sizeof(*dst);
- source += sizeof(*src);
- }
- source++;
- }
-
- return 0;
-}
-
-static const char *p54_rf_chips[] = { "INVALID-0", "Duette3", "Duette2",
- "Frisbee", "Xbow", "Longbow", "INVALID-6", "INVALID-7" };
-
-static int p54_parse_rssical(struct ieee80211_hw *dev,
- u8 *data, int len, u16 type)
-{
- struct p54_common *priv = dev->priv;
- struct p54_rssi_db_entry *entry;
- size_t db_len, entries;
- int offset = 0, i;
-
- if (type != PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) {
- entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2;
- if (len != sizeof(struct pda_rssi_cal_entry) * entries) {
- wiphy_err(dev->wiphy, "rssical size mismatch.\n");
- goto err_data;
- }
- } else {
- /*
- * Some devices (Dell 1450 USB, Xbow 5GHz card, etc...)
- * have an empty two byte header.
- */
- if (*((__le16 *)&data[offset]) == cpu_to_le16(0))
- offset += 2;
-
- entries = (len - offset) /
- sizeof(struct pda_rssi_cal_ext_entry);
-
- if (len < offset ||
- (len - offset) % sizeof(struct pda_rssi_cal_ext_entry) ||
- entries == 0) {
- wiphy_err(dev->wiphy, "invalid rssi database.\n");
- goto err_data;
- }
- }
-
- db_len = sizeof(*entry) * entries;
- priv->rssi_db = kzalloc(db_len + sizeof(*priv->rssi_db), GFP_KERNEL);
- if (!priv->rssi_db)
- return -ENOMEM;
-
- priv->rssi_db->offset = 0;
- priv->rssi_db->entries = entries;
- priv->rssi_db->entry_size = sizeof(*entry);
- priv->rssi_db->len = db_len;
-
- entry = (void *)((unsigned long)priv->rssi_db->data + priv->rssi_db->offset);
- if (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) {
- struct pda_rssi_cal_ext_entry *cal = (void *) &data[offset];
-
- for (i = 0; i < entries; i++) {
- entry[i].freq = le16_to_cpu(cal[i].freq);
- entry[i].mul = (s16) le16_to_cpu(cal[i].mul);
- entry[i].add = (s16) le16_to_cpu(cal[i].add);
- }
- } else {
- struct pda_rssi_cal_entry *cal = (void *) &data[offset];
-
- for (i = 0; i < entries; i++) {
- u16 freq = 0;
- switch (i) {
- case IEEE80211_BAND_2GHZ:
- freq = 2437;
- break;
- case IEEE80211_BAND_5GHZ:
- freq = 5240;
- break;
- }
-
- entry[i].freq = freq;
- entry[i].mul = (s16) le16_to_cpu(cal[i].mul);
- entry[i].add = (s16) le16_to_cpu(cal[i].add);
- }
- }
-
- /* sort the list by channel frequency */
- sort(entry, entries, sizeof(*entry), p54_compare_rssichan, NULL);
- return 0;
-
-err_data:
- wiphy_err(dev->wiphy,
- "rssi calibration data packing type:(%x) len:%d.\n",
- type, len);
-
- print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE, data, len);
-
- wiphy_err(dev->wiphy, "please report this issue.\n");
- return -EINVAL;
-}
-
-struct p54_rssi_db_entry *p54_rssi_find(struct p54_common *priv, const u16 freq)
-{
- struct p54_rssi_db_entry *entry;
- int i, found = -1;
-
- if (!priv->rssi_db)
- return &p54_rssi_default;
-
- entry = (void *)(priv->rssi_db->data + priv->rssi_db->offset);
- for (i = 0; i < priv->rssi_db->entries; i++) {
- if (!same_band(freq, entry[i].freq))
- continue;
-
- if (found == -1) {
- found = i;
- continue;
- }
-
- /* nearest match */
- if (abs(freq - entry[i].freq) <
- abs(freq - entry[found].freq)) {
- found = i;
- continue;
- } else {
- break;
- }
- }
-
- return found < 0 ? &p54_rssi_default : &entry[found];
-}
-
-static void p54_parse_default_country(struct ieee80211_hw *dev,
- void *data, int len)
-{
- struct pda_country *country;
-
- if (len != sizeof(*country)) {
- wiphy_err(dev->wiphy,
- "found possible invalid default country eeprom entry. (entry size: %d)\n",
- len);
-
- print_hex_dump_bytes("country:", DUMP_PREFIX_NONE,
- data, len);
-
- wiphy_err(dev->wiphy, "please report this issue.\n");
- return;
- }
-
- country = (struct pda_country *) data;
- if (country->flags == PDR_COUNTRY_CERT_CODE_PSEUDO)
- regulatory_hint(dev->wiphy, country->alpha2);
- else {
- /* TODO:
- * write a shared/common function that converts
- * "Regulatory domain codes" (802.11-2007 14.8.2.2)
- * into ISO/IEC 3166-1 alpha2 for regulatory_hint.
- */
- }
-}
-
-static int p54_convert_output_limits(struct ieee80211_hw *dev,
- u8 *data, size_t len)
-{
- struct p54_common *priv = dev->priv;
-
- if (len < 2)
- return -EINVAL;
-
- if (data[0] != 0) {
- wiphy_err(dev->wiphy, "unknown output power db revision:%x\n",
- data[0]);
- return -EINVAL;
- }
-
- if (2 + data[1] * sizeof(struct pda_channel_output_limit) > len)
- return -EINVAL;
-
- priv->output_limit = kmalloc(data[1] *
- sizeof(struct pda_channel_output_limit) +
- sizeof(*priv->output_limit), GFP_KERNEL);
-
- if (!priv->output_limit)
- return -ENOMEM;
-
- priv->output_limit->offset = 0;
- priv->output_limit->entries = data[1];
- priv->output_limit->entry_size =
- sizeof(struct pda_channel_output_limit);
- priv->output_limit->len = priv->output_limit->entry_size *
- priv->output_limit->entries +
- priv->output_limit->offset;
-
- memcpy(priv->output_limit->data, &data[2],
- data[1] * sizeof(struct pda_channel_output_limit));
-
- return 0;
-}
-
-static struct p54_cal_database *p54_convert_db(struct pda_custom_wrapper *src,
- size_t total_len)
-{
- struct p54_cal_database *dst;
- size_t payload_len, entries, entry_size, offset;
-
- payload_len = le16_to_cpu(src->len);
- entries = le16_to_cpu(src->entries);
- entry_size = le16_to_cpu(src->entry_size);
- offset = le16_to_cpu(src->offset);
- if (((entries * entry_size + offset) != payload_len) ||
- (payload_len + sizeof(*src) != total_len))
- return NULL;
-
- dst = kmalloc(sizeof(*dst) + payload_len, GFP_KERNEL);
- if (!dst)
- return NULL;
-
- dst->entries = entries;
- dst->entry_size = entry_size;
- dst->offset = offset;
- dst->len = payload_len;
-
- memcpy(dst->data, src->data, payload_len);
- return dst;
-}
-
-int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
-{
- struct p54_common *priv = dev->priv;
- struct eeprom_pda_wrap *wrap;
- struct pda_entry *entry;
- unsigned int data_len, entry_len;
- void *tmp;
- int err;
- u8 *end = (u8 *)eeprom + len;
- u16 synth = 0;
- u16 crc16 = ~0;
-
- wrap = (struct eeprom_pda_wrap *) eeprom;
- entry = (void *)wrap->data + le16_to_cpu(wrap->len);
-
- /* verify that at least the entry length/code fits */
- while ((u8 *)entry <= end - sizeof(*entry)) {
- entry_len = le16_to_cpu(entry->len);
- data_len = ((entry_len - 1) << 1);
-
- /* abort if entry exceeds whole structure */
- if ((u8 *)entry + sizeof(*entry) + data_len > end)
- break;
-
- switch (le16_to_cpu(entry->code)) {
- case PDR_MAC_ADDRESS:
- if (data_len != ETH_ALEN)
- break;
- SET_IEEE80211_PERM_ADDR(dev, entry->data);
- break;
- case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS:
- if (priv->output_limit)
- break;
- err = p54_convert_output_limits(dev, entry->data,
- data_len);
- if (err)
- goto err;
- break;
- case PDR_PRISM_PA_CAL_CURVE_DATA: {
- struct pda_pa_curve_data *curve_data =
- (struct pda_pa_curve_data *)entry->data;
- if (data_len < sizeof(*curve_data)) {
- err = -EINVAL;
- goto err;
- }
-
- switch (curve_data->cal_method_rev) {
- case 0:
- err = p54_convert_rev0(dev, curve_data);
- break;
- case 1:
- err = p54_convert_rev1(dev, curve_data);
- break;
- default:
- wiphy_err(dev->wiphy,
- "unknown curve data revision %d\n",
- curve_data->cal_method_rev);
- err = -ENODEV;
- break;
- }
- if (err)
- goto err;
- }
- break;
- case PDR_PRISM_ZIF_TX_IQ_CALIBRATION:
- priv->iq_autocal = kmemdup(entry->data, data_len,
- GFP_KERNEL);
- if (!priv->iq_autocal) {
- err = -ENOMEM;
- goto err;
- }
-
- priv->iq_autocal_len = data_len / sizeof(struct pda_iq_autocal_entry);
- break;
- case PDR_DEFAULT_COUNTRY:
- p54_parse_default_country(dev, entry->data, data_len);
- break;
- case PDR_INTERFACE_LIST:
- tmp = entry->data;
- while ((u8 *)tmp < entry->data + data_len) {
- struct exp_if *exp_if = tmp;
- if (exp_if->if_id == cpu_to_le16(IF_ID_ISL39000))
- synth = le16_to_cpu(exp_if->variant);
- tmp += sizeof(*exp_if);
- }
- break;
- case PDR_HARDWARE_PLATFORM_COMPONENT_ID:
- if (data_len < 2)
- break;
- priv->version = *(u8 *)(entry->data + 1);
- break;
- case PDR_RSSI_LINEAR_APPROXIMATION:
- case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND:
- case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED:
- err = p54_parse_rssical(dev, entry->data, data_len,
- le16_to_cpu(entry->code));
- if (err)
- goto err;
- break;
- case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2: {
- struct pda_custom_wrapper *pda = (void *) entry->data;
- __le16 *src;
- u16 *dst;
- int i;
-
- if (priv->rssi_db || data_len < sizeof(*pda))
- break;
-
- priv->rssi_db = p54_convert_db(pda, data_len);
- if (!priv->rssi_db)
- break;
-
- src = (void *) priv->rssi_db->data;
- dst = (void *) priv->rssi_db->data;
-
- for (i = 0; i < priv->rssi_db->entries; i++)
- *(dst++) = (s16) le16_to_cpu(*(src++));
-
- }
- break;
- case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: {
- struct pda_custom_wrapper *pda = (void *) entry->data;
- if (priv->output_limit || data_len < sizeof(*pda))
- break;
- priv->output_limit = p54_convert_db(pda, data_len);
- }
- break;
- case PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM: {
- struct pda_custom_wrapper *pda = (void *) entry->data;
- if (priv->curve_data || data_len < sizeof(*pda))
- break;
- priv->curve_data = p54_convert_db(pda, data_len);
- }
- break;
- case PDR_END:
- crc16 = ~crc_ccitt(crc16, (u8 *) entry, sizeof(*entry));
- if (crc16 != le16_to_cpup((__le16 *)entry->data)) {
- wiphy_err(dev->wiphy, "eeprom failed checksum "
- "test!\n");
- err = -ENOMSG;
- goto err;
- } else {
- goto good_eeprom;
- }
- break;
- default:
- break;
- }
-
- crc16 = crc_ccitt(crc16, (u8 *)entry, (entry_len + 1) * 2);
- entry = (void *)entry + (entry_len + 1) * 2;
- }
-
- wiphy_err(dev->wiphy, "unexpected end of eeprom data.\n");
- err = -ENODATA;
- goto err;
-
-good_eeprom:
- if (!synth || !priv->iq_autocal || !priv->output_limit ||
- !priv->curve_data) {
- wiphy_err(dev->wiphy,
- "not all required entries found in eeprom!\n");
- err = -EINVAL;
- goto err;
- }
-
- priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK;
-
- err = p54_generate_channel_lists(dev);
- if (err)
- goto err;
-
- if (priv->rxhw == PDR_SYNTH_FRONTEND_XBOW)
- p54_init_xbow_synth(priv);
- if (!(synth & PDR_SYNTH_24_GHZ_DISABLED))
- dev->wiphy->bands[IEEE80211_BAND_2GHZ] =
- priv->band_table[IEEE80211_BAND_2GHZ];
- if (!(synth & PDR_SYNTH_5_GHZ_DISABLED))
- dev->wiphy->bands[IEEE80211_BAND_5GHZ] =
- priv->band_table[IEEE80211_BAND_5GHZ];
- if ((synth & PDR_SYNTH_RX_DIV_MASK) == PDR_SYNTH_RX_DIV_SUPPORTED)
- priv->rx_diversity_mask = 3;
- if ((synth & PDR_SYNTH_TX_DIV_MASK) == PDR_SYNTH_TX_DIV_SUPPORTED)
- priv->tx_diversity_mask = 3;
-
- if (!is_valid_ether_addr(dev->wiphy->perm_addr)) {
- u8 perm_addr[ETH_ALEN];
-
- wiphy_warn(dev->wiphy,
- "Invalid hwaddr! Using randomly generated MAC addr\n");
- eth_random_addr(perm_addr);
- SET_IEEE80211_PERM_ADDR(dev, perm_addr);
- }
-
- priv->cur_rssi = &p54_rssi_default;
-
- wiphy_info(dev->wiphy, "hwaddr %pM, MAC:isl38%02x RF:%s\n",
- dev->wiphy->perm_addr, priv->version,
- p54_rf_chips[priv->rxhw]);
-
- return 0;
-
-err:
- kfree(priv->iq_autocal);
- kfree(priv->output_limit);
- kfree(priv->curve_data);
- kfree(priv->rssi_db);
- kfree(priv->survey);
- priv->iq_autocal = NULL;
- priv->output_limit = NULL;
- priv->curve_data = NULL;
- priv->rssi_db = NULL;
- priv->survey = NULL;
-
- wiphy_err(dev->wiphy, "eeprom parse failed!\n");
- return err;
-}
-EXPORT_SYMBOL_GPL(p54_parse_eeprom);
-
-int p54_read_eeprom(struct ieee80211_hw *dev)
-{
- struct p54_common *priv = dev->priv;
- size_t eeprom_size = 0x2020, offset = 0, blocksize, maxblocksize;
- int ret = -ENOMEM;
- void *eeprom;
-
- maxblocksize = EEPROM_READBACK_LEN;
- if (priv->fw_var >= 0x509)
- maxblocksize -= 0xc;
- else
- maxblocksize -= 0x4;
-
- eeprom = kzalloc(eeprom_size, GFP_KERNEL);
- if (unlikely(!eeprom))
- goto free;
-
- while (eeprom_size) {
- blocksize = min(eeprom_size, maxblocksize);
- ret = p54_download_eeprom(priv, eeprom + offset,
- offset, blocksize);
- if (unlikely(ret))
- goto free;
-
- offset += blocksize;
- eeprom_size -= blocksize;
- }
-
- ret = p54_parse_eeprom(dev, eeprom, offset);
-free:
- kfree(eeprom);
- return ret;
-}
-EXPORT_SYMBOL_GPL(p54_read_eeprom);
+++ /dev/null
-/*
- * eeprom specific definitions for mac80211 Prism54 drivers
- *
- * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
- * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
- *
- * Based on:
- * - the islsm (softmac prism54) driver, which is:
- * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
- *
- * - LMAC API interface header file for STLC4560 (lmac_longbow.h)
- * Copyright (C) 2007 Conexant Systems, Inc.
- *
- * - islmvc driver
- * Copyright (C) 2001 Intersil Americas Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef EEPROM_H
-#define EEPROM_H
-
-/* PDA defines are Copyright (C) 2005 Nokia Corporation (taken from islsm_pda.h) */
-
-struct pda_entry {
- __le16 len; /* includes both code and data */
- __le16 code;
- u8 data[0];
-} __packed;
-
-struct eeprom_pda_wrap {
- __le32 magic;
- __le16 pad;
- __le16 len;
- __le32 arm_opcode;
- u8 data[0];
-} __packed;
-
-struct p54_iq_autocal_entry {
- __le16 iq_param[4];
-} __packed;
-
-struct pda_iq_autocal_entry {
- __le16 freq;
- struct p54_iq_autocal_entry params;
-} __packed;
-
-struct pda_channel_output_limit {
- __le16 freq;
- u8 val_bpsk;
- u8 val_qpsk;
- u8 val_16qam;
- u8 val_64qam;
- u8 rate_set_mask;
- u8 rate_set_size;
-} __packed;
-
-struct pda_channel_output_limit_point_longbow {
- __le16 val_bpsk;
- __le16 val_qpsk;
- __le16 val_16qam;
- __le16 val_64qam;
-} __packed;
-
-struct pda_channel_output_limit_longbow {
- __le16 freq;
- struct pda_channel_output_limit_point_longbow point[3];
-} __packed;
-
-struct pda_pa_curve_data_sample_rev0 {
- u8 rf_power;
- u8 pa_detector;
- u8 pcv;
-} __packed;
-
-struct pda_pa_curve_data_sample_rev1 {
- u8 rf_power;
- u8 pa_detector;
- u8 data_barker;
- u8 data_bpsk;
- u8 data_qpsk;
- u8 data_16qam;
- u8 data_64qam;
-} __packed;
-
-struct pda_pa_curve_data {
- u8 cal_method_rev;
- u8 channels;
- u8 points_per_channel;
- u8 padding;
- u8 data[0];
-} __packed;
-
-struct pda_rssi_cal_ext_entry {
- __le16 freq;
- __le16 mul;
- __le16 add;
-} __packed;
-
-struct pda_rssi_cal_entry {
- __le16 mul;
- __le16 add;
-} __packed;
-
-struct pda_country {
- u8 regdomain;
- u8 alpha2[2];
- u8 flags;
-} __packed;
-
-struct pda_antenna_gain {
- struct {
- u8 gain_5GHz; /* 0.25 dBi units */
- u8 gain_2GHz; /* 0.25 dBi units */
- } __packed antenna[0];
-} __packed;
-
-struct pda_custom_wrapper {
- __le16 entries;
- __le16 entry_size;
- __le16 offset;
- __le16 len;
- u8 data[0];
-} __packed;
-
-/*
- * this defines the PDR codes used to build PDAs as defined in document
- * number 553155. The current implementation mirrors version 1.1 of the
- * document and lists only PDRs supported by the ARM platform.
- */
-
-/* common and choice range (0x0000 - 0x0fff) */
-#define PDR_END 0x0000
-#define PDR_MANUFACTURING_PART_NUMBER 0x0001
-#define PDR_PDA_VERSION 0x0002
-#define PDR_NIC_SERIAL_NUMBER 0x0003
-#define PDR_NIC_RAM_SIZE 0x0005
-#define PDR_RFMODEM_SUP_RANGE 0x0006
-#define PDR_PRISM_MAC_SUP_RANGE 0x0007
-#define PDR_NIC_ID 0x0008
-
-#define PDR_MAC_ADDRESS 0x0101
-#define PDR_REGULATORY_DOMAIN_LIST 0x0103 /* obsolete */
-#define PDR_ALLOWED_CHAN_SET 0x0104
-#define PDR_DEFAULT_CHAN 0x0105
-#define PDR_TEMPERATURE_TYPE 0x0107
-
-#define PDR_IFR_SETTING 0x0200
-#define PDR_RFR_SETTING 0x0201
-#define PDR_3861_BASELINE_REG_SETTINGS 0x0202
-#define PDR_3861_SHADOW_REG_SETTINGS 0x0203
-#define PDR_3861_IFRF_REG_SETTINGS 0x0204
-
-#define PDR_3861_CHAN_CALIB_SET_POINTS 0x0300
-#define PDR_3861_CHAN_CALIB_INTEGRATOR 0x0301
-
-#define PDR_3842_PRISM_II_NIC_CONFIG 0x0400
-#define PDR_PRISM_USB_ID 0x0401
-#define PDR_PRISM_PCI_ID 0x0402
-#define PDR_PRISM_PCI_IF_CONFIG 0x0403
-#define PDR_PRISM_PCI_PM_CONFIG 0x0404
-
-#define PDR_3861_MF_TEST_CHAN_SET_POINTS 0x0900
-#define PDR_3861_MF_TEST_CHAN_INTEGRATORS 0x0901
-
-/* ARM range (0x1000 - 0x1fff) */
-#define PDR_COUNTRY_INFORMATION 0x1000 /* obsolete */
-#define PDR_INTERFACE_LIST 0x1001
-#define PDR_HARDWARE_PLATFORM_COMPONENT_ID 0x1002
-#define PDR_OEM_NAME 0x1003
-#define PDR_PRODUCT_NAME 0x1004
-#define PDR_UTF8_OEM_NAME 0x1005
-#define PDR_UTF8_PRODUCT_NAME 0x1006
-#define PDR_COUNTRY_LIST 0x1007
-#define PDR_DEFAULT_COUNTRY 0x1008
-
-#define PDR_ANTENNA_GAIN 0x1100
-
-#define PDR_PRISM_INDIGO_PA_CALIBRATION_DATA 0x1901
-#define PDR_RSSI_LINEAR_APPROXIMATION 0x1902
-#define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS 0x1903
-#define PDR_PRISM_PA_CAL_CURVE_DATA 0x1904
-#define PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND 0x1905
-#define PDR_PRISM_ZIF_TX_IQ_CALIBRATION 0x1906
-#define PDR_REGULATORY_POWER_LIMITS 0x1907
-#define PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED 0x1908
-#define PDR_RADIATED_TRANSMISSION_CORRECTION 0x1909
-#define PDR_PRISM_TX_IQ_CALIBRATION 0x190a
-
-/* reserved range (0x2000 - 0x7fff) */
-
-/* customer range (0x8000 - 0xffff) */
-#define PDR_BASEBAND_REGISTERS 0x8000
-#define PDR_PER_CHANNEL_BASEBAND_REGISTERS 0x8001
-
-/* used by our modificated eeprom image */
-#define PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM 0xDEAD
-#define PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2 0xCAFF
-#define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM 0xBEEF
-#define PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM 0xB05D
-
-/* Interface Definitions */
-#define PDR_INTERFACE_ROLE_SERVER 0x0000
-#define PDR_INTERFACE_ROLE_CLIENT 0x0001
-
-/* PDR definitions for default country & country list */
-#define PDR_COUNTRY_CERT_CODE 0x80
-#define PDR_COUNTRY_CERT_CODE_REAL 0x00
-#define PDR_COUNTRY_CERT_CODE_PSEUDO 0x80
-#define PDR_COUNTRY_CERT_BAND 0x40
-#define PDR_COUNTRY_CERT_BAND_2GHZ 0x00
-#define PDR_COUNTRY_CERT_BAND_5GHZ 0x40
-#define PDR_COUNTRY_CERT_IODOOR 0x30
-#define PDR_COUNTRY_CERT_IODOOR_BOTH 0x00
-#define PDR_COUNTRY_CERT_IODOOR_INDOOR 0x20
-#define PDR_COUNTRY_CERT_IODOOR_OUTDOOR 0x30
-#define PDR_COUNTRY_CERT_INDEX 0x0f
-
-/* Specific LMAC FW/HW variant definitions */
-#define PDR_SYNTH_FRONTEND_MASK 0x0007
-#define PDR_SYNTH_FRONTEND_DUETTE3 0x0001
-#define PDR_SYNTH_FRONTEND_DUETTE2 0x0002
-#define PDR_SYNTH_FRONTEND_FRISBEE 0x0003
-#define PDR_SYNTH_FRONTEND_XBOW 0x0004
-#define PDR_SYNTH_FRONTEND_LONGBOW 0x0005
-#define PDR_SYNTH_IQ_CAL_MASK 0x0018
-#define PDR_SYNTH_IQ_CAL_PA_DETECTOR 0x0000
-#define PDR_SYNTH_IQ_CAL_DISABLED 0x0008
-#define PDR_SYNTH_IQ_CAL_ZIF 0x0010
-#define PDR_SYNTH_FAA_SWITCH_MASK 0x0020
-#define PDR_SYNTH_FAA_SWITCH_ENABLED 0x0020
-#define PDR_SYNTH_24_GHZ_MASK 0x0040
-#define PDR_SYNTH_24_GHZ_DISABLED 0x0040
-#define PDR_SYNTH_5_GHZ_MASK 0x0080
-#define PDR_SYNTH_5_GHZ_DISABLED 0x0080
-#define PDR_SYNTH_RX_DIV_MASK 0x0100
-#define PDR_SYNTH_RX_DIV_SUPPORTED 0x0100
-#define PDR_SYNTH_TX_DIV_MASK 0x0200
-#define PDR_SYNTH_TX_DIV_SUPPORTED 0x0200
-#define PDR_SYNTH_ASM_MASK 0x0400
-#define PDR_SYNTH_ASM_XSWON 0x0400
-
-#endif /* EEPROM_H */
+++ /dev/null
-/*
- * Firmware I/O code for mac80211 Prism54 drivers
- *
- * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
- * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
- * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
- *
- * Based on:
- * - the islsm (softmac prism54) driver, which is:
- * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
- * - stlc45xx driver
- * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/slab.h>
-#include <linux/firmware.h>
-#include <linux/etherdevice.h>
-#include <linux/export.h>
-
-#include <net/mac80211.h>
-
-#include "p54.h"
-#include "eeprom.h"
-#include "lmac.h"
-
-int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
-{
- struct p54_common *priv = dev->priv;
- struct exp_if *exp_if;
- struct bootrec *bootrec;
- u32 *data = (u32 *)fw->data;
- u32 *end_data = (u32 *)fw->data + (fw->size >> 2);
- u8 *fw_version = NULL;
- size_t len;
- int i;
- int maxlen;
-
- if (priv->rx_start)
- return 0;
-
- while (data < end_data && *data)
- data++;
-
- while (data < end_data && !*data)
- data++;
-
- bootrec = (struct bootrec *) data;
-
- while (bootrec->data <= end_data && (bootrec->data +
- (len = le32_to_cpu(bootrec->len))) <= end_data) {
- u32 code = le32_to_cpu(bootrec->code);
- switch (code) {
- case BR_CODE_COMPONENT_ID:
- priv->fw_interface = be32_to_cpup((__be32 *)
- bootrec->data);
- switch (priv->fw_interface) {
- case FW_LM86:
- case FW_LM20:
- case FW_LM87: {
- char *iftype = (char *)bootrec->data;
- wiphy_info(priv->hw->wiphy,
- "p54 detected a LM%c%c firmware\n",
- iftype[2], iftype[3]);
- break;
- }
- case FW_FMAC:
- default:
- wiphy_err(priv->hw->wiphy,
- "unsupported firmware\n");
- return -ENODEV;
- }
- break;
- case BR_CODE_COMPONENT_VERSION:
- /* 24 bytes should be enough for all firmwares */
- if (strnlen((unsigned char *) bootrec->data, 24) < 24)
- fw_version = (unsigned char *) bootrec->data;
- break;
- case BR_CODE_DESCR: {
- struct bootrec_desc *desc =
- (struct bootrec_desc *)bootrec->data;
- priv->rx_start = le32_to_cpu(desc->rx_start);
- /* FIXME add sanity checking */
- priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500;
- priv->headroom = desc->headroom;
- priv->tailroom = desc->tailroom;
- priv->privacy_caps = desc->privacy_caps;
- priv->rx_keycache_size = desc->rx_keycache_size;
- if (le32_to_cpu(bootrec->len) == 11)
- priv->rx_mtu = le16_to_cpu(desc->rx_mtu);
- else
- priv->rx_mtu = (size_t)
- 0x620 - priv->tx_hdr_len;
- maxlen = priv->tx_hdr_len + /* USB devices */
- sizeof(struct p54_rx_data) +
- 4 + /* rx alignment */
- IEEE80211_MAX_FRAG_THRESHOLD;
- if (priv->rx_mtu > maxlen && PAGE_SIZE == 4096) {
- printk(KERN_INFO "p54: rx_mtu reduced from %d "
- "to %d\n", priv->rx_mtu, maxlen);
- priv->rx_mtu = maxlen;
- }
- break;
- }
- case BR_CODE_EXPOSED_IF:
- exp_if = (struct exp_if *) bootrec->data;
- for (i = 0; i < (len * sizeof(*exp_if) / 4); i++)
- if (exp_if[i].if_id == cpu_to_le16(IF_ID_LMAC))
- priv->fw_var = le16_to_cpu(exp_if[i].variant);
- break;
- case BR_CODE_DEPENDENT_IF:
- break;
- case BR_CODE_END_OF_BRA:
- case LEGACY_BR_CODE_END_OF_BRA:
- end_data = NULL;
- break;
- default:
- break;
- }
- bootrec = (struct bootrec *)&bootrec->data[len];
- }
-
- if (fw_version) {
- wiphy_info(priv->hw->wiphy,
- "FW rev %s - Softmac protocol %x.%x\n",
- fw_version, priv->fw_var >> 8, priv->fw_var & 0xff);
- snprintf(dev->wiphy->fw_version, sizeof(dev->wiphy->fw_version),
- "%s - %x.%x", fw_version,
- priv->fw_var >> 8, priv->fw_var & 0xff);
- }
-
- if (priv->fw_var < 0x500)
- wiphy_info(priv->hw->wiphy,
- "you are using an obsolete firmware. "
- "visit http://wireless.kernel.org/en/users/Drivers/p54 "
- "and grab one for \"kernel >= 2.6.28\"!\n");
-
- if (priv->fw_var >= 0x300) {
- /* Firmware supports QoS, use it! */
-
- if (priv->fw_var >= 0x500) {
- priv->tx_stats[P54_QUEUE_AC_VO].limit = 16;
- priv->tx_stats[P54_QUEUE_AC_VI].limit = 16;
- priv->tx_stats[P54_QUEUE_AC_BE].limit = 16;
- priv->tx_stats[P54_QUEUE_AC_BK].limit = 16;
- } else {
- priv->tx_stats[P54_QUEUE_AC_VO].limit = 3;
- priv->tx_stats[P54_QUEUE_AC_VI].limit = 4;
- priv->tx_stats[P54_QUEUE_AC_BE].limit = 3;
- priv->tx_stats[P54_QUEUE_AC_BK].limit = 2;
- }
- priv->hw->queues = P54_QUEUE_AC_NUM;
- }
-
- wiphy_info(priv->hw->wiphy,
- "cryptographic accelerator WEP:%s, TKIP:%s, CCMP:%s\n",
- (priv->privacy_caps & BR_DESC_PRIV_CAP_WEP) ? "YES" : "no",
- (priv->privacy_caps &
- (BR_DESC_PRIV_CAP_TKIP | BR_DESC_PRIV_CAP_MICHAEL))
- ? "YES" : "no",
- (priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)
- ? "YES" : "no");
-
- if (priv->rx_keycache_size) {
- /*
- * NOTE:
- *
- * The firmware provides at most 255 (0 - 254) slots
- * for keys which are then used to offload decryption.
- * As a result the 255 entry (aka 0xff) can be used
- * safely by the driver to mark keys that didn't fit
- * into the full cache. This trick saves us from
- * keeping a extra list for uploaded keys.
- */
-
- priv->used_rxkeys = kzalloc(BITS_TO_LONGS(
- priv->rx_keycache_size), GFP_KERNEL);
-
- if (!priv->used_rxkeys)
- return -ENOMEM;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(p54_parse_firmware);
-
-static struct sk_buff *p54_alloc_skb(struct p54_common *priv, u16 hdr_flags,
- u16 payload_len, u16 type, gfp_t memflags)
-{
- struct p54_hdr *hdr;
- struct sk_buff *skb;
- size_t frame_len = sizeof(*hdr) + payload_len;
-
- if (frame_len > P54_MAX_CTRL_FRAME_LEN)
- return NULL;
-
- if (unlikely(skb_queue_len(&priv->tx_pending) > 64))
- return NULL;
-
- skb = __dev_alloc_skb(priv->tx_hdr_len + frame_len, memflags);
- if (!skb)
- return NULL;
- skb_reserve(skb, priv->tx_hdr_len);
-
- hdr = (struct p54_hdr *) skb_put(skb, sizeof(*hdr));
- hdr->flags = cpu_to_le16(hdr_flags);
- hdr->len = cpu_to_le16(payload_len);
- hdr->type = cpu_to_le16(type);
- hdr->tries = hdr->rts_tries = 0;
- return skb;
-}
-
-int p54_download_eeprom(struct p54_common *priv, void *buf,
- u16 offset, u16 len)
-{
- struct p54_eeprom_lm86 *eeprom_hdr;
- struct sk_buff *skb;
- size_t eeprom_hdr_size;
- int ret = 0;
- long timeout;
-
- if (priv->fw_var >= 0x509)
- eeprom_hdr_size = sizeof(*eeprom_hdr);
- else
- eeprom_hdr_size = 0x4;
-
- skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL, eeprom_hdr_size +
- len, P54_CONTROL_TYPE_EEPROM_READBACK,
- GFP_KERNEL);
- if (unlikely(!skb))
- return -ENOMEM;
-
- mutex_lock(&priv->eeprom_mutex);
- priv->eeprom = buf;
- eeprom_hdr = (struct p54_eeprom_lm86 *) skb_put(skb,
- eeprom_hdr_size + len);
-
- if (priv->fw_var < 0x509) {
- eeprom_hdr->v1.offset = cpu_to_le16(offset);
- eeprom_hdr->v1.len = cpu_to_le16(len);
- } else {
- eeprom_hdr->v2.offset = cpu_to_le32(offset);
- eeprom_hdr->v2.len = cpu_to_le16(len);
- eeprom_hdr->v2.magic2 = 0xf;
- memcpy(eeprom_hdr->v2.magic, (const char *)"LOCK", 4);
- }
-
- p54_tx(priv, skb);
-
- timeout = wait_for_completion_interruptible_timeout(
- &priv->eeprom_comp, HZ);
- if (timeout <= 0) {
- wiphy_err(priv->hw->wiphy,
- "device does not respond or signal received!\n");
- ret = -EBUSY;
- }
- priv->eeprom = NULL;
- mutex_unlock(&priv->eeprom_mutex);
- return ret;
-}
-
-int p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set)
-{
- struct sk_buff *skb;
- struct p54_tim *tim;
-
- skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*tim),
- P54_CONTROL_TYPE_TIM, GFP_ATOMIC);
- if (unlikely(!skb))
- return -ENOMEM;
-
- tim = (struct p54_tim *) skb_put(skb, sizeof(*tim));
- tim->count = 1;
- tim->entry[0] = cpu_to_le16(set ? (aid | 0x8000) : aid);
- p54_tx(priv, skb);
- return 0;
-}
-
-int p54_sta_unlock(struct p54_common *priv, u8 *addr)
-{
- struct sk_buff *skb;
- struct p54_sta_unlock *sta;
-
- skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*sta),
- P54_CONTROL_TYPE_PSM_STA_UNLOCK, GFP_ATOMIC);
- if (unlikely(!skb))
- return -ENOMEM;
-
- sta = (struct p54_sta_unlock *)skb_put(skb, sizeof(*sta));
- memcpy(sta->addr, addr, ETH_ALEN);
- p54_tx(priv, skb);
- return 0;
-}
-
-int p54_tx_cancel(struct p54_common *priv, __le32 req_id)
-{
- struct sk_buff *skb;
- struct p54_txcancel *cancel;
- u32 _req_id = le32_to_cpu(req_id);
-
- if (unlikely(_req_id < priv->rx_start || _req_id > priv->rx_end))
- return -EINVAL;
-
- skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*cancel),
- P54_CONTROL_TYPE_TXCANCEL, GFP_ATOMIC);
- if (unlikely(!skb))
- return -ENOMEM;
-
- cancel = (struct p54_txcancel *)skb_put(skb, sizeof(*cancel));
- cancel->req_id = req_id;
- p54_tx(priv, skb);
- return 0;
-}
-
-int p54_setup_mac(struct p54_common *priv)
-{
- struct sk_buff *skb;
- struct p54_setup_mac *setup;
- u16 mode;
-
- skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*setup),
- P54_CONTROL_TYPE_SETUP, GFP_ATOMIC);
- if (!skb)
- return -ENOMEM;
-
- setup = (struct p54_setup_mac *) skb_put(skb, sizeof(*setup));
- if (!(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) {
- switch (priv->mode) {
- case NL80211_IFTYPE_STATION:
- mode = P54_FILTER_TYPE_STATION;
- break;
- case NL80211_IFTYPE_AP:
- mode = P54_FILTER_TYPE_AP;
- break;
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
- mode = P54_FILTER_TYPE_IBSS;
- break;
- case NL80211_IFTYPE_MONITOR:
- mode = P54_FILTER_TYPE_PROMISCUOUS;
- break;
- default:
- mode = P54_FILTER_TYPE_HIBERNATE;
- break;
- }
-
- /*
- * "TRANSPARENT and PROMISCUOUS are mutually exclusive"
- * STSW45X0C LMAC API - page 12
- */
- if (priv->filter_flags & FIF_OTHER_BSS &&
- (mode != P54_FILTER_TYPE_PROMISCUOUS))
- mode |= P54_FILTER_TYPE_TRANSPARENT;
- } else {
- mode = P54_FILTER_TYPE_HIBERNATE;
- }
-
- setup->mac_mode = cpu_to_le16(mode);
- memcpy(setup->mac_addr, priv->mac_addr, ETH_ALEN);
- memcpy(setup->bssid, priv->bssid, ETH_ALEN);
- setup->rx_antenna = 2 & priv->rx_diversity_mask; /* automatic */
- setup->rx_align = 0;
- if (priv->fw_var < 0x500) {
- setup->v1.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);
- memset(setup->v1.rts_rates, 0, 8);
- setup->v1.rx_addr = cpu_to_le32(priv->rx_end);
- setup->v1.max_rx = cpu_to_le16(priv->rx_mtu);
- setup->v1.rxhw = cpu_to_le16(priv->rxhw);
- setup->v1.wakeup_timer = cpu_to_le16(priv->wakeup_timer);
- setup->v1.unalloc0 = cpu_to_le16(0);
- } else {
- setup->v2.rx_addr = cpu_to_le32(priv->rx_end);
- setup->v2.max_rx = cpu_to_le16(priv->rx_mtu);
- setup->v2.rxhw = cpu_to_le16(priv->rxhw);
- setup->v2.timer = cpu_to_le16(priv->wakeup_timer);
- setup->v2.truncate = cpu_to_le16(48896);
- setup->v2.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);
- setup->v2.sbss_offset = 0;
- setup->v2.mcast_window = 0;
- setup->v2.rx_rssi_threshold = 0;
- setup->v2.rx_ed_threshold = 0;
- setup->v2.ref_clock = cpu_to_le32(644245094);
- setup->v2.lpf_bandwidth = cpu_to_le16(65535);
- setup->v2.osc_start_delay = cpu_to_le16(65535);
- }
- p54_tx(priv, skb);
- priv->phy_idle = mode == P54_FILTER_TYPE_HIBERNATE;
- return 0;
-}
-
-int p54_scan(struct p54_common *priv, u16 mode, u16 dwell)
-{
- struct sk_buff *skb;
- struct p54_hdr *hdr;
- struct p54_scan_head *head;
- struct p54_iq_autocal_entry *iq_autocal;
- union p54_scan_body_union *body;
- struct p54_scan_tail_rate *rate;
- struct pda_rssi_cal_entry *rssi;
- struct p54_rssi_db_entry *rssi_data;
- unsigned int i;
- void *entry;
- __le16 freq = cpu_to_le16(priv->hw->conf.chandef.chan->center_freq);
-
- skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*head) +
- 2 + sizeof(*iq_autocal) + sizeof(*body) +
- sizeof(*rate) + 2 * sizeof(*rssi),
- P54_CONTROL_TYPE_SCAN, GFP_ATOMIC);
- if (!skb)
- return -ENOMEM;
-
- head = (struct p54_scan_head *) skb_put(skb, sizeof(*head));
- memset(head->scan_params, 0, sizeof(head->scan_params));
- head->mode = cpu_to_le16(mode);
- head->dwell = cpu_to_le16(dwell);
- head->freq = freq;
-
- if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {
- __le16 *pa_power_points = (__le16 *) skb_put(skb, 2);
- *pa_power_points = cpu_to_le16(0x0c);
- }
-
- iq_autocal = (void *) skb_put(skb, sizeof(*iq_autocal));
- for (i = 0; i < priv->iq_autocal_len; i++) {
- if (priv->iq_autocal[i].freq != freq)
- continue;
-
- memcpy(iq_autocal, &priv->iq_autocal[i].params,
- sizeof(struct p54_iq_autocal_entry));
- break;
- }
- if (i == priv->iq_autocal_len)
- goto err;
-
- if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW)
- body = (void *) skb_put(skb, sizeof(body->longbow));
- else
- body = (void *) skb_put(skb, sizeof(body->normal));
-
- for (i = 0; i < priv->output_limit->entries; i++) {
- __le16 *entry_freq = (void *) (priv->output_limit->data +
- priv->output_limit->entry_size * i);
-
- if (*entry_freq != freq)
- continue;
-
- if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {
- memcpy(&body->longbow.power_limits,
- (void *) entry_freq + sizeof(__le16),
- priv->output_limit->entry_size);
- } else {
- struct pda_channel_output_limit *limits =
- (void *) entry_freq;
-
- body->normal.val_barker = 0x38;
- body->normal.val_bpsk = body->normal.dup_bpsk =
- limits->val_bpsk;
- body->normal.val_qpsk = body->normal.dup_qpsk =
- limits->val_qpsk;
- body->normal.val_16qam = body->normal.dup_16qam =
- limits->val_16qam;
- body->normal.val_64qam = body->normal.dup_64qam =
- limits->val_64qam;
- }
- break;
- }
- if (i == priv->output_limit->entries)
- goto err;
-
- entry = (void *)(priv->curve_data->data + priv->curve_data->offset);
- for (i = 0; i < priv->curve_data->entries; i++) {
- if (*((__le16 *)entry) != freq) {
- entry += priv->curve_data->entry_size;
- continue;
- }
-
- if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {
- memcpy(&body->longbow.curve_data,
- entry + sizeof(__le16),
- priv->curve_data->entry_size);
- } else {
- struct p54_scan_body *chan = &body->normal;
- struct pda_pa_curve_data *curve_data =
- (void *) priv->curve_data->data;
-
- entry += sizeof(__le16);
- chan->pa_points_per_curve = 8;
- memset(chan->curve_data, 0, sizeof(*chan->curve_data));
- memcpy(chan->curve_data, entry,
- sizeof(struct p54_pa_curve_data_sample) *
- min((u8)8, curve_data->points_per_channel));
- }
- break;
- }
- if (i == priv->curve_data->entries)
- goto err;
-
- if ((priv->fw_var >= 0x500) && (priv->fw_var < 0x509)) {
- rate = (void *) skb_put(skb, sizeof(*rate));
- rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);
- for (i = 0; i < sizeof(rate->rts_rates); i++)
- rate->rts_rates[i] = i;
- }
-
- rssi = (struct pda_rssi_cal_entry *) skb_put(skb, sizeof(*rssi));
- rssi_data = p54_rssi_find(priv, le16_to_cpu(freq));
- rssi->mul = cpu_to_le16(rssi_data->mul);
- rssi->add = cpu_to_le16(rssi_data->add);
- if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {
- /* Longbow frontend needs ever more */
- rssi = (void *) skb_put(skb, sizeof(*rssi));
- rssi->mul = cpu_to_le16(rssi_data->longbow_unkn);
- rssi->add = cpu_to_le16(rssi_data->longbow_unk2);
- }
-
- if (priv->fw_var >= 0x509) {
- rate = (void *) skb_put(skb, sizeof(*rate));
- rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);
- for (i = 0; i < sizeof(rate->rts_rates); i++)
- rate->rts_rates[i] = i;
- }
-
- hdr = (struct p54_hdr *) skb->data;
- hdr->len = cpu_to_le16(skb->len - sizeof(*hdr));
-
- p54_tx(priv, skb);
- priv->cur_rssi = rssi_data;
- return 0;
-
-err:
- wiphy_err(priv->hw->wiphy, "frequency change to channel %d failed.\n",
- ieee80211_frequency_to_channel(
- priv->hw->conf.chandef.chan->center_freq));
-
- dev_kfree_skb_any(skb);
- return -EINVAL;
-}
-
-int p54_set_leds(struct p54_common *priv)
-{
- struct sk_buff *skb;
- struct p54_led *led;
-
- skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*led),
- P54_CONTROL_TYPE_LED, GFP_ATOMIC);
- if (unlikely(!skb))
- return -ENOMEM;
-
- led = (struct p54_led *) skb_put(skb, sizeof(*led));
- led->flags = cpu_to_le16(0x0003);
- led->mask[0] = led->mask[1] = cpu_to_le16(priv->softled_state);
- led->delay[0] = cpu_to_le16(1);
- led->delay[1] = cpu_to_le16(0);
- p54_tx(priv, skb);
- return 0;
-}
-
-int p54_set_edcf(struct p54_common *priv)
-{
- struct sk_buff *skb;
- struct p54_edcf *edcf;
- u8 rtd;
-
- skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*edcf),
- P54_CONTROL_TYPE_DCFINIT, GFP_ATOMIC);
- if (unlikely(!skb))
- return -ENOMEM;
-
- edcf = (struct p54_edcf *)skb_put(skb, sizeof(*edcf));
- if (priv->use_short_slot) {
- edcf->slottime = 9;
- edcf->sifs = 0x10;
- edcf->eofpad = 0x00;
- } else {
- edcf->slottime = 20;
- edcf->sifs = 0x0a;
- edcf->eofpad = 0x06;
- }
- /*
- * calculate the extra round trip delay according to the
- * formula from 802.11-2007 17.3.8.6.
- */
- rtd = 3 * priv->coverage_class;
- edcf->slottime += rtd;
- edcf->round_trip_delay = cpu_to_le16(rtd);
- /* (see prism54/isl_oid.h for further details) */
- edcf->frameburst = cpu_to_le16(0);
- edcf->flags = 0;
- memset(edcf->mapping, 0, sizeof(edcf->mapping));
- memcpy(edcf->queue, priv->qos_params, sizeof(edcf->queue));
- p54_tx(priv, skb);
- return 0;
-}
-
-int p54_set_ps(struct p54_common *priv)
-{
- struct sk_buff *skb;
- struct p54_psm *psm;
- unsigned int i;
- u16 mode;
-
- if (priv->hw->conf.flags & IEEE80211_CONF_PS &&
- !priv->powersave_override)
- mode = P54_PSM | P54_PSM_BEACON_TIMEOUT | P54_PSM_DTIM |
- P54_PSM_CHECKSUM | P54_PSM_MCBC;
- else
- mode = P54_PSM_CAM;
-
- skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*psm),
- P54_CONTROL_TYPE_PSM, GFP_ATOMIC);
- if (!skb)
- return -ENOMEM;
-
- psm = (struct p54_psm *)skb_put(skb, sizeof(*psm));
- psm->mode = cpu_to_le16(mode);
- psm->aid = cpu_to_le16(priv->aid);
- for (i = 0; i < ARRAY_SIZE(psm->intervals); i++) {
- psm->intervals[i].interval =
- cpu_to_le16(priv->hw->conf.listen_interval);
- psm->intervals[i].periods = cpu_to_le16(1);
- }
-
- psm->beacon_rssi_skip_max = 200;
- psm->rssi_delta_threshold = 0;
- psm->nr = 1;
- psm->exclude[0] = WLAN_EID_TIM;
-
- p54_tx(priv, skb);
- priv->phy_ps = mode != P54_PSM_CAM;
- return 0;
-}
-
-int p54_init_xbow_synth(struct p54_common *priv)
-{
- struct sk_buff *skb;
- struct p54_xbow_synth *xbow;
-
- skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*xbow),
- P54_CONTROL_TYPE_XBOW_SYNTH_CFG, GFP_KERNEL);
- if (unlikely(!skb))
- return -ENOMEM;
-
- xbow = (struct p54_xbow_synth *)skb_put(skb, sizeof(*xbow));
- xbow->magic1 = cpu_to_le16(0x1);
- xbow->magic2 = cpu_to_le16(0x2);
- xbow->freq = cpu_to_le16(5390);
- memset(xbow->padding, 0, sizeof(xbow->padding));
- p54_tx(priv, skb);
- return 0;
-}
-
-int p54_upload_key(struct p54_common *priv, u8 algo, int slot, u8 idx, u8 len,
- u8 *addr, u8* key)
-{
- struct sk_buff *skb;
- struct p54_keycache *rxkey;
-
- skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey),
- P54_CONTROL_TYPE_RX_KEYCACHE, GFP_KERNEL);
- if (unlikely(!skb))
- return -ENOMEM;
-
- rxkey = (struct p54_keycache *)skb_put(skb, sizeof(*rxkey));
- rxkey->entry = slot;
- rxkey->key_id = idx;
- rxkey->key_type = algo;
- if (addr)
- memcpy(rxkey->mac, addr, ETH_ALEN);
- else
- eth_broadcast_addr(rxkey->mac);
-
- switch (algo) {
- case P54_CRYPTO_WEP:
- case P54_CRYPTO_AESCCMP:
- rxkey->key_len = min_t(u8, 16, len);
- memcpy(rxkey->key, key, rxkey->key_len);
- break;
-
- case P54_CRYPTO_TKIPMICHAEL:
- rxkey->key_len = 24;
- memcpy(rxkey->key, key, 16);
- memcpy(&(rxkey->key[16]), &(key
- [NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]), 8);
- break;
-
- case P54_CRYPTO_NONE:
- rxkey->key_len = 0;
- memset(rxkey->key, 0, sizeof(rxkey->key));
- break;
-
- default:
- wiphy_err(priv->hw->wiphy,
- "invalid cryptographic algorithm: %d\n", algo);
- dev_kfree_skb(skb);
- return -EINVAL;
- }
-
- p54_tx(priv, skb);
- return 0;
-}
-
-int p54_fetch_statistics(struct p54_common *priv)
-{
- struct ieee80211_tx_info *txinfo;
- struct p54_tx_info *p54info;
- struct sk_buff *skb;
-
- skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL,
- sizeof(struct p54_statistics),
- P54_CONTROL_TYPE_STAT_READBACK, GFP_KERNEL);
- if (!skb)
- return -ENOMEM;
-
- /*
- * The statistic feedback causes some extra headaches here, if it
- * is not to crash/corrupt the firmware data structures.
- *
- * Unlike all other Control Get OIDs we can not use helpers like
- * skb_put to reserve the space for the data we're requesting.
- * Instead the extra frame length -which will hold the results later-
- * will only be told to the p54_assign_address, so that following
- * frames won't be placed into the allegedly empty area.
- */
- txinfo = IEEE80211_SKB_CB(skb);
- p54info = (void *) txinfo->rate_driver_data;
- p54info->extra_len = sizeof(struct p54_statistics);
-
- p54_tx(priv, skb);
- return 0;
-}
-
-int p54_set_groupfilter(struct p54_common *priv)
-{
- struct p54_group_address_table *grp;
- struct sk_buff *skb;
- bool on = false;
-
- skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*grp),
- P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE, GFP_KERNEL);
- if (!skb)
- return -ENOMEM;
-
- grp = (struct p54_group_address_table *)skb_put(skb, sizeof(*grp));
-
- on = !(priv->filter_flags & FIF_ALLMULTI) &&
- (priv->mc_maclist_num > 0 &&
- priv->mc_maclist_num <= MC_FILTER_ADDRESS_NUM);
-
- if (on) {
- grp->filter_enable = cpu_to_le16(1);
- grp->num_address = cpu_to_le16(priv->mc_maclist_num);
- memcpy(grp->mac_list, priv->mc_maclist, sizeof(grp->mac_list));
- } else {
- grp->filter_enable = cpu_to_le16(0);
- grp->num_address = cpu_to_le16(0);
- memset(grp->mac_list, 0, sizeof(grp->mac_list));
- }
-
- p54_tx(priv, skb);
- return 0;
-}
+++ /dev/null
-/*
- * Common code for mac80211 Prism54 drivers
- *
- * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
- * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
- * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
- *
- * Based on:
- * - the islsm (softmac prism54) driver, which is:
- * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
- * - stlc45xx driver
- * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/firmware.h>
-#include <linux/etherdevice.h>
-
-#include <net/mac80211.h>
-#ifdef CONFIG_P54_LEDS
-#include <linux/leds.h>
-#endif /* CONFIG_P54_LEDS */
-
-#include "p54.h"
-#include "lmac.h"
-
-static void p54_update_leds(struct work_struct *work)
-{
- struct p54_common *priv = container_of(work, struct p54_common,
- led_work.work);
- int err, i, tmp, blink_delay = 400;
- bool rerun = false;
-
- /* Don't toggle the LED, when the device is down. */
- if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
- return ;
-
- for (i = 0; i < ARRAY_SIZE(priv->leds); i++)
- if (priv->leds[i].toggled) {
- priv->softled_state |= BIT(i);
-
- tmp = 70 + 200 / (priv->leds[i].toggled);
- if (tmp < blink_delay)
- blink_delay = tmp;
-
- if (priv->leds[i].led_dev.brightness == LED_OFF)
- rerun = true;
-
- priv->leds[i].toggled =
- !!priv->leds[i].led_dev.brightness;
- } else
- priv->softled_state &= ~BIT(i);
-
- err = p54_set_leds(priv);
- if (err && net_ratelimit())
- wiphy_err(priv->hw->wiphy,
- "failed to update LEDs (%d).\n", err);
-
- if (rerun)
- ieee80211_queue_delayed_work(priv->hw, &priv->led_work,
- msecs_to_jiffies(blink_delay));
-}
-
-static void p54_led_brightness_set(struct led_classdev *led_dev,
- enum led_brightness brightness)
-{
- struct p54_led_dev *led = container_of(led_dev, struct p54_led_dev,
- led_dev);
- struct ieee80211_hw *dev = led->hw_dev;
- struct p54_common *priv = dev->priv;
-
- if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
- return ;
-
- if ((brightness) && (led->registered)) {
- led->toggled++;
- ieee80211_queue_delayed_work(priv->hw, &priv->led_work, HZ/10);
- }
-}
-
-static int p54_register_led(struct p54_common *priv,
- unsigned int led_index,
- char *name, const char *trigger)
-{
- struct p54_led_dev *led = &priv->leds[led_index];
- int err;
-
- if (led->registered)
- return -EEXIST;
-
- snprintf(led->name, sizeof(led->name), "p54-%s::%s",
- wiphy_name(priv->hw->wiphy), name);
- led->hw_dev = priv->hw;
- led->index = led_index;
- led->led_dev.name = led->name;
- led->led_dev.default_trigger = trigger;
- led->led_dev.brightness_set = p54_led_brightness_set;
-
- err = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_dev);
- if (err)
- wiphy_err(priv->hw->wiphy,
- "Failed to register %s LED.\n", name);
- else
- led->registered = 1;
-
- return err;
-}
-
-int p54_init_leds(struct p54_common *priv)
-{
- int err;
-
- /*
- * TODO:
- * Figure out if the EEPROM contains some hints about the number
- * of available/programmable LEDs of the device.
- */
-
- INIT_DELAYED_WORK(&priv->led_work, p54_update_leds);
-
- err = p54_register_led(priv, 0, "assoc",
- ieee80211_get_assoc_led_name(priv->hw));
- if (err)
- return err;
-
- err = p54_register_led(priv, 1, "tx",
- ieee80211_get_tx_led_name(priv->hw));
- if (err)
- return err;
-
- err = p54_register_led(priv, 2, "rx",
- ieee80211_get_rx_led_name(priv->hw));
- if (err)
- return err;
-
- err = p54_register_led(priv, 3, "radio",
- ieee80211_get_radio_led_name(priv->hw));
- if (err)
- return err;
-
- err = p54_set_leds(priv);
- return err;
-}
-
-void p54_unregister_leds(struct p54_common *priv)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(priv->leds); i++) {
- if (priv->leds[i].registered) {
- priv->leds[i].registered = false;
- priv->leds[i].toggled = 0;
- led_classdev_unregister(&priv->leds[i].led_dev);
- }
- }
-
- cancel_delayed_work_sync(&priv->led_work);
-}
+++ /dev/null
-/*
- * LMAC Interface specific definitions for mac80211 Prism54 drivers
- *
- * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
- * Copyright (c) 2007 - 2009, Christian Lamparter <chunkeey@web.de>
- *
- * Based on:
- * - the islsm (softmac prism54) driver, which is:
- * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
- *
- * - LMAC API interface header file for STLC4560 (lmac_longbow.h)
- * Copyright (C) 2007 Conexant Systems, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef LMAC_H
-#define LMAC_H
-
-enum p54_control_frame_types {
- P54_CONTROL_TYPE_SETUP = 0,
- P54_CONTROL_TYPE_SCAN,
- P54_CONTROL_TYPE_TRAP,
- P54_CONTROL_TYPE_DCFINIT,
- P54_CONTROL_TYPE_RX_KEYCACHE,
- P54_CONTROL_TYPE_TIM,
- P54_CONTROL_TYPE_PSM,
- P54_CONTROL_TYPE_TXCANCEL,
- P54_CONTROL_TYPE_TXDONE,
- P54_CONTROL_TYPE_BURST,
- P54_CONTROL_TYPE_STAT_READBACK,
- P54_CONTROL_TYPE_BBP,
- P54_CONTROL_TYPE_EEPROM_READBACK,
- P54_CONTROL_TYPE_LED,
- P54_CONTROL_TYPE_GPIO,
- P54_CONTROL_TYPE_TIMER,
- P54_CONTROL_TYPE_MODULATION,
- P54_CONTROL_TYPE_SYNTH_CONFIG,
- P54_CONTROL_TYPE_DETECTOR_VALUE,
- P54_CONTROL_TYPE_XBOW_SYNTH_CFG,
- P54_CONTROL_TYPE_CCE_QUIET,
- P54_CONTROL_TYPE_PSM_STA_UNLOCK,
- P54_CONTROL_TYPE_PCS,
- P54_CONTROL_TYPE_BT_BALANCER = 28,
- P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE = 30,
- P54_CONTROL_TYPE_ARPTABLE = 31,
- P54_CONTROL_TYPE_BT_OPTIONS = 35,
-};
-
-#define P54_HDR_FLAG_CONTROL BIT(15)
-#define P54_HDR_FLAG_CONTROL_OPSET (BIT(15) + BIT(0))
-#define P54_HDR_FLAG_DATA_ALIGN BIT(14)
-
-#define P54_HDR_FLAG_DATA_OUT_PROMISC BIT(0)
-#define P54_HDR_FLAG_DATA_OUT_TIMESTAMP BIT(1)
-#define P54_HDR_FLAG_DATA_OUT_SEQNR BIT(2)
-#define P54_HDR_FLAG_DATA_OUT_BIT3 BIT(3)
-#define P54_HDR_FLAG_DATA_OUT_BURST BIT(4)
-#define P54_HDR_FLAG_DATA_OUT_NOCANCEL BIT(5)
-#define P54_HDR_FLAG_DATA_OUT_CLEARTIM BIT(6)
-#define P54_HDR_FLAG_DATA_OUT_HITCHHIKE BIT(7)
-#define P54_HDR_FLAG_DATA_OUT_COMPRESS BIT(8)
-#define P54_HDR_FLAG_DATA_OUT_CONCAT BIT(9)
-#define P54_HDR_FLAG_DATA_OUT_PCS_ACCEPT BIT(10)
-#define P54_HDR_FLAG_DATA_OUT_WAITEOSP BIT(11)
-
-#define P54_HDR_FLAG_DATA_IN_FCS_GOOD BIT(0)
-#define P54_HDR_FLAG_DATA_IN_MATCH_MAC BIT(1)
-#define P54_HDR_FLAG_DATA_IN_MCBC BIT(2)
-#define P54_HDR_FLAG_DATA_IN_BEACON BIT(3)
-#define P54_HDR_FLAG_DATA_IN_MATCH_BSS BIT(4)
-#define P54_HDR_FLAG_DATA_IN_BCAST_BSS BIT(5)
-#define P54_HDR_FLAG_DATA_IN_DATA BIT(6)
-#define P54_HDR_FLAG_DATA_IN_TRUNCATED BIT(7)
-#define P54_HDR_FLAG_DATA_IN_BIT8 BIT(8)
-#define P54_HDR_FLAG_DATA_IN_TRANSPARENT BIT(9)
-
-struct p54_hdr {
- __le16 flags;
- __le16 len;
- __le32 req_id;
- __le16 type; /* enum p54_control_frame_types */
- u8 rts_tries;
- u8 tries;
- u8 data[0];
-} __packed;
-
-#define GET_REQ_ID(skb) \
- (((struct p54_hdr *) ((struct sk_buff *) skb)->data)->req_id) \
-
-#define FREE_AFTER_TX(skb) \
- ((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \
- flags) == cpu_to_le16(P54_HDR_FLAG_CONTROL_OPSET))
-
-#define IS_DATA_FRAME(skb) \
- (!((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \
- flags) & cpu_to_le16(P54_HDR_FLAG_CONTROL)))
-
-#define GET_HW_QUEUE(skb) \
- (((struct p54_tx_data *)((struct p54_hdr *) \
- skb->data)->data)->hw_queue)
-
-/*
- * shared interface ID definitions
- * The interface ID is a unique identification of a specific interface.
- * The following values are reserved: 0x0000, 0x0002, 0x0012, 0x0014, 0x0015
- */
-#define IF_ID_ISL36356A 0x0001 /* ISL36356A <-> Firmware */
-#define IF_ID_MVC 0x0003 /* MAC Virtual Coprocessor */
-#define IF_ID_DEBUG 0x0008 /* PolDebug Interface */
-#define IF_ID_PRODUCT 0x0009
-#define IF_ID_OEM 0x000a
-#define IF_ID_PCI3877 0x000b /* 3877 <-> Host PCI */
-#define IF_ID_ISL37704C 0x000c /* ISL37704C <-> Fw */
-#define IF_ID_ISL39000 0x000f /* ISL39000 <-> Fw */
-#define IF_ID_ISL39300A 0x0010 /* ISL39300A <-> Fw */
-#define IF_ID_ISL37700_UAP 0x0016 /* ISL37700 uAP Fw <-> Fw */
-#define IF_ID_ISL39000_UAP 0x0017 /* ISL39000 uAP Fw <-> Fw */
-#define IF_ID_LMAC 0x001a /* Interface exposed by LMAC */
-
-struct exp_if {
- __le16 role;
- __le16 if_id;
- __le16 variant;
- __le16 btm_compat;
- __le16 top_compat;
-} __packed;
-
-struct dep_if {
- __le16 role;
- __le16 if_id;
- __le16 variant;
-} __packed;
-
-/* driver <-> lmac definitions */
-struct p54_eeprom_lm86 {
- union {
- struct {
- __le16 offset;
- __le16 len;
- u8 data[0];
- } __packed v1;
- struct {
- __le32 offset;
- __le16 len;
- u8 magic2;
- u8 pad;
- u8 magic[4];
- u8 data[0];
- } __packed v2;
- } __packed;
-} __packed;
-
-enum p54_rx_decrypt_status {
- P54_DECRYPT_NONE = 0,
- P54_DECRYPT_OK,
- P54_DECRYPT_NOKEY,
- P54_DECRYPT_NOMICHAEL,
- P54_DECRYPT_NOCKIPMIC,
- P54_DECRYPT_FAIL_WEP,
- P54_DECRYPT_FAIL_TKIP,
- P54_DECRYPT_FAIL_MICHAEL,
- P54_DECRYPT_FAIL_CKIPKP,
- P54_DECRYPT_FAIL_CKIPMIC,
- P54_DECRYPT_FAIL_AESCCMP
-};
-
-struct p54_rx_data {
- __le16 flags;
- __le16 len;
- __le16 freq;
- u8 antenna;
- u8 rate;
- u8 rssi;
- u8 quality;
- u8 decrypt_status;
- u8 rssi_raw;
- __le32 tsf32;
- __le32 unalloc0;
- u8 align[0];
-} __packed;
-
-enum p54_trap_type {
- P54_TRAP_SCAN = 0,
- P54_TRAP_TIMER,
- P54_TRAP_BEACON_TX,
- P54_TRAP_FAA_RADIO_ON,
- P54_TRAP_FAA_RADIO_OFF,
- P54_TRAP_RADAR,
- P54_TRAP_NO_BEACON,
- P54_TRAP_TBTT,
- P54_TRAP_SCO_ENTER,
- P54_TRAP_SCO_EXIT
-};
-
-struct p54_trap {
- __le16 event;
- __le16 frequency;
-} __packed;
-
-enum p54_frame_sent_status {
- P54_TX_OK = 0,
- P54_TX_FAILED,
- P54_TX_PSM,
- P54_TX_PSM_CANCELLED = 4
-};
-
-struct p54_frame_sent {
- u8 status;
- u8 tries;
- u8 ack_rssi;
- u8 quality;
- __le16 seq;
- u8 antenna;
- u8 padding;
-} __packed;
-
-enum p54_tx_data_crypt {
- P54_CRYPTO_NONE = 0,
- P54_CRYPTO_WEP,
- P54_CRYPTO_TKIP,
- P54_CRYPTO_TKIPMICHAEL,
- P54_CRYPTO_CCX_WEPMIC,
- P54_CRYPTO_CCX_KPMIC,
- P54_CRYPTO_CCX_KP,
- P54_CRYPTO_AESCCMP
-};
-
-enum p54_tx_data_queue {
- P54_QUEUE_BEACON = 0,
- P54_QUEUE_FWSCAN = 1,
- P54_QUEUE_MGMT = 2,
- P54_QUEUE_CAB = 3,
- P54_QUEUE_DATA = 4,
-
- P54_QUEUE_AC_NUM = 4,
- P54_QUEUE_AC_VO = 4,
- P54_QUEUE_AC_VI = 5,
- P54_QUEUE_AC_BE = 6,
- P54_QUEUE_AC_BK = 7,
-
- /* keep last */
- P54_QUEUE_NUM = 8,
-};
-
-#define IS_QOS_QUEUE(n) (n >= P54_QUEUE_DATA)
-
-struct p54_tx_data {
- u8 rateset[8];
- u8 rts_rate_idx;
- u8 crypt_offset;
- u8 key_type;
- u8 key_len;
- u8 key[16];
- u8 hw_queue;
- u8 backlog;
- __le16 durations[4];
- u8 tx_antenna;
- union {
- struct {
- u8 cts_rate;
- __le16 output_power;
- } __packed longbow;
- struct {
- u8 output_power;
- u8 cts_rate;
- u8 unalloc;
- } __packed normal;
- } __packed;
- u8 unalloc2[2];
- u8 align[0];
-} __packed;
-
-/* unit is ms */
-#define P54_TX_FRAME_LIFETIME 2000
-#define P54_TX_TIMEOUT 4000
-#define P54_STATISTICS_UPDATE 5000
-
-#define P54_FILTER_TYPE_NONE 0
-#define P54_FILTER_TYPE_STATION BIT(0)
-#define P54_FILTER_TYPE_IBSS BIT(1)
-#define P54_FILTER_TYPE_AP BIT(2)
-#define P54_FILTER_TYPE_TRANSPARENT BIT(3)
-#define P54_FILTER_TYPE_PROMISCUOUS BIT(4)
-#define P54_FILTER_TYPE_HIBERNATE BIT(5)
-#define P54_FILTER_TYPE_NOACK BIT(6)
-#define P54_FILTER_TYPE_RX_DISABLED BIT(7)
-
-struct p54_setup_mac {
- __le16 mac_mode;
- u8 mac_addr[ETH_ALEN];
- u8 bssid[ETH_ALEN];
- u8 rx_antenna;
- u8 rx_align;
- union {
- struct {
- __le32 basic_rate_mask;
- u8 rts_rates[8];
- __le32 rx_addr;
- __le16 max_rx;
- __le16 rxhw;
- __le16 wakeup_timer;
- __le16 unalloc0;
- } __packed v1;
- struct {
- __le32 rx_addr;
- __le16 max_rx;
- __le16 rxhw;
- __le16 timer;
- __le16 truncate;
- __le32 basic_rate_mask;
- u8 sbss_offset;
- u8 mcast_window;
- u8 rx_rssi_threshold;
- u8 rx_ed_threshold;
- __le32 ref_clock;
- __le16 lpf_bandwidth;
- __le16 osc_start_delay;
- } __packed v2;
- } __packed;
-} __packed;
-
-#define P54_SETUP_V1_LEN 40
-#define P54_SETUP_V2_LEN (sizeof(struct p54_setup_mac))
-
-#define P54_SCAN_EXIT BIT(0)
-#define P54_SCAN_TRAP BIT(1)
-#define P54_SCAN_ACTIVE BIT(2)
-#define P54_SCAN_FILTER BIT(3)
-
-struct p54_scan_head {
- __le16 mode;
- __le16 dwell;
- u8 scan_params[20];
- __le16 freq;
-} __packed;
-
-struct p54_pa_curve_data_sample {
- u8 rf_power;
- u8 pa_detector;
- u8 data_barker;
- u8 data_bpsk;
- u8 data_qpsk;
- u8 data_16qam;
- u8 data_64qam;
- u8 padding;
-} __packed;
-
-struct p54_scan_body {
- u8 pa_points_per_curve;
- u8 val_barker;
- u8 val_bpsk;
- u8 val_qpsk;
- u8 val_16qam;
- u8 val_64qam;
- struct p54_pa_curve_data_sample curve_data[8];
- u8 dup_bpsk;
- u8 dup_qpsk;
- u8 dup_16qam;
- u8 dup_64qam;
-} __packed;
-
-/*
- * Warning: Longbow's structures are bogus.
- */
-struct p54_channel_output_limit_longbow {
- __le16 rf_power_points[12];
-} __packed;
-
-struct p54_pa_curve_data_sample_longbow {
- __le16 rf_power;
- __le16 pa_detector;
- struct {
- __le16 data[4];
- } points[3] __packed;
-} __packed;
-
-struct p54_scan_body_longbow {
- struct p54_channel_output_limit_longbow power_limits;
- struct p54_pa_curve_data_sample_longbow curve_data[8];
- __le16 unkn[6]; /* maybe more power_limits or rate_mask */
-} __packed;
-
-union p54_scan_body_union {
- struct p54_scan_body normal;
- struct p54_scan_body_longbow longbow;
-} __packed;
-
-struct p54_scan_tail_rate {
- __le32 basic_rate_mask;
- u8 rts_rates[8];
-} __packed;
-
-struct p54_led {
- __le16 flags;
- __le16 mask[2];
- __le16 delay[2];
-} __packed;
-
-struct p54_edcf {
- u8 flags;
- u8 slottime;
- u8 sifs;
- u8 eofpad;
- struct p54_edcf_queue_param queue[8];
- u8 mapping[4];
- __le16 frameburst;
- __le16 round_trip_delay;
-} __packed;
-
-struct p54_statistics {
- __le32 rx_success;
- __le32 rx_bad_fcs;
- __le32 rx_abort;
- __le32 rx_abort_phy;
- __le32 rts_success;
- __le32 rts_fail;
- __le32 tsf32;
- __le32 airtime;
- __le32 noise;
- __le32 sample_noise[8];
- __le32 sample_cca;
- __le32 sample_tx;
-} __packed;
-
-struct p54_xbow_synth {
- __le16 magic1;
- __le16 magic2;
- __le16 freq;
- u32 padding[5];
-} __packed;
-
-struct p54_timer {
- __le32 interval;
-} __packed;
-
-struct p54_keycache {
- u8 entry;
- u8 key_id;
- u8 mac[ETH_ALEN];
- u8 padding[2];
- u8 key_type;
- u8 key_len;
- u8 key[24];
-} __packed;
-
-struct p54_burst {
- u8 flags;
- u8 queue;
- u8 backlog;
- u8 pad;
- __le16 durations[32];
-} __packed;
-
-struct p54_psm_interval {
- __le16 interval;
- __le16 periods;
-} __packed;
-
-#define P54_PSM_CAM 0
-#define P54_PSM BIT(0)
-#define P54_PSM_DTIM BIT(1)
-#define P54_PSM_MCBC BIT(2)
-#define P54_PSM_CHECKSUM BIT(3)
-#define P54_PSM_SKIP_MORE_DATA BIT(4)
-#define P54_PSM_BEACON_TIMEOUT BIT(5)
-#define P54_PSM_HFOSLEEP BIT(6)
-#define P54_PSM_AUTOSWITCH_SLEEP BIT(7)
-#define P54_PSM_LPIT BIT(8)
-#define P54_PSM_BF_UCAST_SKIP BIT(9)
-#define P54_PSM_BF_MCAST_SKIP BIT(10)
-
-struct p54_psm {
- __le16 mode;
- __le16 aid;
- struct p54_psm_interval intervals[4];
- u8 beacon_rssi_skip_max;
- u8 rssi_delta_threshold;
- u8 nr;
- u8 exclude[1];
-} __packed;
-
-#define MC_FILTER_ADDRESS_NUM 4
-
-struct p54_group_address_table {
- __le16 filter_enable;
- __le16 num_address;
- u8 mac_list[MC_FILTER_ADDRESS_NUM][ETH_ALEN];
-} __packed;
-
-struct p54_txcancel {
- __le32 req_id;
-} __packed;
-
-struct p54_sta_unlock {
- u8 addr[ETH_ALEN];
- u16 padding;
-} __packed;
-
-#define P54_TIM_CLEAR BIT(15)
-struct p54_tim {
- u8 count;
- u8 padding[3];
- __le16 entry[8];
-} __packed;
-
-struct p54_cce_quiet {
- __le32 period;
-} __packed;
-
-struct p54_bt_balancer {
- __le16 prio_thresh;
- __le16 acl_thresh;
-} __packed;
-
-struct p54_arp_table {
- __le16 filter_enable;
- u8 ipv4_addr[4];
-} __packed;
-
-/* LED control */
-int p54_set_leds(struct p54_common *priv);
-int p54_init_leds(struct p54_common *priv);
-void p54_unregister_leds(struct p54_common *priv);
-
-/* xmit functions */
-void p54_tx_80211(struct ieee80211_hw *dev,
- struct ieee80211_tx_control *control,
- struct sk_buff *skb);
-int p54_tx_cancel(struct p54_common *priv, __le32 req_id);
-void p54_tx(struct p54_common *priv, struct sk_buff *skb);
-
-/* synth/phy configuration */
-int p54_init_xbow_synth(struct p54_common *priv);
-int p54_scan(struct p54_common *priv, u16 mode, u16 dwell);
-
-/* MAC */
-int p54_sta_unlock(struct p54_common *priv, u8 *addr);
-int p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set);
-int p54_setup_mac(struct p54_common *priv);
-int p54_set_ps(struct p54_common *priv);
-int p54_fetch_statistics(struct p54_common *priv);
-int p54_set_groupfilter(struct p54_common *priv);
-
-/* e/v DCF setup */
-int p54_set_edcf(struct p54_common *priv);
-
-/* cryptographic engine */
-int p54_upload_key(struct p54_common *priv, u8 algo, int slot,
- u8 idx, u8 len, u8 *addr, u8* key);
-
-/* eeprom */
-int p54_download_eeprom(struct p54_common *priv, void *buf,
- u16 offset, u16 len);
-struct p54_rssi_db_entry *p54_rssi_find(struct p54_common *p, const u16 freq);
-
-/* utility */
-u8 *p54_find_ie(struct sk_buff *skb, u8 ie);
-
-#endif /* LMAC_H */
+++ /dev/null
-/*
- * mac80211 glue code for mac80211 Prism54 drivers
- *
- * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
- * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
- * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
- *
- * Based on:
- * - the islsm (softmac prism54) driver, which is:
- * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
- * - stlc45xx driver
- * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/slab.h>
-#include <linux/firmware.h>
-#include <linux/etherdevice.h>
-#include <linux/module.h>
-
-#include <net/mac80211.h>
-
-#include "p54.h"
-#include "lmac.h"
-
-static bool modparam_nohwcrypt;
-module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
-MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
-MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
-MODULE_DESCRIPTION("Softmac Prism54 common code");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("prism54common");
-
-static int p54_sta_add_remove(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
-{
- struct p54_common *priv = hw->priv;
-
- /*
- * Notify the firmware that we don't want or we don't
- * need to buffer frames for this station anymore.
- */
-
- p54_sta_unlock(priv, sta->addr);
-
- return 0;
-}
-
-static void p54_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
- enum sta_notify_cmd notify_cmd,
- struct ieee80211_sta *sta)
-{
- struct p54_common *priv = dev->priv;
-
- switch (notify_cmd) {
- case STA_NOTIFY_AWAKE:
- /* update the firmware's filter table */
- p54_sta_unlock(priv, sta->addr);
- break;
- default:
- break;
- }
-}
-
-static int p54_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
- bool set)
-{
- struct p54_common *priv = dev->priv;
-
- return p54_update_beacon_tim(priv, sta->aid, set);
-}
-
-u8 *p54_find_ie(struct sk_buff *skb, u8 ie)
-{
- struct ieee80211_mgmt *mgmt = (void *)skb->data;
- u8 *pos, *end;
-
- if (skb->len <= sizeof(mgmt))
- return NULL;
-
- pos = (u8 *)mgmt->u.beacon.variable;
- end = skb->data + skb->len;
- while (pos < end) {
- if (pos + 2 + pos[1] > end)
- return NULL;
-
- if (pos[0] == ie)
- return pos;
-
- pos += 2 + pos[1];
- }
- return NULL;
-}
-
-static int p54_beacon_format_ie_tim(struct sk_buff *skb)
-{
- /*
- * the good excuse for this mess is ... the firmware.
- * The dummy TIM MUST be at the end of the beacon frame,
- * because it'll be overwritten!
- */
- u8 *tim;
- u8 dtim_len;
- u8 dtim_period;
- u8 *next;
-
- tim = p54_find_ie(skb, WLAN_EID_TIM);
- if (!tim)
- return 0;
-
- dtim_len = tim[1];
- dtim_period = tim[3];
- next = tim + 2 + dtim_len;
-
- if (dtim_len < 3)
- return -EINVAL;
-
- memmove(tim, next, skb_tail_pointer(skb) - next);
- tim = skb_tail_pointer(skb) - (dtim_len + 2);
-
- /* add the dummy at the end */
- tim[0] = WLAN_EID_TIM;
- tim[1] = 3;
- tim[2] = 0;
- tim[3] = dtim_period;
- tim[4] = 0;
-
- if (dtim_len > 3)
- skb_trim(skb, skb->len - (dtim_len - 3));
-
- return 0;
-}
-
-static int p54_beacon_update(struct p54_common *priv,
- struct ieee80211_vif *vif)
-{
- struct ieee80211_tx_control control = { };
- struct sk_buff *beacon;
- int ret;
-
- beacon = ieee80211_beacon_get(priv->hw, vif);
- if (!beacon)
- return -ENOMEM;
- ret = p54_beacon_format_ie_tim(beacon);
- if (ret)
- return ret;
-
- /*
- * During operation, the firmware takes care of beaconing.
- * The driver only needs to upload a new beacon template, once
- * the template was changed by the stack or userspace.
- *
- * LMAC API 3.2.2 also specifies that the driver does not need
- * to cancel the old beacon template by hand, instead the firmware
- * will release the previous one through the feedback mechanism.
- */
- p54_tx_80211(priv->hw, &control, beacon);
- priv->tsf_high32 = 0;
- priv->tsf_low32 = 0;
-
- return 0;
-}
-
-static int p54_start(struct ieee80211_hw *dev)
-{
- struct p54_common *priv = dev->priv;
- int err;
-
- mutex_lock(&priv->conf_mutex);
- err = priv->open(dev);
- if (err)
- goto out;
- P54_SET_QUEUE(priv->qos_params[0], 0x0002, 0x0003, 0x0007, 47);
- P54_SET_QUEUE(priv->qos_params[1], 0x0002, 0x0007, 0x000f, 94);
- P54_SET_QUEUE(priv->qos_params[2], 0x0003, 0x000f, 0x03ff, 0);
- P54_SET_QUEUE(priv->qos_params[3], 0x0007, 0x000f, 0x03ff, 0);
- err = p54_set_edcf(priv);
- if (err)
- goto out;
-
- eth_broadcast_addr(priv->bssid);
- priv->mode = NL80211_IFTYPE_MONITOR;
- err = p54_setup_mac(priv);
- if (err) {
- priv->mode = NL80211_IFTYPE_UNSPECIFIED;
- goto out;
- }
-
- ieee80211_queue_delayed_work(dev, &priv->work, 0);
-
- priv->softled_state = 0;
- err = p54_set_leds(priv);
-
-out:
- mutex_unlock(&priv->conf_mutex);
- return err;
-}
-
-static void p54_stop(struct ieee80211_hw *dev)
-{
- struct p54_common *priv = dev->priv;
- int i;
-
- priv->mode = NL80211_IFTYPE_UNSPECIFIED;
- priv->softled_state = 0;
- cancel_delayed_work_sync(&priv->work);
- mutex_lock(&priv->conf_mutex);
- p54_set_leds(priv);
- priv->stop(dev);
- skb_queue_purge(&priv->tx_pending);
- skb_queue_purge(&priv->tx_queue);
- for (i = 0; i < P54_QUEUE_NUM; i++) {
- priv->tx_stats[i].count = 0;
- priv->tx_stats[i].len = 0;
- }
-
- priv->beacon_req_id = cpu_to_le32(0);
- priv->tsf_high32 = priv->tsf_low32 = 0;
- mutex_unlock(&priv->conf_mutex);
-}
-
-static int p54_add_interface(struct ieee80211_hw *dev,
- struct ieee80211_vif *vif)
-{
- struct p54_common *priv = dev->priv;
- int err;
-
- vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
-
- mutex_lock(&priv->conf_mutex);
- if (priv->mode != NL80211_IFTYPE_MONITOR) {
- mutex_unlock(&priv->conf_mutex);
- return -EOPNOTSUPP;
- }
-
- priv->vif = vif;
-
- switch (vif->type) {
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_MESH_POINT:
- priv->mode = vif->type;
- break;
- default:
- mutex_unlock(&priv->conf_mutex);
- return -EOPNOTSUPP;
- }
-
- memcpy(priv->mac_addr, vif->addr, ETH_ALEN);
- err = p54_setup_mac(priv);
- mutex_unlock(&priv->conf_mutex);
- return err;
-}
-
-static void p54_remove_interface(struct ieee80211_hw *dev,
- struct ieee80211_vif *vif)
-{
- struct p54_common *priv = dev->priv;
-
- mutex_lock(&priv->conf_mutex);
- priv->vif = NULL;
-
- /*
- * LMAC API 3.2.2 states that any active beacon template must be
- * canceled by the driver before attempting a mode transition.
- */
- if (le32_to_cpu(priv->beacon_req_id) != 0) {
- p54_tx_cancel(priv, priv->beacon_req_id);
- wait_for_completion_interruptible_timeout(&priv->beacon_comp, HZ);
- }
- priv->mode = NL80211_IFTYPE_MONITOR;
- eth_zero_addr(priv->mac_addr);
- eth_zero_addr(priv->bssid);
- p54_setup_mac(priv);
- mutex_unlock(&priv->conf_mutex);
-}
-
-static int p54_wait_for_stats(struct ieee80211_hw *dev)
-{
- struct p54_common *priv = dev->priv;
- int ret;
-
- priv->update_stats = true;
- ret = p54_fetch_statistics(priv);
- if (ret)
- return ret;
-
- ret = wait_for_completion_interruptible_timeout(&priv->stat_comp, HZ);
- if (ret == 0)
- return -ETIMEDOUT;
-
- return 0;
-}
-
-static void p54_reset_stats(struct p54_common *priv)
-{
- struct ieee80211_channel *chan = priv->curchan;
-
- if (chan) {
- struct survey_info *info = &priv->survey[chan->hw_value];
-
- /* only reset channel statistics, don't touch .filled, etc. */
- info->time = 0;
- info->time_busy = 0;
- info->time_tx = 0;
- }
-
- priv->update_stats = true;
- priv->survey_raw.active = 0;
- priv->survey_raw.cca = 0;
- priv->survey_raw.tx = 0;
-}
-
-static int p54_config(struct ieee80211_hw *dev, u32 changed)
-{
- int ret = 0;
- struct p54_common *priv = dev->priv;
- struct ieee80211_conf *conf = &dev->conf;
-
- mutex_lock(&priv->conf_mutex);
- if (changed & IEEE80211_CONF_CHANGE_POWER)
- priv->output_power = conf->power_level << 2;
- if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
- struct ieee80211_channel *oldchan;
- WARN_ON(p54_wait_for_stats(dev));
- oldchan = priv->curchan;
- priv->curchan = NULL;
- ret = p54_scan(priv, P54_SCAN_EXIT, 0);
- if (ret) {
- priv->curchan = oldchan;
- goto out;
- }
- /*
- * TODO: Use the LM_SCAN_TRAP to determine the current
- * operating channel.
- */
- priv->curchan = priv->hw->conf.chandef.chan;
- p54_reset_stats(priv);
- WARN_ON(p54_fetch_statistics(priv));
- }
- if (changed & IEEE80211_CONF_CHANGE_PS) {
- WARN_ON(p54_wait_for_stats(dev));
- ret = p54_set_ps(priv);
- if (ret)
- goto out;
- WARN_ON(p54_wait_for_stats(dev));
- }
- if (changed & IEEE80211_CONF_CHANGE_IDLE) {
- WARN_ON(p54_wait_for_stats(dev));
- ret = p54_setup_mac(priv);
- if (ret)
- goto out;
- WARN_ON(p54_wait_for_stats(dev));
- }
-
-out:
- mutex_unlock(&priv->conf_mutex);
- return ret;
-}
-
-static u64 p54_prepare_multicast(struct ieee80211_hw *dev,
- struct netdev_hw_addr_list *mc_list)
-{
- struct p54_common *priv = dev->priv;
- struct netdev_hw_addr *ha;
- int i;
-
- BUILD_BUG_ON(ARRAY_SIZE(priv->mc_maclist) !=
- ARRAY_SIZE(((struct p54_group_address_table *)NULL)->mac_list));
- /*
- * The first entry is reserved for the global broadcast MAC.
- * Otherwise the firmware will drop it and ARP will no longer work.
- */
- i = 1;
- priv->mc_maclist_num = netdev_hw_addr_list_count(mc_list) + i;
- netdev_hw_addr_list_for_each(ha, mc_list) {
- memcpy(&priv->mc_maclist[i], ha->addr, ETH_ALEN);
- i++;
- if (i >= ARRAY_SIZE(priv->mc_maclist))
- break;
- }
-
- return 1; /* update */
-}
-
-static void p54_configure_filter(struct ieee80211_hw *dev,
- unsigned int changed_flags,
- unsigned int *total_flags,
- u64 multicast)
-{
- struct p54_common *priv = dev->priv;
-
- *total_flags &= FIF_ALLMULTI | FIF_OTHER_BSS;
-
- priv->filter_flags = *total_flags;
-
- if (changed_flags & FIF_OTHER_BSS)
- p54_setup_mac(priv);
-
- if (changed_flags & FIF_ALLMULTI || multicast)
- p54_set_groupfilter(priv);
-}
-
-static int p54_conf_tx(struct ieee80211_hw *dev,
- struct ieee80211_vif *vif, u16 queue,
- const struct ieee80211_tx_queue_params *params)
-{
- struct p54_common *priv = dev->priv;
- int ret;
-
- mutex_lock(&priv->conf_mutex);
- if (queue < dev->queues) {
- P54_SET_QUEUE(priv->qos_params[queue], params->aifs,
- params->cw_min, params->cw_max, params->txop);
- ret = p54_set_edcf(priv);
- } else
- ret = -EINVAL;
- mutex_unlock(&priv->conf_mutex);
- return ret;
-}
-
-static void p54_work(struct work_struct *work)
-{
- struct p54_common *priv = container_of(work, struct p54_common,
- work.work);
-
- if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
- return ;
-
- /*
- * TODO: walk through tx_queue and do the following tasks
- * 1. initiate bursts.
- * 2. cancel stuck frames / reset the device if necessary.
- */
-
- mutex_lock(&priv->conf_mutex);
- WARN_ON_ONCE(p54_fetch_statistics(priv));
- mutex_unlock(&priv->conf_mutex);
-}
-
-static int p54_get_stats(struct ieee80211_hw *dev,
- struct ieee80211_low_level_stats *stats)
-{
- struct p54_common *priv = dev->priv;
-
- memcpy(stats, &priv->stats, sizeof(*stats));
- return 0;
-}
-
-static void p54_bss_info_changed(struct ieee80211_hw *dev,
- struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *info,
- u32 changed)
-{
- struct p54_common *priv = dev->priv;
-
- mutex_lock(&priv->conf_mutex);
- if (changed & BSS_CHANGED_BSSID) {
- memcpy(priv->bssid, info->bssid, ETH_ALEN);
- p54_setup_mac(priv);
- }
-
- if (changed & BSS_CHANGED_BEACON) {
- p54_scan(priv, P54_SCAN_EXIT, 0);
- p54_setup_mac(priv);
- p54_beacon_update(priv, vif);
- p54_set_edcf(priv);
- }
-
- if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BEACON)) {
- priv->use_short_slot = info->use_short_slot;
- p54_set_edcf(priv);
- }
- if (changed & BSS_CHANGED_BASIC_RATES) {
- if (dev->conf.chandef.chan->band == IEEE80211_BAND_5GHZ)
- priv->basic_rate_mask = (info->basic_rates << 4);
- else
- priv->basic_rate_mask = info->basic_rates;
- p54_setup_mac(priv);
- if (priv->fw_var >= 0x500)
- p54_scan(priv, P54_SCAN_EXIT, 0);
- }
- if (changed & BSS_CHANGED_ASSOC) {
- if (info->assoc) {
- priv->aid = info->aid;
- priv->wakeup_timer = info->beacon_int *
- info->dtim_period * 5;
- p54_setup_mac(priv);
- } else {
- priv->wakeup_timer = 500;
- priv->aid = 0;
- }
- }
-
- mutex_unlock(&priv->conf_mutex);
-}
-
-static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
- struct ieee80211_vif *vif, struct ieee80211_sta *sta,
- struct ieee80211_key_conf *key)
-{
- struct p54_common *priv = dev->priv;
- int slot, ret = 0;
- u8 algo = 0;
- u8 *addr = NULL;
-
- if (modparam_nohwcrypt)
- return -EOPNOTSUPP;
-
- if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT) {
- /*
- * Unfortunately most/all firmwares are trying to decrypt
- * incoming management frames if a suitable key can be found.
- * However, in doing so the data in these frames gets
- * corrupted. So, we can't have firmware supported crypto
- * offload in this case.
- */
- return -EOPNOTSUPP;
- }
-
- mutex_lock(&priv->conf_mutex);
- if (cmd == SET_KEY) {
- switch (key->cipher) {
- case WLAN_CIPHER_SUITE_TKIP:
- if (!(priv->privacy_caps & (BR_DESC_PRIV_CAP_MICHAEL |
- BR_DESC_PRIV_CAP_TKIP))) {
- ret = -EOPNOTSUPP;
- goto out_unlock;
- }
- key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
- algo = P54_CRYPTO_TKIPMICHAEL;
- break;
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP)) {
- ret = -EOPNOTSUPP;
- goto out_unlock;
- }
- key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
- algo = P54_CRYPTO_WEP;
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)) {
- ret = -EOPNOTSUPP;
- goto out_unlock;
- }
- key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
- algo = P54_CRYPTO_AESCCMP;
- break;
- default:
- ret = -EOPNOTSUPP;
- goto out_unlock;
- }
- slot = bitmap_find_free_region(priv->used_rxkeys,
- priv->rx_keycache_size, 0);
-
- if (slot < 0) {
- /*
- * The device supports the chosen algorithm, but the
- * firmware does not provide enough key slots to store
- * all of them.
- * But encryption offload for outgoing frames is always
- * possible, so we just pretend that the upload was
- * successful and do the decryption in software.
- */
-
- /* mark the key as invalid. */
- key->hw_key_idx = 0xff;
- goto out_unlock;
- }
-
- key->flags |= IEEE80211_KEY_FLAG_RESERVE_TAILROOM;
- } else {
- slot = key->hw_key_idx;
-
- if (slot == 0xff) {
- /* This key was not uploaded into the rx key cache. */
-
- goto out_unlock;
- }
-
- bitmap_release_region(priv->used_rxkeys, slot, 0);
- algo = 0;
- }
-
- if (sta)
- addr = sta->addr;
-
- ret = p54_upload_key(priv, algo, slot, key->keyidx,
- key->keylen, addr, key->key);
- if (ret) {
- bitmap_release_region(priv->used_rxkeys, slot, 0);
- ret = -EOPNOTSUPP;
- goto out_unlock;
- }
-
- key->hw_key_idx = slot;
-
-out_unlock:
- mutex_unlock(&priv->conf_mutex);
- return ret;
-}
-
-static int p54_get_survey(struct ieee80211_hw *dev, int idx,
- struct survey_info *survey)
-{
- struct p54_common *priv = dev->priv;
- struct ieee80211_channel *chan;
- int err, tries;
- bool in_use = false;
-
- if (idx >= priv->chan_num)
- return -ENOENT;
-
-#define MAX_TRIES 1
- for (tries = 0; tries < MAX_TRIES; tries++) {
- chan = priv->curchan;
- if (chan && chan->hw_value == idx) {
- mutex_lock(&priv->conf_mutex);
- err = p54_wait_for_stats(dev);
- mutex_unlock(&priv->conf_mutex);
- if (err)
- return err;
-
- in_use = true;
- }
-
- memcpy(survey, &priv->survey[idx], sizeof(*survey));
-
- if (in_use) {
- /* test if the reported statistics are valid. */
- if (survey->time != 0) {
- survey->filled |= SURVEY_INFO_IN_USE;
- } else {
- /*
- * hw/fw has not accumulated enough sample sets.
- * Wait for 100ms, this ought to be enough to
- * to get at least one non-null set of channel
- * usage statistics.
- */
- msleep(100);
- continue;
- }
- }
- return 0;
- }
- return -ETIMEDOUT;
-#undef MAX_TRIES
-}
-
-static unsigned int p54_flush_count(struct p54_common *priv)
-{
- unsigned int total = 0, i;
-
- BUILD_BUG_ON(P54_QUEUE_NUM > ARRAY_SIZE(priv->tx_stats));
-
- /*
- * Because the firmware has the sole control over any frames
- * in the P54_QUEUE_BEACON or P54_QUEUE_SCAN queues, they
- * don't really count as pending or active.
- */
- for (i = P54_QUEUE_MGMT; i < P54_QUEUE_NUM; i++)
- total += priv->tx_stats[i].len;
- return total;
-}
-
-static void p54_flush(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
- u32 queues, bool drop)
-{
- struct p54_common *priv = dev->priv;
- unsigned int total, i;
-
- /*
- * Currently, it wouldn't really matter if we wait for one second
- * or 15 minutes. But once someone gets around and completes the
- * TODOs [ancel stuck frames / reset device] in p54_work, it will
- * suddenly make sense to wait that long.
- */
- i = P54_STATISTICS_UPDATE * 2 / 20;
-
- /*
- * In this case no locking is required because as we speak the
- * queues have already been stopped and no new frames can sneak
- * up from behind.
- */
- while ((total = p54_flush_count(priv) && i--)) {
- /* waste time */
- msleep(20);
- }
-
- WARN(total, "tx flush timeout, unresponsive firmware");
-}
-
-static void p54_set_coverage_class(struct ieee80211_hw *dev,
- s16 coverage_class)
-{
- struct p54_common *priv = dev->priv;
-
- mutex_lock(&priv->conf_mutex);
- /* support all coverage class values as in 802.11-2007 Table 7-27 */
- priv->coverage_class = clamp_t(u8, coverage_class, 0, 31);
- p54_set_edcf(priv);
- mutex_unlock(&priv->conf_mutex);
-}
-
-static const struct ieee80211_ops p54_ops = {
- .tx = p54_tx_80211,
- .start = p54_start,
- .stop = p54_stop,
- .add_interface = p54_add_interface,
- .remove_interface = p54_remove_interface,
- .set_tim = p54_set_tim,
- .sta_notify = p54_sta_notify,
- .sta_add = p54_sta_add_remove,
- .sta_remove = p54_sta_add_remove,
- .set_key = p54_set_key,
- .config = p54_config,
- .flush = p54_flush,
- .bss_info_changed = p54_bss_info_changed,
- .prepare_multicast = p54_prepare_multicast,
- .configure_filter = p54_configure_filter,
- .conf_tx = p54_conf_tx,
- .get_stats = p54_get_stats,
- .get_survey = p54_get_survey,
- .set_coverage_class = p54_set_coverage_class,
-};
-
-struct ieee80211_hw *p54_init_common(size_t priv_data_len)
-{
- struct ieee80211_hw *dev;
- struct p54_common *priv;
-
- dev = ieee80211_alloc_hw(priv_data_len, &p54_ops);
- if (!dev)
- return NULL;
-
- priv = dev->priv;
- priv->hw = dev;
- priv->mode = NL80211_IFTYPE_UNSPECIFIED;
- priv->basic_rate_mask = 0x15f;
- spin_lock_init(&priv->tx_stats_lock);
- skb_queue_head_init(&priv->tx_queue);
- skb_queue_head_init(&priv->tx_pending);
- ieee80211_hw_set(dev, REPORTS_TX_ACK_STATUS);
- ieee80211_hw_set(dev, MFP_CAPABLE);
- ieee80211_hw_set(dev, PS_NULLFUNC_STACK);
- ieee80211_hw_set(dev, SUPPORTS_PS);
- ieee80211_hw_set(dev, RX_INCLUDES_FCS);
- ieee80211_hw_set(dev, SIGNAL_DBM);
-
- dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_ADHOC) |
- BIT(NL80211_IFTYPE_AP) |
- BIT(NL80211_IFTYPE_MESH_POINT);
-
- priv->beacon_req_id = cpu_to_le32(0);
- priv->tx_stats[P54_QUEUE_BEACON].limit = 1;
- priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1;
- priv->tx_stats[P54_QUEUE_MGMT].limit = 3;
- priv->tx_stats[P54_QUEUE_CAB].limit = 3;
- priv->tx_stats[P54_QUEUE_DATA].limit = 5;
- dev->queues = 1;
- priv->noise = -94;
- /*
- * We support at most 8 tries no matter which rate they're at,
- * we cannot support max_rates * max_rate_tries as we set it
- * here, but setting it correctly to 4/2 or so would limit us
- * artificially if the RC algorithm wants just two rates, so
- * let's say 4/7, we'll redistribute it at TX time, see the
- * comments there.
- */
- dev->max_rates = 4;
- dev->max_rate_tries = 7;
- dev->extra_tx_headroom = sizeof(struct p54_hdr) + 4 +
- sizeof(struct p54_tx_data);
-
- /*
- * For now, disable PS by default because it affects
- * link stability significantly.
- */
- dev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
-
- mutex_init(&priv->conf_mutex);
- mutex_init(&priv->eeprom_mutex);
- init_completion(&priv->stat_comp);
- init_completion(&priv->eeprom_comp);
- init_completion(&priv->beacon_comp);
- INIT_DELAYED_WORK(&priv->work, p54_work);
-
- eth_broadcast_addr(priv->mc_maclist[0]);
- priv->curchan = NULL;
- p54_reset_stats(priv);
- return dev;
-}
-EXPORT_SYMBOL_GPL(p54_init_common);
-
-int p54_register_common(struct ieee80211_hw *dev, struct device *pdev)
-{
- struct p54_common __maybe_unused *priv = dev->priv;
- int err;
-
- err = ieee80211_register_hw(dev);
- if (err) {
- dev_err(pdev, "Cannot register device (%d).\n", err);
- return err;
- }
- priv->registered = true;
-
-#ifdef CONFIG_P54_LEDS
- err = p54_init_leds(priv);
- if (err) {
- p54_unregister_common(dev);
- return err;
- }
-#endif /* CONFIG_P54_LEDS */
-
- dev_info(pdev, "is registered as '%s'\n", wiphy_name(dev->wiphy));
- return 0;
-}
-EXPORT_SYMBOL_GPL(p54_register_common);
-
-void p54_free_common(struct ieee80211_hw *dev)
-{
- struct p54_common *priv = dev->priv;
- unsigned int i;
-
- for (i = 0; i < IEEE80211_NUM_BANDS; i++)
- kfree(priv->band_table[i]);
-
- kfree(priv->iq_autocal);
- kfree(priv->output_limit);
- kfree(priv->curve_data);
- kfree(priv->rssi_db);
- kfree(priv->used_rxkeys);
- kfree(priv->survey);
- priv->iq_autocal = NULL;
- priv->output_limit = NULL;
- priv->curve_data = NULL;
- priv->rssi_db = NULL;
- priv->used_rxkeys = NULL;
- priv->survey = NULL;
- ieee80211_free_hw(dev);
-}
-EXPORT_SYMBOL_GPL(p54_free_common);
-
-void p54_unregister_common(struct ieee80211_hw *dev)
-{
- struct p54_common *priv = dev->priv;
-
-#ifdef CONFIG_P54_LEDS
- p54_unregister_leds(priv);
-#endif /* CONFIG_P54_LEDS */
-
- if (priv->registered) {
- priv->registered = false;
- ieee80211_unregister_hw(dev);
- }
-
- mutex_destroy(&priv->conf_mutex);
- mutex_destroy(&priv->eeprom_mutex);
-}
-EXPORT_SYMBOL_GPL(p54_unregister_common);
+++ /dev/null
-/*
- * Shared defines for all mac80211 Prism54 code
- *
- * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
- *
- * Based on the islsm (softmac prism54) driver, which is:
- * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef P54_H
-#define P54_H
-
-#ifdef CONFIG_P54_LEDS
-#include <linux/leds.h>
-#endif /* CONFIG_P54_LEDS */
-
-#define ISL38XX_DEV_FIRMWARE_ADDR 0x20000
-
-#define BR_CODE_MIN 0x80000000
-#define BR_CODE_COMPONENT_ID 0x80000001
-#define BR_CODE_COMPONENT_VERSION 0x80000002
-#define BR_CODE_DEPENDENT_IF 0x80000003
-#define BR_CODE_EXPOSED_IF 0x80000004
-#define BR_CODE_DESCR 0x80000101
-#define BR_CODE_MAX 0x8FFFFFFF
-#define BR_CODE_END_OF_BRA 0xFF0000FF
-#define LEGACY_BR_CODE_END_OF_BRA 0xFFFFFFFF
-
-struct bootrec {
- __le32 code;
- __le32 len;
- u32 data[10];
-} __packed;
-
-/* Interface role definitions */
-#define BR_INTERFACE_ROLE_SERVER 0x0000
-#define BR_INTERFACE_ROLE_CLIENT 0x8000
-
-#define BR_DESC_PRIV_CAP_WEP BIT(0)
-#define BR_DESC_PRIV_CAP_TKIP BIT(1)
-#define BR_DESC_PRIV_CAP_MICHAEL BIT(2)
-#define BR_DESC_PRIV_CAP_CCX_CP BIT(3)
-#define BR_DESC_PRIV_CAP_CCX_MIC BIT(4)
-#define BR_DESC_PRIV_CAP_AESCCMP BIT(5)
-
-struct bootrec_desc {
- __le16 modes;
- __le16 flags;
- __le32 rx_start;
- __le32 rx_end;
- u8 headroom;
- u8 tailroom;
- u8 tx_queues;
- u8 tx_depth;
- u8 privacy_caps;
- u8 rx_keycache_size;
- u8 time_size;
- u8 padding;
- u8 rates[16];
- u8 padding2[4];
- __le16 rx_mtu;
-} __packed;
-
-#define FW_FMAC 0x464d4143
-#define FW_LM86 0x4c4d3836
-#define FW_LM87 0x4c4d3837
-#define FW_LM20 0x4c4d3230
-
-struct bootrec_comp_id {
- __le32 fw_variant;
-} __packed;
-
-struct bootrec_comp_ver {
- char fw_version[24];
-} __packed;
-
-struct bootrec_end {
- __le16 crc;
- u8 padding[2];
- u8 md5[16];
-} __packed;
-
-/* provide 16 bytes for the transport back-end */
-#define P54_TX_INFO_DATA_SIZE 16
-
-/* stored in ieee80211_tx_info's rate_driver_data */
-struct p54_tx_info {
- u32 start_addr;
- u32 end_addr;
- union {
- void *data[P54_TX_INFO_DATA_SIZE / sizeof(void *)];
- struct {
- u32 extra_len;
- };
- };
-};
-
-#define P54_MAX_CTRL_FRAME_LEN 0x1000
-
-#define P54_SET_QUEUE(queue, ai_fs, cw_min, cw_max, _txop) \
-do { \
- queue.aifs = cpu_to_le16(ai_fs); \
- queue.cwmin = cpu_to_le16(cw_min); \
- queue.cwmax = cpu_to_le16(cw_max); \
- queue.txop = cpu_to_le16(_txop); \
-} while (0)
-
-struct p54_edcf_queue_param {
- __le16 aifs;
- __le16 cwmin;
- __le16 cwmax;
- __le16 txop;
-} __packed;
-
-struct p54_rssi_db_entry {
- u16 freq;
- s16 mul;
- s16 add;
- s16 longbow_unkn;
- s16 longbow_unk2;
-};
-
-struct p54_cal_database {
- size_t entries;
- size_t entry_size;
- size_t offset;
- size_t len;
- u8 data[0];
-};
-
-#define EEPROM_READBACK_LEN 0x3fc
-
-enum fw_state {
- FW_STATE_OFF,
- FW_STATE_BOOTING,
- FW_STATE_READY,
- FW_STATE_RESET,
- FW_STATE_RESETTING,
-};
-
-#ifdef CONFIG_P54_LEDS
-
-#define P54_LED_MAX_NAME_LEN 31
-
-struct p54_led_dev {
- struct ieee80211_hw *hw_dev;
- struct led_classdev led_dev;
- char name[P54_LED_MAX_NAME_LEN + 1];
-
- unsigned int toggled;
- unsigned int index;
- unsigned int registered;
-};
-
-#endif /* CONFIG_P54_LEDS */
-
-struct p54_tx_queue_stats {
- unsigned int len;
- unsigned int limit;
- unsigned int count;
-};
-
-struct p54_common {
- struct ieee80211_hw *hw;
- struct ieee80211_vif *vif;
- void (*tx)(struct ieee80211_hw *dev, struct sk_buff *skb);
- int (*open)(struct ieee80211_hw *dev);
- void (*stop)(struct ieee80211_hw *dev);
- struct sk_buff_head tx_pending;
- struct sk_buff_head tx_queue;
- struct mutex conf_mutex;
- bool registered;
-
- /* memory management (as seen by the firmware) */
- u32 rx_start;
- u32 rx_end;
- u16 rx_mtu;
- u8 headroom;
- u8 tailroom;
-
- /* firmware/hardware info */
- unsigned int tx_hdr_len;
- unsigned int fw_var;
- unsigned int fw_interface;
- u8 version;
-
- /* (e)DCF / QOS state */
- bool use_short_slot;
- spinlock_t tx_stats_lock;
- struct p54_tx_queue_stats tx_stats[8];
- struct p54_edcf_queue_param qos_params[8];
-
- /* Radio data */
- u16 rxhw;
- u8 rx_diversity_mask;
- u8 tx_diversity_mask;
- unsigned int output_power;
- struct p54_rssi_db_entry *cur_rssi;
- struct ieee80211_channel *curchan;
- struct survey_info *survey;
- unsigned int chan_num;
- struct completion stat_comp;
- bool update_stats;
- struct {
- unsigned int timestamp;
- unsigned int cached_cca;
- unsigned int cached_tx;
- unsigned int cached_rssi;
- u64 active;
- u64 cca;
- u64 tx;
- u64 rssi;
- } survey_raw;
-
- int noise;
- /* calibration, output power limit and rssi<->dBm conversation data */
- struct pda_iq_autocal_entry *iq_autocal;
- unsigned int iq_autocal_len;
- struct p54_cal_database *curve_data;
- struct p54_cal_database *output_limit;
- struct p54_cal_database *rssi_db;
- struct ieee80211_supported_band *band_table[IEEE80211_NUM_BANDS];
-
- /* BBP/MAC state */
- u8 mac_addr[ETH_ALEN];
- u8 bssid[ETH_ALEN];
- u8 mc_maclist[4][ETH_ALEN];
- u16 wakeup_timer;
- unsigned int filter_flags;
- int mc_maclist_num;
- int mode;
- u32 tsf_low32, tsf_high32;
- u32 basic_rate_mask;
- u16 aid;
- u8 coverage_class;
- bool phy_idle;
- bool phy_ps;
- bool powersave_override;
- __le32 beacon_req_id;
- struct completion beacon_comp;
-
- /* cryptographic engine information */
- u8 privacy_caps;
- u8 rx_keycache_size;
- unsigned long *used_rxkeys;
-
- /* LED management */
-#ifdef CONFIG_P54_LEDS
- struct p54_led_dev leds[4];
- struct delayed_work led_work;
-#endif /* CONFIG_P54_LEDS */
- u16 softled_state; /* bit field of glowing LEDs */
-
- /* statistics */
- struct ieee80211_low_level_stats stats;
- struct delayed_work work;
-
- /* eeprom handling */
- void *eeprom;
- struct completion eeprom_comp;
- struct mutex eeprom_mutex;
-};
-
-/* interfaces for the drivers */
-int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb);
-void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb);
-int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw);
-int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len);
-int p54_read_eeprom(struct ieee80211_hw *dev);
-
-struct ieee80211_hw *p54_init_common(size_t priv_data_len);
-int p54_register_common(struct ieee80211_hw *dev, struct device *pdev);
-void p54_free_common(struct ieee80211_hw *dev);
-
-void p54_unregister_common(struct ieee80211_hw *dev);
-
-#endif /* P54_H */
+++ /dev/null
-
-/*
- * Linux device driver for PCI based Prism54
- *
- * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
- * Copyright (c) 2008, Christian Lamparter <chunkeey@web.de>
- *
- * Based on the islsm (softmac prism54) driver, which is:
- * Copyright 2004-2006 Jean-Baptiste Note <jean-baptiste.note@m4x.org>, et al.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <linux/firmware.h>
-#include <linux/etherdevice.h>
-#include <linux/delay.h>
-#include <linux/completion.h>
-#include <linux/module.h>
-#include <net/mac80211.h>
-
-#include "p54.h"
-#include "lmac.h"
-#include "p54pci.h"
-
-MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
-MODULE_DESCRIPTION("Prism54 PCI wireless driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("prism54pci");
-MODULE_FIRMWARE("isl3886pci");
-
-static const struct pci_device_id p54p_table[] = {
- /* Intersil PRISM Duette/Prism GT Wireless LAN adapter */
- { PCI_DEVICE(0x1260, 0x3890) },
- /* 3COM 3CRWE154G72 Wireless LAN adapter */
- { PCI_DEVICE(0x10b7, 0x6001) },
- /* Intersil PRISM Indigo Wireless LAN adapter */
- { PCI_DEVICE(0x1260, 0x3877) },
- /* Intersil PRISM Javelin/Xbow Wireless LAN adapter */
- { PCI_DEVICE(0x1260, 0x3886) },
- /* Intersil PRISM Xbow Wireless LAN adapter (Symbol AP-300) */
- { PCI_DEVICE(0x1260, 0xffff) },
- { },
-};
-
-MODULE_DEVICE_TABLE(pci, p54p_table);
-
-static int p54p_upload_firmware(struct ieee80211_hw *dev)
-{
- struct p54p_priv *priv = dev->priv;
- __le32 reg;
- int err;
- __le32 *data;
- u32 remains, left, device_addr;
-
- P54P_WRITE(int_enable, cpu_to_le32(0));
- P54P_READ(int_enable);
- udelay(10);
-
- reg = P54P_READ(ctrl_stat);
- reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
- reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT);
- P54P_WRITE(ctrl_stat, reg);
- P54P_READ(ctrl_stat);
- udelay(10);
-
- reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
- P54P_WRITE(ctrl_stat, reg);
- wmb();
- udelay(10);
-
- reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
- P54P_WRITE(ctrl_stat, reg);
- wmb();
-
- /* wait for the firmware to reset properly */
- mdelay(10);
-
- err = p54_parse_firmware(dev, priv->firmware);
- if (err)
- return err;
-
- if (priv->common.fw_interface != FW_LM86) {
- dev_err(&priv->pdev->dev, "wrong firmware, "
- "please get a LM86(PCI) firmware a try again.\n");
- return -EINVAL;
- }
-
- data = (__le32 *) priv->firmware->data;
- remains = priv->firmware->size;
- device_addr = ISL38XX_DEV_FIRMWARE_ADDR;
- while (remains) {
- u32 i = 0;
- left = min((u32)0x1000, remains);
- P54P_WRITE(direct_mem_base, cpu_to_le32(device_addr));
- P54P_READ(int_enable);
-
- device_addr += 0x1000;
- while (i < left) {
- P54P_WRITE(direct_mem_win[i], *data++);
- i += sizeof(u32);
- }
-
- remains -= left;
- P54P_READ(int_enable);
- }
-
- reg = P54P_READ(ctrl_stat);
- reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);
- reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
- reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT);
- P54P_WRITE(ctrl_stat, reg);
- P54P_READ(ctrl_stat);
- udelay(10);
-
- reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
- P54P_WRITE(ctrl_stat, reg);
- wmb();
- udelay(10);
-
- reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
- P54P_WRITE(ctrl_stat, reg);
- wmb();
- udelay(10);
-
- /* wait for the firmware to boot properly */
- mdelay(100);
-
- return 0;
-}
-
-static void p54p_refill_rx_ring(struct ieee80211_hw *dev,
- int ring_index, struct p54p_desc *ring, u32 ring_limit,
- struct sk_buff **rx_buf, u32 index)
-{
- struct p54p_priv *priv = dev->priv;
- struct p54p_ring_control *ring_control = priv->ring_control;
- u32 limit, idx, i;
-
- idx = le32_to_cpu(ring_control->host_idx[ring_index]);
- limit = idx;
- limit -= index;
- limit = ring_limit - limit;
-
- i = idx % ring_limit;
- while (limit-- > 1) {
- struct p54p_desc *desc = &ring[i];
-
- if (!desc->host_addr) {
- struct sk_buff *skb;
- dma_addr_t mapping;
- skb = dev_alloc_skb(priv->common.rx_mtu + 32);
- if (!skb)
- break;
-
- mapping = pci_map_single(priv->pdev,
- skb_tail_pointer(skb),
- priv->common.rx_mtu + 32,
- PCI_DMA_FROMDEVICE);
-
- if (pci_dma_mapping_error(priv->pdev, mapping)) {
- dev_kfree_skb_any(skb);
- dev_err(&priv->pdev->dev,
- "RX DMA Mapping error\n");
- break;
- }
-
- desc->host_addr = cpu_to_le32(mapping);
- desc->device_addr = 0; // FIXME: necessary?
- desc->len = cpu_to_le16(priv->common.rx_mtu + 32);
- desc->flags = 0;
- rx_buf[i] = skb;
- }
-
- i++;
- idx++;
- i %= ring_limit;
- }
-
- wmb();
- ring_control->host_idx[ring_index] = cpu_to_le32(idx);
-}
-
-static void p54p_check_rx_ring(struct ieee80211_hw *dev, u32 *index,
- int ring_index, struct p54p_desc *ring, u32 ring_limit,
- struct sk_buff **rx_buf)
-{
- struct p54p_priv *priv = dev->priv;
- struct p54p_ring_control *ring_control = priv->ring_control;
- struct p54p_desc *desc;
- u32 idx, i;
-
- i = (*index) % ring_limit;
- (*index) = idx = le32_to_cpu(ring_control->device_idx[ring_index]);
- idx %= ring_limit;
- while (i != idx) {
- u16 len;
- struct sk_buff *skb;
- dma_addr_t dma_addr;
- desc = &ring[i];
- len = le16_to_cpu(desc->len);
- skb = rx_buf[i];
-
- if (!skb) {
- i++;
- i %= ring_limit;
- continue;
- }
-
- if (unlikely(len > priv->common.rx_mtu)) {
- if (net_ratelimit())
- dev_err(&priv->pdev->dev, "rx'd frame size "
- "exceeds length threshold.\n");
-
- len = priv->common.rx_mtu;
- }
- dma_addr = le32_to_cpu(desc->host_addr);
- pci_dma_sync_single_for_cpu(priv->pdev, dma_addr,
- priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE);
- skb_put(skb, len);
-
- if (p54_rx(dev, skb)) {
- pci_unmap_single(priv->pdev, dma_addr,
- priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE);
- rx_buf[i] = NULL;
- desc->host_addr = cpu_to_le32(0);
- } else {
- skb_trim(skb, 0);
- pci_dma_sync_single_for_device(priv->pdev, dma_addr,
- priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE);
- desc->len = cpu_to_le16(priv->common.rx_mtu + 32);
- }
-
- i++;
- i %= ring_limit;
- }
-
- p54p_refill_rx_ring(dev, ring_index, ring, ring_limit, rx_buf, *index);
-}
-
-static void p54p_check_tx_ring(struct ieee80211_hw *dev, u32 *index,
- int ring_index, struct p54p_desc *ring, u32 ring_limit,
- struct sk_buff **tx_buf)
-{
- struct p54p_priv *priv = dev->priv;
- struct p54p_ring_control *ring_control = priv->ring_control;
- struct p54p_desc *desc;
- struct sk_buff *skb;
- u32 idx, i;
-
- i = (*index) % ring_limit;
- (*index) = idx = le32_to_cpu(ring_control->device_idx[ring_index]);
- idx %= ring_limit;
-
- while (i != idx) {
- desc = &ring[i];
-
- skb = tx_buf[i];
- tx_buf[i] = NULL;
-
- pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr),
- le16_to_cpu(desc->len), PCI_DMA_TODEVICE);
-
- desc->host_addr = 0;
- desc->device_addr = 0;
- desc->len = 0;
- desc->flags = 0;
-
- if (skb && FREE_AFTER_TX(skb))
- p54_free_skb(dev, skb);
-
- i++;
- i %= ring_limit;
- }
-}
-
-static void p54p_tasklet(unsigned long dev_id)
-{
- struct ieee80211_hw *dev = (struct ieee80211_hw *)dev_id;
- struct p54p_priv *priv = dev->priv;
- struct p54p_ring_control *ring_control = priv->ring_control;
-
- p54p_check_tx_ring(dev, &priv->tx_idx_mgmt, 3, ring_control->tx_mgmt,
- ARRAY_SIZE(ring_control->tx_mgmt),
- priv->tx_buf_mgmt);
-
- p54p_check_tx_ring(dev, &priv->tx_idx_data, 1, ring_control->tx_data,
- ARRAY_SIZE(ring_control->tx_data),
- priv->tx_buf_data);
-
- p54p_check_rx_ring(dev, &priv->rx_idx_mgmt, 2, ring_control->rx_mgmt,
- ARRAY_SIZE(ring_control->rx_mgmt), priv->rx_buf_mgmt);
-
- p54p_check_rx_ring(dev, &priv->rx_idx_data, 0, ring_control->rx_data,
- ARRAY_SIZE(ring_control->rx_data), priv->rx_buf_data);
-
- wmb();
- P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE));
-}
-
-static irqreturn_t p54p_interrupt(int irq, void *dev_id)
-{
- struct ieee80211_hw *dev = dev_id;
- struct p54p_priv *priv = dev->priv;
- __le32 reg;
-
- reg = P54P_READ(int_ident);
- if (unlikely(reg == cpu_to_le32(0xFFFFFFFF))) {
- goto out;
- }
- P54P_WRITE(int_ack, reg);
-
- reg &= P54P_READ(int_enable);
-
- if (reg & cpu_to_le32(ISL38XX_INT_IDENT_UPDATE))
- tasklet_schedule(&priv->tasklet);
- else if (reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT))
- complete(&priv->boot_comp);
-
-out:
- return reg ? IRQ_HANDLED : IRQ_NONE;
-}
-
-static void p54p_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
-{
- unsigned long flags;
- struct p54p_priv *priv = dev->priv;
- struct p54p_ring_control *ring_control = priv->ring_control;
- struct p54p_desc *desc;
- dma_addr_t mapping;
- u32 idx, i;
-
- spin_lock_irqsave(&priv->lock, flags);
- idx = le32_to_cpu(ring_control->host_idx[1]);
- i = idx % ARRAY_SIZE(ring_control->tx_data);
-
- mapping = pci_map_single(priv->pdev, skb->data, skb->len,
- PCI_DMA_TODEVICE);
- if (pci_dma_mapping_error(priv->pdev, mapping)) {
- spin_unlock_irqrestore(&priv->lock, flags);
- p54_free_skb(dev, skb);
- dev_err(&priv->pdev->dev, "TX DMA mapping error\n");
- return ;
- }
- priv->tx_buf_data[i] = skb;
-
- desc = &ring_control->tx_data[i];
- desc->host_addr = cpu_to_le32(mapping);
- desc->device_addr = ((struct p54_hdr *)skb->data)->req_id;
- desc->len = cpu_to_le16(skb->len);
- desc->flags = 0;
-
- wmb();
- ring_control->host_idx[1] = cpu_to_le32(idx + 1);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE));
- P54P_READ(dev_int);
-}
-
-static void p54p_stop(struct ieee80211_hw *dev)
-{
- struct p54p_priv *priv = dev->priv;
- struct p54p_ring_control *ring_control = priv->ring_control;
- unsigned int i;
- struct p54p_desc *desc;
-
- P54P_WRITE(int_enable, cpu_to_le32(0));
- P54P_READ(int_enable);
- udelay(10);
-
- free_irq(priv->pdev->irq, dev);
-
- tasklet_kill(&priv->tasklet);
-
- P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET));
-
- for (i = 0; i < ARRAY_SIZE(priv->rx_buf_data); i++) {
- desc = &ring_control->rx_data[i];
- if (desc->host_addr)
- pci_unmap_single(priv->pdev,
- le32_to_cpu(desc->host_addr),
- priv->common.rx_mtu + 32,
- PCI_DMA_FROMDEVICE);
- kfree_skb(priv->rx_buf_data[i]);
- priv->rx_buf_data[i] = NULL;
- }
-
- for (i = 0; i < ARRAY_SIZE(priv->rx_buf_mgmt); i++) {
- desc = &ring_control->rx_mgmt[i];
- if (desc->host_addr)
- pci_unmap_single(priv->pdev,
- le32_to_cpu(desc->host_addr),
- priv->common.rx_mtu + 32,
- PCI_DMA_FROMDEVICE);
- kfree_skb(priv->rx_buf_mgmt[i]);
- priv->rx_buf_mgmt[i] = NULL;
- }
-
- for (i = 0; i < ARRAY_SIZE(priv->tx_buf_data); i++) {
- desc = &ring_control->tx_data[i];
- if (desc->host_addr)
- pci_unmap_single(priv->pdev,
- le32_to_cpu(desc->host_addr),
- le16_to_cpu(desc->len),
- PCI_DMA_TODEVICE);
-
- p54_free_skb(dev, priv->tx_buf_data[i]);
- priv->tx_buf_data[i] = NULL;
- }
-
- for (i = 0; i < ARRAY_SIZE(priv->tx_buf_mgmt); i++) {
- desc = &ring_control->tx_mgmt[i];
- if (desc->host_addr)
- pci_unmap_single(priv->pdev,
- le32_to_cpu(desc->host_addr),
- le16_to_cpu(desc->len),
- PCI_DMA_TODEVICE);
-
- p54_free_skb(dev, priv->tx_buf_mgmt[i]);
- priv->tx_buf_mgmt[i] = NULL;
- }
-
- memset(ring_control, 0, sizeof(*ring_control));
-}
-
-static int p54p_open(struct ieee80211_hw *dev)
-{
- struct p54p_priv *priv = dev->priv;
- int err;
- long timeout;
-
- init_completion(&priv->boot_comp);
- err = request_irq(priv->pdev->irq, p54p_interrupt,
- IRQF_SHARED, "p54pci", dev);
- if (err) {
- dev_err(&priv->pdev->dev, "failed to register IRQ handler\n");
- return err;
- }
-
- memset(priv->ring_control, 0, sizeof(*priv->ring_control));
- err = p54p_upload_firmware(dev);
- if (err) {
- free_irq(priv->pdev->irq, dev);
- return err;
- }
- priv->rx_idx_data = priv->tx_idx_data = 0;
- priv->rx_idx_mgmt = priv->tx_idx_mgmt = 0;
-
- p54p_refill_rx_ring(dev, 0, priv->ring_control->rx_data,
- ARRAY_SIZE(priv->ring_control->rx_data), priv->rx_buf_data, 0);
-
- p54p_refill_rx_ring(dev, 2, priv->ring_control->rx_mgmt,
- ARRAY_SIZE(priv->ring_control->rx_mgmt), priv->rx_buf_mgmt, 0);
-
- P54P_WRITE(ring_control_base, cpu_to_le32(priv->ring_control_dma));
- P54P_READ(ring_control_base);
- wmb();
- udelay(10);
-
- P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_INIT));
- P54P_READ(int_enable);
- wmb();
- udelay(10);
-
- P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET));
- P54P_READ(dev_int);
-
- timeout = wait_for_completion_interruptible_timeout(
- &priv->boot_comp, HZ);
- if (timeout <= 0) {
- wiphy_err(dev->wiphy, "Cannot boot firmware!\n");
- p54p_stop(dev);
- return timeout ? -ERESTARTSYS : -ETIMEDOUT;
- }
-
- P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_UPDATE));
- P54P_READ(int_enable);
- wmb();
- udelay(10);
-
- P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE));
- P54P_READ(dev_int);
- wmb();
- udelay(10);
-
- return 0;
-}
-
-static void p54p_firmware_step2(const struct firmware *fw,
- void *context)
-{
- struct p54p_priv *priv = context;
- struct ieee80211_hw *dev = priv->common.hw;
- struct pci_dev *pdev = priv->pdev;
- int err;
-
- if (!fw) {
- dev_err(&pdev->dev, "Cannot find firmware (isl3886pci)\n");
- err = -ENOENT;
- goto out;
- }
-
- priv->firmware = fw;
-
- err = p54p_open(dev);
- if (err)
- goto out;
- err = p54_read_eeprom(dev);
- p54p_stop(dev);
- if (err)
- goto out;
-
- err = p54_register_common(dev, &pdev->dev);
- if (err)
- goto out;
-
-out:
-
- complete(&priv->fw_loaded);
-
- if (err) {
- struct device *parent = pdev->dev.parent;
-
- if (parent)
- device_lock(parent);
-
- /*
- * This will indirectly result in a call to p54p_remove.
- * Hence, we don't need to bother with freeing any
- * allocated ressources at all.
- */
- device_release_driver(&pdev->dev);
-
- if (parent)
- device_unlock(parent);
- }
-
- pci_dev_put(pdev);
-}
-
-static int p54p_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
-{
- struct p54p_priv *priv;
- struct ieee80211_hw *dev;
- unsigned long mem_addr, mem_len;
- int err;
-
- pci_dev_get(pdev);
- err = pci_enable_device(pdev);
- if (err) {
- dev_err(&pdev->dev, "Cannot enable new PCI device\n");
- return err;
- }
-
- mem_addr = pci_resource_start(pdev, 0);
- mem_len = pci_resource_len(pdev, 0);
- if (mem_len < sizeof(struct p54p_csr)) {
- dev_err(&pdev->dev, "Too short PCI resources\n");
- err = -ENODEV;
- goto err_disable_dev;
- }
-
- err = pci_request_regions(pdev, "p54pci");
- if (err) {
- dev_err(&pdev->dev, "Cannot obtain PCI resources\n");
- goto err_disable_dev;
- }
-
- err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
- if (!err)
- err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
- if (err) {
- dev_err(&pdev->dev, "No suitable DMA available\n");
- goto err_free_reg;
- }
-
- pci_set_master(pdev);
- pci_try_set_mwi(pdev);
-
- pci_write_config_byte(pdev, 0x40, 0);
- pci_write_config_byte(pdev, 0x41, 0);
-
- dev = p54_init_common(sizeof(*priv));
- if (!dev) {
- dev_err(&pdev->dev, "ieee80211 alloc failed\n");
- err = -ENOMEM;
- goto err_free_reg;
- }
-
- priv = dev->priv;
- priv->pdev = pdev;
-
- init_completion(&priv->fw_loaded);
- SET_IEEE80211_DEV(dev, &pdev->dev);
- pci_set_drvdata(pdev, dev);
-
- priv->map = ioremap(mem_addr, mem_len);
- if (!priv->map) {
- dev_err(&pdev->dev, "Cannot map device memory\n");
- err = -ENOMEM;
- goto err_free_dev;
- }
-
- priv->ring_control = pci_alloc_consistent(pdev, sizeof(*priv->ring_control),
- &priv->ring_control_dma);
- if (!priv->ring_control) {
- dev_err(&pdev->dev, "Cannot allocate rings\n");
- err = -ENOMEM;
- goto err_iounmap;
- }
- priv->common.open = p54p_open;
- priv->common.stop = p54p_stop;
- priv->common.tx = p54p_tx;
-
- spin_lock_init(&priv->lock);
- tasklet_init(&priv->tasklet, p54p_tasklet, (unsigned long)dev);
-
- err = request_firmware_nowait(THIS_MODULE, 1, "isl3886pci",
- &priv->pdev->dev, GFP_KERNEL,
- priv, p54p_firmware_step2);
- if (!err)
- return 0;
-
- pci_free_consistent(pdev, sizeof(*priv->ring_control),
- priv->ring_control, priv->ring_control_dma);
-
- err_iounmap:
- iounmap(priv->map);
-
- err_free_dev:
- p54_free_common(dev);
-
- err_free_reg:
- pci_release_regions(pdev);
- err_disable_dev:
- pci_disable_device(pdev);
- pci_dev_put(pdev);
- return err;
-}
-
-static void p54p_remove(struct pci_dev *pdev)
-{
- struct ieee80211_hw *dev = pci_get_drvdata(pdev);
- struct p54p_priv *priv;
-
- if (!dev)
- return;
-
- priv = dev->priv;
- wait_for_completion(&priv->fw_loaded);
- p54_unregister_common(dev);
- release_firmware(priv->firmware);
- pci_free_consistent(pdev, sizeof(*priv->ring_control),
- priv->ring_control, priv->ring_control_dma);
- iounmap(priv->map);
- pci_release_regions(pdev);
- pci_disable_device(pdev);
- p54_free_common(dev);
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int p54p_suspend(struct device *device)
-{
- struct pci_dev *pdev = to_pci_dev(device);
-
- pci_save_state(pdev);
- pci_set_power_state(pdev, PCI_D3hot);
- pci_disable_device(pdev);
- return 0;
-}
-
-static int p54p_resume(struct device *device)
-{
- struct pci_dev *pdev = to_pci_dev(device);
- int err;
-
- err = pci_reenable_device(pdev);
- if (err)
- return err;
- return pci_set_power_state(pdev, PCI_D0);
-}
-
-static SIMPLE_DEV_PM_OPS(p54pci_pm_ops, p54p_suspend, p54p_resume);
-
-#define P54P_PM_OPS (&p54pci_pm_ops)
-#else
-#define P54P_PM_OPS (NULL)
-#endif /* CONFIG_PM_SLEEP */
-
-static struct pci_driver p54p_driver = {
- .name = "p54pci",
- .id_table = p54p_table,
- .probe = p54p_probe,
- .remove = p54p_remove,
- .driver.pm = P54P_PM_OPS,
-};
-
-module_pci_driver(p54p_driver);
+++ /dev/null
-#ifndef P54PCI_H
-#define P54PCI_H
-#include <linux/interrupt.h>
-
-/*
- * Defines for PCI based mac80211 Prism54 driver
- *
- * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
- *
- * Based on the islsm (softmac prism54) driver, which is:
- * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-/* Device Interrupt register bits */
-#define ISL38XX_DEV_INT_RESET 0x0001
-#define ISL38XX_DEV_INT_UPDATE 0x0002
-#define ISL38XX_DEV_INT_WAKEUP 0x0008
-#define ISL38XX_DEV_INT_SLEEP 0x0010
-#define ISL38XX_DEV_INT_ABORT 0x0020
-/* these two only used in USB */
-#define ISL38XX_DEV_INT_DATA 0x0040
-#define ISL38XX_DEV_INT_MGMT 0x0080
-
-#define ISL38XX_DEV_INT_PCIUART_CTS 0x4000
-#define ISL38XX_DEV_INT_PCIUART_DR 0x8000
-
-/* Interrupt Identification/Acknowledge/Enable register bits */
-#define ISL38XX_INT_IDENT_UPDATE 0x0002
-#define ISL38XX_INT_IDENT_INIT 0x0004
-#define ISL38XX_INT_IDENT_WAKEUP 0x0008
-#define ISL38XX_INT_IDENT_SLEEP 0x0010
-#define ISL38XX_INT_IDENT_PCIUART_CTS 0x4000
-#define ISL38XX_INT_IDENT_PCIUART_DR 0x8000
-
-/* Control/Status register bits */
-#define ISL38XX_CTRL_STAT_SLEEPMODE 0x00000200
-#define ISL38XX_CTRL_STAT_CLKRUN 0x00800000
-#define ISL38XX_CTRL_STAT_RESET 0x10000000
-#define ISL38XX_CTRL_STAT_RAMBOOT 0x20000000
-#define ISL38XX_CTRL_STAT_STARTHALTED 0x40000000
-#define ISL38XX_CTRL_STAT_HOST_OVERRIDE 0x80000000
-
-struct p54p_csr {
- __le32 dev_int;
- u8 unused_1[12];
- __le32 int_ident;
- __le32 int_ack;
- __le32 int_enable;
- u8 unused_2[4];
- union {
- __le32 ring_control_base;
- __le32 gen_purp_com[2];
- };
- u8 unused_3[8];
- __le32 direct_mem_base;
- u8 unused_4[44];
- __le32 dma_addr;
- __le32 dma_len;
- __le32 dma_ctrl;
- u8 unused_5[12];
- __le32 ctrl_stat;
- u8 unused_6[1924];
- u8 cardbus_cis[0x800];
- u8 direct_mem_win[0x1000];
-} __packed;
-
-/* usb backend only needs the register defines above */
-#ifndef P54USB_H
-struct p54p_desc {
- __le32 host_addr;
- __le32 device_addr;
- __le16 len;
- __le16 flags;
-} __packed;
-
-struct p54p_ring_control {
- __le32 host_idx[4];
- __le32 device_idx[4];
- struct p54p_desc rx_data[8];
- struct p54p_desc tx_data[32];
- struct p54p_desc rx_mgmt[4];
- struct p54p_desc tx_mgmt[4];
-} __packed;
-
-#define P54P_READ(r) (__force __le32)__raw_readl(&priv->map->r)
-#define P54P_WRITE(r, val) __raw_writel((__force u32)(__le32)(val), &priv->map->r)
-
-struct p54p_priv {
- struct p54_common common;
- struct pci_dev *pdev;
- struct p54p_csr __iomem *map;
- struct tasklet_struct tasklet;
- const struct firmware *firmware;
- spinlock_t lock;
- struct p54p_ring_control *ring_control;
- dma_addr_t ring_control_dma;
- u32 rx_idx_data, tx_idx_data;
- u32 rx_idx_mgmt, tx_idx_mgmt;
- struct sk_buff *rx_buf_data[8];
- struct sk_buff *rx_buf_mgmt[4];
- struct sk_buff *tx_buf_data[32];
- struct sk_buff *tx_buf_mgmt[4];
- struct completion boot_comp;
- struct completion fw_loaded;
-};
-
-#endif /* P54USB_H */
-#endif /* P54PCI_H */
+++ /dev/null
-/*
- * Copyright (C) 2008 Christian Lamparter <chunkeey@web.de>
- * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
- *
- * This driver is a port from stlc45xx:
- * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/firmware.h>
-#include <linux/delay.h>
-#include <linux/irq.h>
-#include <linux/spi/spi.h>
-#include <linux/etherdevice.h>
-#include <linux/gpio.h>
-#include <linux/slab.h>
-
-#include "p54spi.h"
-#include "p54.h"
-
-#include "lmac.h"
-
-#ifdef CONFIG_P54_SPI_DEFAULT_EEPROM
-#include "p54spi_eeprom.h"
-#endif /* CONFIG_P54_SPI_DEFAULT_EEPROM */
-
-MODULE_FIRMWARE("3826.arm");
-
-/* gpios should be handled in board files and provided via platform data,
- * but because it's currently impossible for p54spi to have a header file
- * in include/linux, let's use module paramaters for now
- */
-
-static int p54spi_gpio_power = 97;
-module_param(p54spi_gpio_power, int, 0444);
-MODULE_PARM_DESC(p54spi_gpio_power, "gpio number for power line");
-
-static int p54spi_gpio_irq = 87;
-module_param(p54spi_gpio_irq, int, 0444);
-MODULE_PARM_DESC(p54spi_gpio_irq, "gpio number for irq line");
-
-static void p54spi_spi_read(struct p54s_priv *priv, u8 address,
- void *buf, size_t len)
-{
- struct spi_transfer t[2];
- struct spi_message m;
- __le16 addr;
-
- /* We first push the address */
- addr = cpu_to_le16(address << 8 | SPI_ADRS_READ_BIT_15);
-
- spi_message_init(&m);
- memset(t, 0, sizeof(t));
-
- t[0].tx_buf = &addr;
- t[0].len = sizeof(addr);
- spi_message_add_tail(&t[0], &m);
-
- t[1].rx_buf = buf;
- t[1].len = len;
- spi_message_add_tail(&t[1], &m);
-
- spi_sync(priv->spi, &m);
-}
-
-
-static void p54spi_spi_write(struct p54s_priv *priv, u8 address,
- const void *buf, size_t len)
-{
- struct spi_transfer t[3];
- struct spi_message m;
- __le16 addr;
-
- /* We first push the address */
- addr = cpu_to_le16(address << 8);
-
- spi_message_init(&m);
- memset(t, 0, sizeof(t));
-
- t[0].tx_buf = &addr;
- t[0].len = sizeof(addr);
- spi_message_add_tail(&t[0], &m);
-
- t[1].tx_buf = buf;
- t[1].len = len & ~1;
- spi_message_add_tail(&t[1], &m);
-
- if (len % 2) {
- __le16 last_word;
- last_word = cpu_to_le16(((u8 *)buf)[len - 1]);
-
- t[2].tx_buf = &last_word;
- t[2].len = sizeof(last_word);
- spi_message_add_tail(&t[2], &m);
- }
-
- spi_sync(priv->spi, &m);
-}
-
-static u32 p54spi_read32(struct p54s_priv *priv, u8 addr)
-{
- __le32 val;
-
- p54spi_spi_read(priv, addr, &val, sizeof(val));
-
- return le32_to_cpu(val);
-}
-
-static inline void p54spi_write16(struct p54s_priv *priv, u8 addr, __le16 val)
-{
- p54spi_spi_write(priv, addr, &val, sizeof(val));
-}
-
-static inline void p54spi_write32(struct p54s_priv *priv, u8 addr, __le32 val)
-{
- p54spi_spi_write(priv, addr, &val, sizeof(val));
-}
-
-static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, u32 bits)
-{
- int i;
-
- for (i = 0; i < 2000; i++) {
- u32 buffer = p54spi_read32(priv, reg);
- if ((buffer & bits) == bits)
- return 1;
- }
- return 0;
-}
-
-static int p54spi_spi_write_dma(struct p54s_priv *priv, __le32 base,
- const void *buf, size_t len)
-{
- if (!p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL, HOST_ALLOWED)) {
- dev_err(&priv->spi->dev, "spi_write_dma not allowed "
- "to DMA write.\n");
- return -EAGAIN;
- }
-
- p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL,
- cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE));
-
- p54spi_write16(priv, SPI_ADRS_DMA_WRITE_LEN, cpu_to_le16(len));
- p54spi_write32(priv, SPI_ADRS_DMA_WRITE_BASE, base);
- p54spi_spi_write(priv, SPI_ADRS_DMA_DATA, buf, len);
- return 0;
-}
-
-static int p54spi_request_firmware(struct ieee80211_hw *dev)
-{
- struct p54s_priv *priv = dev->priv;
- int ret;
-
- /* FIXME: should driver use it's own struct device? */
- ret = request_firmware(&priv->firmware, "3826.arm", &priv->spi->dev);
-
- if (ret < 0) {
- dev_err(&priv->spi->dev, "request_firmware() failed: %d", ret);
- return ret;
- }
-
- ret = p54_parse_firmware(dev, priv->firmware);
- if (ret) {
- release_firmware(priv->firmware);
- return ret;
- }
-
- return 0;
-}
-
-static int p54spi_request_eeprom(struct ieee80211_hw *dev)
-{
- struct p54s_priv *priv = dev->priv;
- const struct firmware *eeprom;
- int ret;
-
- /* allow users to customize their eeprom.
- */
-
- ret = request_firmware_direct(&eeprom, "3826.eeprom", &priv->spi->dev);
- if (ret < 0) {
-#ifdef CONFIG_P54_SPI_DEFAULT_EEPROM
- dev_info(&priv->spi->dev, "loading default eeprom...\n");
- ret = p54_parse_eeprom(dev, (void *) p54spi_eeprom,
- sizeof(p54spi_eeprom));
-#else
- dev_err(&priv->spi->dev, "Failed to request user eeprom\n");
-#endif /* CONFIG_P54_SPI_DEFAULT_EEPROM */
- } else {
- dev_info(&priv->spi->dev, "loading user eeprom...\n");
- ret = p54_parse_eeprom(dev, (void *) eeprom->data,
- (int)eeprom->size);
- release_firmware(eeprom);
- }
- return ret;
-}
-
-static int p54spi_upload_firmware(struct ieee80211_hw *dev)
-{
- struct p54s_priv *priv = dev->priv;
- unsigned long fw_len, _fw_len;
- unsigned int offset = 0;
- int err = 0;
- u8 *fw;
-
- fw_len = priv->firmware->size;
- fw = kmemdup(priv->firmware->data, fw_len, GFP_KERNEL);
- if (!fw)
- return -ENOMEM;
-
- /* stop the device */
- p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16(
- SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET |
- SPI_CTRL_STAT_START_HALTED));
-
- msleep(TARGET_BOOT_SLEEP);
-
- p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16(
- SPI_CTRL_STAT_HOST_OVERRIDE |
- SPI_CTRL_STAT_START_HALTED));
-
- msleep(TARGET_BOOT_SLEEP);
-
- while (fw_len > 0) {
- _fw_len = min_t(long, fw_len, SPI_MAX_PACKET_SIZE);
-
- err = p54spi_spi_write_dma(priv, cpu_to_le32(
- ISL38XX_DEV_FIRMWARE_ADDR + offset),
- (fw + offset), _fw_len);
- if (err < 0)
- goto out;
-
- fw_len -= _fw_len;
- offset += _fw_len;
- }
-
- BUG_ON(fw_len != 0);
-
- /* enable host interrupts */
- p54spi_write32(priv, SPI_ADRS_HOST_INT_EN,
- cpu_to_le32(SPI_HOST_INTS_DEFAULT));
-
- /* boot the device */
- p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16(
- SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET |
- SPI_CTRL_STAT_RAM_BOOT));
-
- msleep(TARGET_BOOT_SLEEP);
-
- p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16(
- SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_RAM_BOOT));
- msleep(TARGET_BOOT_SLEEP);
-
-out:
- kfree(fw);
- return err;
-}
-
-static void p54spi_power_off(struct p54s_priv *priv)
-{
- disable_irq(gpio_to_irq(p54spi_gpio_irq));
- gpio_set_value(p54spi_gpio_power, 0);
-}
-
-static void p54spi_power_on(struct p54s_priv *priv)
-{
- gpio_set_value(p54spi_gpio_power, 1);
- enable_irq(gpio_to_irq(p54spi_gpio_irq));
-
- /* need to wait a while before device can be accessed, the length
- * is just a guess
- */
- msleep(10);
-}
-
-static inline void p54spi_int_ack(struct p54s_priv *priv, u32 val)
-{
- p54spi_write32(priv, SPI_ADRS_HOST_INT_ACK, cpu_to_le32(val));
-}
-
-static int p54spi_wakeup(struct p54s_priv *priv)
-{
- /* wake the chip */
- p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS,
- cpu_to_le32(SPI_TARGET_INT_WAKEUP));
-
- /* And wait for the READY interrupt */
- if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS,
- SPI_HOST_INT_READY)) {
- dev_err(&priv->spi->dev, "INT_READY timeout\n");
- return -EBUSY;
- }
-
- p54spi_int_ack(priv, SPI_HOST_INT_READY);
- return 0;
-}
-
-static inline void p54spi_sleep(struct p54s_priv *priv)
-{
- p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS,
- cpu_to_le32(SPI_TARGET_INT_SLEEP));
-}
-
-static void p54spi_int_ready(struct p54s_priv *priv)
-{
- p54spi_write32(priv, SPI_ADRS_HOST_INT_EN, cpu_to_le32(
- SPI_HOST_INT_UPDATE | SPI_HOST_INT_SW_UPDATE));
-
- switch (priv->fw_state) {
- case FW_STATE_BOOTING:
- priv->fw_state = FW_STATE_READY;
- complete(&priv->fw_comp);
- break;
- case FW_STATE_RESETTING:
- priv->fw_state = FW_STATE_READY;
- /* TODO: reinitialize state */
- break;
- default:
- break;
- }
-}
-
-static int p54spi_rx(struct p54s_priv *priv)
-{
- struct sk_buff *skb;
- u16 len;
- u16 rx_head[2];
-#define READAHEAD_SZ (sizeof(rx_head)-sizeof(u16))
-
- if (p54spi_wakeup(priv) < 0)
- return -EBUSY;
-
- /* Read data size and first data word in one SPI transaction
- * This is workaround for firmware/DMA bug,
- * when first data word gets lost under high load.
- */
- p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, rx_head, sizeof(rx_head));
- len = rx_head[0];
-
- if (len == 0) {
- p54spi_sleep(priv);
- dev_err(&priv->spi->dev, "rx request of zero bytes\n");
- return 0;
- }
-
- /* Firmware may insert up to 4 padding bytes after the lmac header,
- * but it does not amend the size of SPI data transfer.
- * Such packets has correct data size in header, thus referencing
- * past the end of allocated skb. Reserve extra 4 bytes for this case
- */
- skb = dev_alloc_skb(len + 4);
- if (!skb) {
- p54spi_sleep(priv);
- dev_err(&priv->spi->dev, "could not alloc skb");
- return -ENOMEM;
- }
-
- if (len <= READAHEAD_SZ) {
- memcpy(skb_put(skb, len), rx_head + 1, len);
- } else {
- memcpy(skb_put(skb, READAHEAD_SZ), rx_head + 1, READAHEAD_SZ);
- p54spi_spi_read(priv, SPI_ADRS_DMA_DATA,
- skb_put(skb, len - READAHEAD_SZ),
- len - READAHEAD_SZ);
- }
- p54spi_sleep(priv);
- /* Put additional bytes to compensate for the possible
- * alignment-caused truncation
- */
- skb_put(skb, 4);
-
- if (p54_rx(priv->hw, skb) == 0)
- dev_kfree_skb(skb);
-
- return 0;
-}
-
-
-static irqreturn_t p54spi_interrupt(int irq, void *config)
-{
- struct spi_device *spi = config;
- struct p54s_priv *priv = spi_get_drvdata(spi);
-
- ieee80211_queue_work(priv->hw, &priv->work);
-
- return IRQ_HANDLED;
-}
-
-static int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb)
-{
- struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
- int ret = 0;
-
- if (p54spi_wakeup(priv) < 0)
- return -EBUSY;
-
- ret = p54spi_spi_write_dma(priv, hdr->req_id, skb->data, skb->len);
- if (ret < 0)
- goto out;
-
- if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS,
- SPI_HOST_INT_WR_READY)) {
- dev_err(&priv->spi->dev, "WR_READY timeout\n");
- ret = -EAGAIN;
- goto out;
- }
-
- p54spi_int_ack(priv, SPI_HOST_INT_WR_READY);
-
- if (FREE_AFTER_TX(skb))
- p54_free_skb(priv->hw, skb);
-out:
- p54spi_sleep(priv);
- return ret;
-}
-
-static int p54spi_wq_tx(struct p54s_priv *priv)
-{
- struct p54s_tx_info *entry;
- struct sk_buff *skb;
- struct ieee80211_tx_info *info;
- struct p54_tx_info *minfo;
- struct p54s_tx_info *dinfo;
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&priv->tx_lock, flags);
-
- while (!list_empty(&priv->tx_pending)) {
- entry = list_entry(priv->tx_pending.next,
- struct p54s_tx_info, tx_list);
-
- list_del_init(&entry->tx_list);
-
- spin_unlock_irqrestore(&priv->tx_lock, flags);
-
- dinfo = container_of((void *) entry, struct p54s_tx_info,
- tx_list);
- minfo = container_of((void *) dinfo, struct p54_tx_info,
- data);
- info = container_of((void *) minfo, struct ieee80211_tx_info,
- rate_driver_data);
- skb = container_of((void *) info, struct sk_buff, cb);
-
- ret = p54spi_tx_frame(priv, skb);
-
- if (ret < 0) {
- p54_free_skb(priv->hw, skb);
- return ret;
- }
-
- spin_lock_irqsave(&priv->tx_lock, flags);
- }
- spin_unlock_irqrestore(&priv->tx_lock, flags);
- return ret;
-}
-
-static void p54spi_op_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
-{
- struct p54s_priv *priv = dev->priv;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct p54_tx_info *mi = (struct p54_tx_info *) info->rate_driver_data;
- struct p54s_tx_info *di = (struct p54s_tx_info *) mi->data;
- unsigned long flags;
-
- BUILD_BUG_ON(sizeof(*di) > sizeof((mi->data)));
-
- spin_lock_irqsave(&priv->tx_lock, flags);
- list_add_tail(&di->tx_list, &priv->tx_pending);
- spin_unlock_irqrestore(&priv->tx_lock, flags);
-
- ieee80211_queue_work(priv->hw, &priv->work);
-}
-
-static void p54spi_work(struct work_struct *work)
-{
- struct p54s_priv *priv = container_of(work, struct p54s_priv, work);
- u32 ints;
- int ret;
-
- mutex_lock(&priv->mutex);
-
- if (priv->fw_state == FW_STATE_OFF)
- goto out;
-
- ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS);
-
- if (ints & SPI_HOST_INT_READY) {
- p54spi_int_ready(priv);
- p54spi_int_ack(priv, SPI_HOST_INT_READY);
- }
-
- if (priv->fw_state != FW_STATE_READY)
- goto out;
-
- if (ints & SPI_HOST_INT_UPDATE) {
- p54spi_int_ack(priv, SPI_HOST_INT_UPDATE);
- ret = p54spi_rx(priv);
- if (ret < 0)
- goto out;
- }
- if (ints & SPI_HOST_INT_SW_UPDATE) {
- p54spi_int_ack(priv, SPI_HOST_INT_SW_UPDATE);
- ret = p54spi_rx(priv);
- if (ret < 0)
- goto out;
- }
-
- ret = p54spi_wq_tx(priv);
-out:
- mutex_unlock(&priv->mutex);
-}
-
-static int p54spi_op_start(struct ieee80211_hw *dev)
-{
- struct p54s_priv *priv = dev->priv;
- unsigned long timeout;
- int ret = 0;
-
- if (mutex_lock_interruptible(&priv->mutex)) {
- ret = -EINTR;
- goto out;
- }
-
- priv->fw_state = FW_STATE_BOOTING;
-
- p54spi_power_on(priv);
-
- ret = p54spi_upload_firmware(dev);
- if (ret < 0) {
- p54spi_power_off(priv);
- goto out_unlock;
- }
-
- mutex_unlock(&priv->mutex);
-
- timeout = msecs_to_jiffies(2000);
- timeout = wait_for_completion_interruptible_timeout(&priv->fw_comp,
- timeout);
- if (!timeout) {
- dev_err(&priv->spi->dev, "firmware boot failed");
- p54spi_power_off(priv);
- ret = -1;
- goto out;
- }
-
- if (mutex_lock_interruptible(&priv->mutex)) {
- ret = -EINTR;
- p54spi_power_off(priv);
- goto out;
- }
-
- WARN_ON(priv->fw_state != FW_STATE_READY);
-
-out_unlock:
- mutex_unlock(&priv->mutex);
-
-out:
- return ret;
-}
-
-static void p54spi_op_stop(struct ieee80211_hw *dev)
-{
- struct p54s_priv *priv = dev->priv;
- unsigned long flags;
-
- mutex_lock(&priv->mutex);
- WARN_ON(priv->fw_state != FW_STATE_READY);
-
- p54spi_power_off(priv);
- spin_lock_irqsave(&priv->tx_lock, flags);
- INIT_LIST_HEAD(&priv->tx_pending);
- spin_unlock_irqrestore(&priv->tx_lock, flags);
-
- priv->fw_state = FW_STATE_OFF;
- mutex_unlock(&priv->mutex);
-
- cancel_work_sync(&priv->work);
-}
-
-static int p54spi_probe(struct spi_device *spi)
-{
- struct p54s_priv *priv = NULL;
- struct ieee80211_hw *hw;
- int ret = -EINVAL;
-
- hw = p54_init_common(sizeof(*priv));
- if (!hw) {
- dev_err(&spi->dev, "could not alloc ieee80211_hw");
- return -ENOMEM;
- }
-
- priv = hw->priv;
- priv->hw = hw;
- spi_set_drvdata(spi, priv);
- priv->spi = spi;
-
- spi->bits_per_word = 16;
- spi->max_speed_hz = 24000000;
-
- ret = spi_setup(spi);
- if (ret < 0) {
- dev_err(&priv->spi->dev, "spi_setup failed");
- goto err_free;
- }
-
- ret = gpio_request(p54spi_gpio_power, "p54spi power");
- if (ret < 0) {
- dev_err(&priv->spi->dev, "power GPIO request failed: %d", ret);
- goto err_free;
- }
-
- ret = gpio_request(p54spi_gpio_irq, "p54spi irq");
- if (ret < 0) {
- dev_err(&priv->spi->dev, "irq GPIO request failed: %d", ret);
- goto err_free_gpio_power;
- }
-
- gpio_direction_output(p54spi_gpio_power, 0);
- gpio_direction_input(p54spi_gpio_irq);
-
- ret = request_irq(gpio_to_irq(p54spi_gpio_irq),
- p54spi_interrupt, 0, "p54spi",
- priv->spi);
- if (ret < 0) {
- dev_err(&priv->spi->dev, "request_irq() failed");
- goto err_free_gpio_irq;
- }
-
- irq_set_irq_type(gpio_to_irq(p54spi_gpio_irq), IRQ_TYPE_EDGE_RISING);
-
- disable_irq(gpio_to_irq(p54spi_gpio_irq));
-
- INIT_WORK(&priv->work, p54spi_work);
- init_completion(&priv->fw_comp);
- INIT_LIST_HEAD(&priv->tx_pending);
- mutex_init(&priv->mutex);
- spin_lock_init(&priv->tx_lock);
- SET_IEEE80211_DEV(hw, &spi->dev);
- priv->common.open = p54spi_op_start;
- priv->common.stop = p54spi_op_stop;
- priv->common.tx = p54spi_op_tx;
-
- ret = p54spi_request_firmware(hw);
- if (ret < 0)
- goto err_free_common;
-
- ret = p54spi_request_eeprom(hw);
- if (ret)
- goto err_free_common;
-
- ret = p54_register_common(hw, &priv->spi->dev);
- if (ret)
- goto err_free_common;
-
- return 0;
-
-err_free_common:
- free_irq(gpio_to_irq(p54spi_gpio_irq), spi);
-err_free_gpio_irq:
- gpio_free(p54spi_gpio_irq);
-err_free_gpio_power:
- gpio_free(p54spi_gpio_power);
-err_free:
- p54_free_common(priv->hw);
- return ret;
-}
-
-static int p54spi_remove(struct spi_device *spi)
-{
- struct p54s_priv *priv = spi_get_drvdata(spi);
-
- p54_unregister_common(priv->hw);
-
- free_irq(gpio_to_irq(p54spi_gpio_irq), spi);
-
- gpio_free(p54spi_gpio_power);
- gpio_free(p54spi_gpio_irq);
- release_firmware(priv->firmware);
-
- mutex_destroy(&priv->mutex);
-
- p54_free_common(priv->hw);
-
- return 0;
-}
-
-
-static struct spi_driver p54spi_driver = {
- .driver = {
- .name = "p54spi",
- },
-
- .probe = p54spi_probe,
- .remove = p54spi_remove,
-};
-
-module_spi_driver(p54spi_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>");
-MODULE_ALIAS("spi:cx3110x");
-MODULE_ALIAS("spi:p54spi");
-MODULE_ALIAS("spi:stlc45xx");
+++ /dev/null
-/*
- * Copyright (C) 2008 Christian Lamparter <chunkeey@web.de>
- *
- * This driver is a port from stlc45xx:
- * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#ifndef P54SPI_H
-#define P54SPI_H
-
-#include <linux/mutex.h>
-#include <linux/list.h>
-#include <net/mac80211.h>
-
-#include "p54.h"
-
-/* Bit 15 is read/write bit; ON = READ, OFF = WRITE */
-#define SPI_ADRS_READ_BIT_15 0x8000
-
-#define SPI_ADRS_ARM_INTERRUPTS 0x00
-#define SPI_ADRS_ARM_INT_EN 0x04
-
-#define SPI_ADRS_HOST_INTERRUPTS 0x08
-#define SPI_ADRS_HOST_INT_EN 0x0c
-#define SPI_ADRS_HOST_INT_ACK 0x10
-
-#define SPI_ADRS_GEN_PURP_1 0x14
-#define SPI_ADRS_GEN_PURP_2 0x18
-
-#define SPI_ADRS_DEV_CTRL_STAT 0x26 /* high word */
-
-#define SPI_ADRS_DMA_DATA 0x28
-
-#define SPI_ADRS_DMA_WRITE_CTRL 0x2c
-#define SPI_ADRS_DMA_WRITE_LEN 0x2e
-#define SPI_ADRS_DMA_WRITE_BASE 0x30
-
-#define SPI_ADRS_DMA_READ_CTRL 0x34
-#define SPI_ADRS_DMA_READ_LEN 0x36
-#define SPI_ADRS_DMA_READ_BASE 0x38
-
-#define SPI_CTRL_STAT_HOST_OVERRIDE 0x8000
-#define SPI_CTRL_STAT_START_HALTED 0x4000
-#define SPI_CTRL_STAT_RAM_BOOT 0x2000
-#define SPI_CTRL_STAT_HOST_RESET 0x1000
-#define SPI_CTRL_STAT_HOST_CPU_EN 0x0800
-
-#define SPI_DMA_WRITE_CTRL_ENABLE 0x0001
-#define SPI_DMA_READ_CTRL_ENABLE 0x0001
-#define HOST_ALLOWED (1 << 7)
-
-#define SPI_TIMEOUT 100 /* msec */
-
-#define SPI_MAX_TX_PACKETS 32
-
-#define SPI_MAX_PACKET_SIZE 32767
-
-#define SPI_TARGET_INT_WAKEUP 0x00000001
-#define SPI_TARGET_INT_SLEEP 0x00000002
-#define SPI_TARGET_INT_RDDONE 0x00000004
-
-#define SPI_TARGET_INT_CTS 0x00004000
-#define SPI_TARGET_INT_DR 0x00008000
-
-#define SPI_HOST_INT_READY 0x00000001
-#define SPI_HOST_INT_WR_READY 0x00000002
-#define SPI_HOST_INT_SW_UPDATE 0x00000004
-#define SPI_HOST_INT_UPDATE 0x10000000
-
-/* clear to send */
-#define SPI_HOST_INT_CR 0x00004000
-
-/* data ready */
-#define SPI_HOST_INT_DR 0x00008000
-
-#define SPI_HOST_INTS_DEFAULT \
- (SPI_HOST_INT_READY | SPI_HOST_INT_UPDATE | SPI_HOST_INT_SW_UPDATE)
-
-#define TARGET_BOOT_SLEEP 50
-
-struct p54s_dma_regs {
- __le16 cmd;
- __le16 len;
- __le32 addr;
-} __packed;
-
-struct p54s_tx_info {
- struct list_head tx_list;
-};
-
-struct p54s_priv {
- /* p54_common has to be the first entry */
- struct p54_common common;
- struct ieee80211_hw *hw;
- struct spi_device *spi;
-
- struct work_struct work;
-
- struct mutex mutex;
- struct completion fw_comp;
-
- spinlock_t tx_lock;
-
- /* protected by tx_lock */
- struct list_head tx_pending;
-
- enum fw_state fw_state;
- const struct firmware *firmware;
-};
-
-#endif /* P54SPI_H */
+++ /dev/null
-/*
- * Copyright (C) 2003 Conexant Americas Inc. All Rights Reserved.
- * Copyright (C) 2004, 2005, 2006 Nokia Corporation
- * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2008 Christian Lamparter <chunkeey@web.de>
- *
- * based on:
- * - cx3110x's pda.h from Nokia
- * - cx3110-transfer.log by Johannes Berg
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#ifndef P54SPI_EEPROM_H
-#define P54SPI_EEPROM_H
-
-static unsigned char p54spi_eeprom[] = {
-
-/* struct eeprom_pda_wrap */
-0x47, 0x4d, 0x55, 0xaa, /* magic */
-0x00, 0x00, /* pad */
-0x00, 0x00, /* eeprom_pda_data_wrap length */
-0x00, 0x00, 0x00, 0x00, /* arm opcode */
-
-/* bogus MAC address */
-0x04, 0x00, 0x01, 0x01, /* PDR_MAC_ADDRESS */
- 0x00, 0x02, 0xee, 0xc0, 0xff, 0xee,
-
-/* struct bootrec_exp_if */
-0x06, 0x00, 0x01, 0x10, /* PDR_INTERFACE_LIST */
- 0x00, 0x00, /* role */
- 0x0f, 0x00, /* if_id */
- 0x85, 0x00, /* variant = Longbow RF, 2GHz */
- 0x01, 0x00, /* btm_compat */
- 0x1f, 0x00, /* top_compat */
-
-0x03, 0x00, 0x02, 0x10, /* PDR_HARDWARE_PLATFORM_COMPONENT_ID */
- 0x03, 0x20, 0x00, 0x43,
-
-/* struct pda_country[6] */
-0x0d, 0x00, 0x07, 0x10, /* PDR_COUNTRY_LIST */
- 0x10, 0x00, 0x00, 0x00,
- 0x20, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00,
- 0x31, 0x00, 0x00, 0x00,
- 0x32, 0x00, 0x00, 0x00,
- 0x40, 0x00, 0x00, 0x00,
-
-/* struct pda_country */
-0x03, 0x00, 0x08, 0x10, /* PDR_DEFAULT_COUNTRY */
- 0x30, 0x00, 0x00, 0x00, /* ETSI */
-
-0x03, 0x00, 0x00, 0x11, /* PDR_ANTENNA_GAIN */
- 0x08, 0x08, 0x08, 0x08,
-
-0x0a, 0x00, 0xff, 0xca, /* PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2 */
- 0x01, 0x00, 0x0a, 0x00,
- 0x00, 0x00, 0x0a, 0x00,
- 0x85, 0x09, 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00,
-
-/* struct pda_custom_wrapper */
-0x10, 0x06, 0x5d, 0xb0, /* PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM */
- 0x0d, 0x00, 0xee, 0x00, /* 13 entries, 238 bytes per entry */
- 0x00, 0x00, 0x16, 0x0c, /* no offset, 3094 total len */
- /* 2412 MHz */
- 0x6c, 0x09,
- 0x10, 0x01, 0x9a, 0x84,
- 0xaa, 0x8a, 0xaa, 0x8a, 0xaa, 0x8a, 0xaa, 0x8a,
- 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6,
- 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6,
- 0xf0, 0x00, 0x94, 0x6c,
- 0x99, 0x82, 0x99, 0x82, 0x99, 0x82, 0x99, 0x82,
- 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae,
- 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae,
- 0xd0, 0x00, 0xaa, 0x5a,
- 0x88, 0x7a, 0x88, 0x7a, 0x88, 0x7a, 0x88, 0x7a,
- 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6,
- 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6,
- 0xa0, 0x00, 0xf3, 0x47,
- 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e,
- 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a,
- 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a,
- 0x50, 0x00, 0x59, 0x36,
- 0x43, 0x5a, 0x43, 0x5a, 0x43, 0x5a, 0x43, 0x5a,
- 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85,
- 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85,
- 0x00, 0x00, 0xe4, 0x2d,
- 0x18, 0x46, 0x18, 0x46, 0x18, 0x46, 0x18, 0x46,
- 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71,
- 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
-
- /* 2417 MHz */
- 0x71, 0x09,
- 0x10, 0x01, 0xb9, 0x83,
- 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a,
- 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6,
- 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6,
- 0xf0, 0x00, 0x2e, 0x6c,
- 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, 0x68, 0x82,
- 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad,
- 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad,
- 0xd0, 0x00, 0x8d, 0x5a,
- 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a,
- 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5,
- 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5,
- 0xa0, 0x00, 0x0a, 0x48,
- 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e,
- 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99,
- 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99,
- 0x50, 0x00, 0x7c, 0x36,
- 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59,
- 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85,
- 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85,
- 0x00, 0x00, 0xf5, 0x2d,
- 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45,
- 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71,
- 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
-
- /* 2422 MHz */
- 0x76, 0x09,
- 0x10, 0x01, 0xb9, 0x83,
- 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a,
- 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6,
- 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6,
- 0xf0, 0x00, 0x2e, 0x6c,
- 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, 0x68, 0x82,
- 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad,
- 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad,
- 0xd0, 0x00, 0x8d, 0x5a,
- 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a,
- 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5,
- 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5,
- 0xa0, 0x00, 0x0a, 0x48,
- 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e,
- 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99,
- 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99,
- 0x50, 0x00, 0x7c, 0x36,
- 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59,
- 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85,
- 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85,
- 0x00, 0x00, 0xf5, 0x2d,
- 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45,
- 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71,
- 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
-
- /* 2427 MHz */
- 0x7b, 0x09,
- 0x10, 0x01, 0x48, 0x83,
- 0x67, 0x8a, 0x67, 0x8a, 0x67, 0x8a, 0x67, 0x8a,
- 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5,
- 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5,
- 0xf0, 0x00, 0xfb, 0x6b,
- 0x50, 0x82, 0x50, 0x82, 0x50, 0x82, 0x50, 0x82,
- 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad,
- 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad,
- 0xd0, 0x00, 0x7e, 0x5a,
- 0x38, 0x7a, 0x38, 0x7a, 0x38, 0x7a, 0x38, 0x7a,
- 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5,
- 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5,
- 0xa0, 0x00, 0x15, 0x48,
- 0x14, 0x6e, 0x14, 0x6e, 0x14, 0x6e, 0x14, 0x6e,
- 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99,
- 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99,
- 0x50, 0x00, 0x8e, 0x36,
- 0xd9, 0x59, 0xd9, 0x59, 0xd9, 0x59, 0xd9, 0x59,
- 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85,
- 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85,
- 0x00, 0x00, 0xfe, 0x2d,
- 0x9d, 0x45, 0x9d, 0x45, 0x9d, 0x45, 0x9d, 0x45,
- 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71,
- 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
-
- /* 2432 MHz */
- 0x80, 0x09,
- 0x10, 0x01, 0xd7, 0x82,
- 0x51, 0x8a, 0x51, 0x8a, 0x51, 0x8a, 0x51, 0x8a,
- 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5,
- 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5,
- 0xf0, 0x00, 0xc8, 0x6b,
- 0x37, 0x82, 0x37, 0x82, 0x37, 0x82, 0x37, 0x82,
- 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad,
- 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad,
- 0xd0, 0x00, 0x6f, 0x5a,
- 0x1d, 0x7a, 0x1d, 0x7a, 0x1d, 0x7a, 0x1d, 0x7a,
- 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5,
- 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5,
- 0xa0, 0x00, 0x20, 0x48,
- 0xf6, 0x6d, 0xf6, 0x6d, 0xf6, 0x6d, 0xf6, 0x6d,
- 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, 0x88, 0x99,
- 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, 0x88, 0x99,
- 0x50, 0x00, 0x9f, 0x36,
- 0xb5, 0x59, 0xb5, 0x59, 0xb5, 0x59, 0xb5, 0x59,
- 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, 0x47, 0x85,
- 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, 0x47, 0x85,
- 0x00, 0x00, 0x06, 0x2e,
- 0x74, 0x45, 0x74, 0x45, 0x74, 0x45, 0x74, 0x45,
- 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, 0x06, 0x71,
- 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, 0x06, 0x71,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
-
- /* 2437 MHz */
- 0x85, 0x09,
- 0x10, 0x01, 0x67, 0x82,
- 0x3a, 0x8a, 0x3a, 0x8a, 0x3a, 0x8a, 0x3a, 0x8a,
- 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5,
- 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5,
- 0xf0, 0x00, 0x95, 0x6b,
- 0x1f, 0x82, 0x1f, 0x82, 0x1f, 0x82, 0x1f, 0x82,
- 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad,
- 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad,
- 0xd0, 0x00, 0x61, 0x5a,
- 0x02, 0x7a, 0x02, 0x7a, 0x02, 0x7a, 0x02, 0x7a,
- 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5,
- 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5,
- 0xa0, 0x00, 0x2c, 0x48,
- 0xd8, 0x6d, 0xd8, 0x6d, 0xd8, 0x6d, 0xd8, 0x6d,
- 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99,
- 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99,
- 0x50, 0x00, 0xb1, 0x36,
- 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, 0x92, 0x59,
- 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85,
- 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85,
- 0x00, 0x00, 0x0f, 0x2e,
- 0x4b, 0x45, 0x4b, 0x45, 0x4b, 0x45, 0x4b, 0x45,
- 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70,
- 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
-
- /* 2442 MHz */
- 0x8a, 0x09,
- 0x10, 0x01, 0xf6, 0x81,
- 0x24, 0x8a, 0x24, 0x8a, 0x24, 0x8a, 0x24, 0x8a,
- 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5,
- 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5,
- 0xf0, 0x00, 0x62, 0x6b,
- 0x06, 0x82, 0x06, 0x82, 0x06, 0x82, 0x06, 0x82,
- 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, 0x98, 0xad,
- 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, 0x98, 0xad,
- 0xd0, 0x00, 0x52, 0x5a,
- 0xe7, 0x79, 0xe7, 0x79, 0xe7, 0x79, 0xe7, 0x79,
- 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5,
- 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5,
- 0xa0, 0x00, 0x37, 0x48,
- 0xba, 0x6d, 0xba, 0x6d, 0xba, 0x6d, 0xba, 0x6d,
- 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99,
- 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99,
- 0x50, 0x00, 0xc2, 0x36,
- 0x6e, 0x59, 0x6e, 0x59, 0x6e, 0x59, 0x6e, 0x59,
- 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, 0x00, 0x85,
- 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, 0x00, 0x85,
- 0x00, 0x00, 0x17, 0x2e,
- 0x22, 0x45, 0x22, 0x45, 0x22, 0x45, 0x22, 0x45,
- 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70,
- 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
-
- /* 2447 MHz */
- 0x8f, 0x09,
- 0x10, 0x01, 0x75, 0x83,
- 0x61, 0x8a, 0x61, 0x8a, 0x61, 0x8a, 0x61, 0x8a,
- 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5,
- 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5,
- 0xf0, 0x00, 0x4b, 0x6c,
- 0x3f, 0x82, 0x3f, 0x82, 0x3f, 0x82, 0x3f, 0x82,
- 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad,
- 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad,
- 0xd0, 0x00, 0xda, 0x5a,
- 0x1c, 0x7a, 0x1c, 0x7a, 0x1c, 0x7a, 0x1c, 0x7a,
- 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5,
- 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5,
- 0xa0, 0x00, 0x6d, 0x48,
- 0xe9, 0x6d, 0xe9, 0x6d, 0xe9, 0x6d, 0xe9, 0x6d,
- 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99,
- 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99,
- 0x50, 0x00, 0xc6, 0x36,
- 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, 0x92, 0x59,
- 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85,
- 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85,
- 0x00, 0x00, 0x15, 0x2e,
- 0x3c, 0x45, 0x3c, 0x45, 0x3c, 0x45, 0x3c, 0x45,
- 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, 0xce, 0x70,
- 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, 0xce, 0x70,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
-
- /* 2452 MHz */
- 0x94, 0x09,
- 0x10, 0x01, 0xf4, 0x84,
- 0x9e, 0x8a, 0x9e, 0x8a, 0x9e, 0x8a, 0x9e, 0x8a,
- 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6,
- 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6,
- 0xf0, 0x00, 0x34, 0x6d,
- 0x77, 0x82, 0x77, 0x82, 0x77, 0x82, 0x77, 0x82,
- 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, 0x09, 0xae,
- 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, 0x09, 0xae,
- 0xd0, 0x00, 0x62, 0x5b,
- 0x50, 0x7a, 0x50, 0x7a, 0x50, 0x7a, 0x50, 0x7a,
- 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5,
- 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5,
- 0xa0, 0x00, 0xa2, 0x48,
- 0x17, 0x6e, 0x17, 0x6e, 0x17, 0x6e, 0x17, 0x6e,
- 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99,
- 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99,
- 0x50, 0x00, 0xc9, 0x36,
- 0xb7, 0x59, 0xb7, 0x59, 0xb7, 0x59, 0xb7, 0x59,
- 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, 0x49, 0x85,
- 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, 0x49, 0x85,
- 0x00, 0x00, 0x12, 0x2e,
- 0x57, 0x45, 0x57, 0x45, 0x57, 0x45, 0x57, 0x45,
- 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70,
- 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
-
- /* 2452 MHz */
- 0x99, 0x09,
- 0x10, 0x01, 0x74, 0x86,
- 0xdb, 0x8a, 0xdb, 0x8a, 0xdb, 0x8a, 0xdb, 0x8a,
- 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6,
- 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6,
- 0xf0, 0x00, 0x1e, 0x6e,
- 0xb0, 0x82, 0xb0, 0x82, 0xb0, 0x82, 0xb0, 0x82,
- 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, 0x42, 0xae,
- 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, 0x42, 0xae,
- 0xd0, 0x00, 0xeb, 0x5b,
- 0x85, 0x7a, 0x85, 0x7a, 0x85, 0x7a, 0x85, 0x7a,
- 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6,
- 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6,
- 0xa0, 0x00, 0xd8, 0x48,
- 0x46, 0x6e, 0x46, 0x6e, 0x46, 0x6e, 0x46, 0x6e,
- 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99,
- 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99,
- 0x50, 0x00, 0xcd, 0x36,
- 0xdb, 0x59, 0xdb, 0x59, 0xdb, 0x59, 0xdb, 0x59,
- 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85,
- 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85,
- 0x00, 0x00, 0x10, 0x2e,
- 0x71, 0x45, 0x71, 0x45, 0x71, 0x45, 0x71, 0x45,
- 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, 0x03, 0x71,
- 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, 0x03, 0x71,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
-
- /* 2557 MHz */
- 0x9e, 0x09,
- 0x10, 0x01, 0xf3, 0x87,
- 0x17, 0x8b, 0x17, 0x8b, 0x17, 0x8b, 0x17, 0x8b,
- 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6,
- 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6,
- 0xf0, 0x00, 0x07, 0x6f,
- 0xe9, 0x82, 0xe9, 0x82, 0xe9, 0x82, 0xe9, 0x82,
- 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae,
- 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae,
- 0xd0, 0x00, 0x73, 0x5c,
- 0xba, 0x7a, 0xba, 0x7a, 0xba, 0x7a, 0xba, 0x7a,
- 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6,
- 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6,
- 0xa0, 0x00, 0x0d, 0x49,
- 0x74, 0x6e, 0x74, 0x6e, 0x74, 0x6e, 0x74, 0x6e,
- 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a,
- 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a,
- 0x50, 0x00, 0xd1, 0x36,
- 0xff, 0x59, 0xff, 0x59, 0xff, 0x59, 0xff, 0x59,
- 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, 0x91, 0x85,
- 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, 0x91, 0x85,
- 0x00, 0x00, 0x0e, 0x2e,
- 0x8b, 0x45, 0x8b, 0x45, 0x8b, 0x45, 0x8b, 0x45,
- 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71,
- 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
-
- /* 2562 MHz */
- 0xa3, 0x09,
- 0x10, 0x01, 0x72, 0x89,
- 0x54, 0x8b, 0x54, 0x8b, 0x54, 0x8b, 0x54, 0x8b,
- 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6,
- 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6,
- 0xf0, 0x00, 0xf0, 0x6f,
- 0x21, 0x83, 0x21, 0x83, 0x21, 0x83, 0x21, 0x83,
- 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae,
- 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae,
- 0xd0, 0x00, 0xfb, 0x5c,
- 0xee, 0x7a, 0xee, 0x7a, 0xee, 0x7a, 0xee, 0x7a,
- 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6,
- 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6,
- 0xa0, 0x00, 0x43, 0x49,
- 0xa3, 0x6e, 0xa3, 0x6e, 0xa3, 0x6e, 0xa3, 0x6e,
- 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a,
- 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a,
- 0x50, 0x00, 0xd4, 0x36,
- 0x24, 0x5a, 0x24, 0x5a, 0x24, 0x5a, 0x24, 0x5a,
- 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85,
- 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85,
- 0x00, 0x00, 0x0b, 0x2e,
- 0xa6, 0x45, 0xa6, 0x45, 0xa6, 0x45, 0xa6, 0x45,
- 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, 0x38, 0x71,
- 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, 0x38, 0x71,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
-
- /* 2572 MHz */
- 0xa8, 0x09,
- 0x10, 0x01, 0xf1, 0x8a,
- 0x91, 0x8b, 0x91, 0x8b, 0x91, 0x8b, 0x91, 0x8b,
- 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7,
- 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7,
- 0xf0, 0x00, 0xd9, 0x70,
- 0x5a, 0x83, 0x5a, 0x83, 0x5a, 0x83, 0x5a, 0x83,
- 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, 0xec, 0xae,
- 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, 0xec, 0xae,
- 0xd0, 0x00, 0x83, 0x5d,
- 0x23, 0x7b, 0x23, 0x7b, 0x23, 0x7b, 0x23, 0x7b,
- 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6,
- 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6,
- 0xa0, 0x00, 0x78, 0x49,
- 0xd1, 0x6e, 0xd1, 0x6e, 0xd1, 0x6e, 0xd1, 0x6e,
- 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a,
- 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a,
- 0x50, 0x00, 0xd8, 0x36,
- 0x48, 0x5a, 0x48, 0x5a, 0x48, 0x5a, 0x48, 0x5a,
- 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, 0xda, 0x85,
- 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, 0xda, 0x85,
- 0x00, 0x00, 0x09, 0x2e,
- 0xc0, 0x45, 0xc0, 0x45, 0xc0, 0x45, 0xc0, 0x45,
- 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, 0x52, 0x71,
- 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, 0x52, 0x71,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
-
-/*
- * Not really sure if this is actually the power_limit database,
- * it looks a bit "related" to PDR_PRISM_ZIF_TX_IQ_CALIBRATION
- */
-/* struct pda_custom_wrapper */
-0xae, 0x00, 0xef, 0xbe, /* PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM */
- 0x0d, 0x00, 0x1a, 0x00, /* 13 entries, 26 bytes per entry */
- 0x00, 0x00, 0x52, 0x01, /* no offset, 338 bytes total */
-
- /* 2412 MHz */
- 0x6c, 0x09,
- 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
- 0xe0, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xe0, 0x00,
- 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
-
- /* 2417 MHz */
- 0x71, 0x09,
- 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
- 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
- 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
-
- /* 2422 MHz */
- 0x76, 0x09,
- 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
- 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
- 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
-
- /* 2427 MHz */
- 0x7b, 0x09,
- 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
- 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
- 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
-
- /* 2432 MHz */
- 0x80, 0x09,
- 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
- 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
- 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
-
- /* 2437 MHz */
- 0x85, 0x09,
- 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
- 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
- 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
-
- /* 2442 MHz */
- 0x8a, 0x09,
- 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
- 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
- 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
-
- /* 2447 MHz */
- 0x8f, 0x09,
- 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
- 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
- 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
-
- /* 2452 MHz */
- 0x94, 0x09,
- 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
- 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
- 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
-
- /* 2457 MHz */
- 0x99, 0x09,
- 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
- 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
- 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
-
- /* 2462 MHz */
- 0x9e, 0x09,
- 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
- 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
- 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
-
- /* 2467 MHz */
- 0xa3, 0x09,
- 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
- 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
- 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
-
- /* 2472 MHz */
- 0xa8, 0x09,
- 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
- 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
- 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
-
-/* struct pda_iq_autocal_entry[13] */
-0x42, 0x00, 0x06, 0x19, /* PDR_PRISM_ZIF_TX_IQ_CALIBRATION */
- /* 2412 MHz */
- 0x6c, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00,
- /* 2417 MHz */
- 0x71, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00,
- /* 2422 MHz */
- 0x76, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00,
- /* 2427 MHz */
- 0x7b, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00,
- /* 2432 MHz */
- 0x80, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00,
- /* 2437 MHz */
- 0x85, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00,
- /* 2442 MHz */
- 0x8a, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00,
- /* 2447 MHz */
- 0x8f, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00,
- /* 2452 MHz */
- 0x94, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00,
- /* 2457 MHz */
- 0x99, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01,
- /* 2462 MHz */
- 0x9e, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01,
- /* 2467 MHz */
- 0xa3, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01,
- /* 2472 MHz */
- 0xa8, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01,
-
-0x02, 0x00, 0x00, 0x00, /* PDR_END */
- 0xb6, 0x04,
-};
-
-#endif /* P54SPI_EEPROM_H */
-
+++ /dev/null
-
-/*
- * Linux device driver for USB based Prism54
- *
- * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
- *
- * Based on the islsm (softmac prism54) driver, which is:
- * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/usb.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <linux/firmware.h>
-#include <linux/etherdevice.h>
-#include <linux/delay.h>
-#include <linux/crc32.h>
-#include <linux/module.h>
-#include <net/mac80211.h>
-
-#include "p54.h"
-#include "lmac.h"
-#include "p54usb.h"
-
-MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
-MODULE_DESCRIPTION("Prism54 USB wireless driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("prism54usb");
-MODULE_FIRMWARE("isl3886usb");
-MODULE_FIRMWARE("isl3887usb");
-
-/*
- * Note:
- *
- * Always update our wiki's device list (located at:
- * http://wireless.kernel.org/en/users/Drivers/p54/devices ),
- * whenever you add a new device.
- */
-
-static struct usb_device_id p54u_table[] = {
- /* Version 1 devices (pci chip + net2280) */
- {USB_DEVICE(0x0411, 0x0050)}, /* Buffalo WLI2-USB2-G54 */
- {USB_DEVICE(0x045e, 0x00c2)}, /* Microsoft MN-710 */
- {USB_DEVICE(0x0506, 0x0a11)}, /* 3COM 3CRWE254G72 */
- {USB_DEVICE(0x0675, 0x0530)}, /* DrayTek Vigor 530 */
- {USB_DEVICE(0x06b9, 0x0120)}, /* Thomson SpeedTouch 120g */
- {USB_DEVICE(0x0707, 0xee06)}, /* SMC 2862W-G */
- {USB_DEVICE(0x07aa, 0x001c)}, /* Corega CG-WLUSB2GT */
- {USB_DEVICE(0x083a, 0x4501)}, /* Accton 802.11g WN4501 USB */
- {USB_DEVICE(0x083a, 0x4502)}, /* Siemens Gigaset USB Adapter */
- {USB_DEVICE(0x083a, 0x5501)}, /* Phillips CPWUA054 */
- {USB_DEVICE(0x0846, 0x4200)}, /* Netgear WG121 */
- {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */
- {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */
- {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */
- {USB_DEVICE(0x0bf8, 0x1007)}, /* Fujitsu E-5400 USB */
- {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */
- {USB_DEVICE(0x0db0, 0x6826)}, /* MSI UB54G (MS-6826) */
- {USB_DEVICE(0x107b, 0x55f2)}, /* Gateway WGU-210 (Gemtek) */
- {USB_DEVICE(0x124a, 0x4023)}, /* Shuttle PN15, Airvast WM168g, IOGear GWU513 */
- {USB_DEVICE(0x1435, 0x0210)}, /* Inventel UR054G */
- {USB_DEVICE(0x15a9, 0x0002)}, /* Gemtek WUBI-100GW 802.11g */
- {USB_DEVICE(0x1630, 0x0005)}, /* 2Wire 802.11g USB (v1) / Z-Com */
- {USB_DEVICE(0x182d, 0x096b)}, /* Sitecom WL-107 */
- {USB_DEVICE(0x1915, 0x2234)}, /* Linksys WUSB54G OEM */
- {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */
- {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */
- {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */
- {USB_DEVICE(0x2001, 0x3762)}, /* Conceptronic C54U */
- {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */
- {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */
-
- /* Version 2 devices (3887) */
- {USB_DEVICE(0x0471, 0x1230)}, /* Philips CPWUA054/00 */
- {USB_DEVICE(0x050d, 0x7050)}, /* Belkin F5D7050 ver 1000 */
- {USB_DEVICE(0x0572, 0x2000)}, /* Cohiba Proto board */
- {USB_DEVICE(0x0572, 0x2002)}, /* Cohiba Proto board */
- {USB_DEVICE(0x06a9, 0x000e)}, /* Westell 802.11g USB (A90-211WG-01) */
- {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */
- {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */
- {USB_DEVICE(0x07aa, 0x0020)}, /* Corega WLUSB2GTST USB */
- {USB_DEVICE(0x0803, 0x4310)}, /* Zoom 4410a */
- {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */
- {USB_DEVICE(0x083a, 0x4531)}, /* T-Com Sinus 154 data II */
- {USB_DEVICE(0x083a, 0xc501)}, /* Zoom Wireless-G 4410 */
- {USB_DEVICE(0x083a, 0xf503)}, /* Accton FD7050E ver 1010ec */
- {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */
- {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */
- {USB_DEVICE(0x0915, 0x2002)}, /* Cohiba Proto board */
- {USB_DEVICE(0x0baf, 0x0118)}, /* U.S. Robotics U5 802.11g Adapter*/
- {USB_DEVICE(0x0bf8, 0x1009)}, /* FUJITSU E-5400 USB D1700*/
- /* {USB_DEVICE(0x0cde, 0x0006)}, * Medion MD40900 already listed above,
- * just noting it here for clarity */
- {USB_DEVICE(0x0cde, 0x0008)}, /* Sagem XG703A */
- {USB_DEVICE(0x0cde, 0x0015)}, /* Zcomax XG-705A */
- {USB_DEVICE(0x0d8e, 0x3762)}, /* DLink DWL-G120 Cohiba */
- {USB_DEVICE(0x124a, 0x4025)}, /* IOGear GWU513 (GW3887IK chip) */
- {USB_DEVICE(0x1260, 0xee22)}, /* SMC 2862W-G version 2 */
- {USB_DEVICE(0x13b1, 0x000a)}, /* Linksys WUSB54G ver 2 */
- {USB_DEVICE(0x13B1, 0x000C)}, /* Linksys WUSB54AG */
- {USB_DEVICE(0x1413, 0x5400)}, /* Telsey 802.11g USB2.0 Adapter */
- {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */
- /* {USB_DEVICE(0x15a9, 0x0002)}, * Also SparkLAN WL-682 with 3887 */
- {USB_DEVICE(0x1668, 0x1050)}, /* Actiontec 802UIG-1 */
- {USB_DEVICE(0x1740, 0x1000)}, /* Senao NUB-350 */
- {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */
- {USB_DEVICE(0x2001, 0x3705)}, /* D-Link DWL-G120 rev C1 */
- {USB_DEVICE(0x413c, 0x5513)}, /* Dell WLA3310 USB Wireless Adapter */
- {USB_DEVICE(0x413c, 0x8102)}, /* Spinnaker DUT */
- {USB_DEVICE(0x413c, 0x8104)}, /* Cohiba Proto board */
- {}
-};
-
-MODULE_DEVICE_TABLE(usb, p54u_table);
-
-static const struct {
- u32 intf;
- enum p54u_hw_type type;
- const char *fw;
- char hw[20];
-} p54u_fwlist[__NUM_P54U_HWTYPES] = {
- {
- .type = P54U_NET2280,
- .intf = FW_LM86,
- .fw = "isl3886usb",
- .hw = "ISL3886 + net2280",
- },
- {
- .type = P54U_3887,
- .intf = FW_LM87,
- .fw = "isl3887usb",
- .hw = "ISL3887",
- },
-};
-
-static void p54u_rx_cb(struct urb *urb)
-{
- struct sk_buff *skb = (struct sk_buff *) urb->context;
- struct p54u_rx_info *info = (struct p54u_rx_info *)skb->cb;
- struct ieee80211_hw *dev = info->dev;
- struct p54u_priv *priv = dev->priv;
-
- skb_unlink(skb, &priv->rx_queue);
-
- if (unlikely(urb->status)) {
- dev_kfree_skb_irq(skb);
- return;
- }
-
- skb_put(skb, urb->actual_length);
-
- if (priv->hw_type == P54U_NET2280)
- skb_pull(skb, priv->common.tx_hdr_len);
- if (priv->common.fw_interface == FW_LM87) {
- skb_pull(skb, 4);
- skb_put(skb, 4);
- }
-
- if (p54_rx(dev, skb)) {
- skb = dev_alloc_skb(priv->common.rx_mtu + 32);
- if (unlikely(!skb)) {
- /* TODO check rx queue length and refill *somewhere* */
- return;
- }
-
- info = (struct p54u_rx_info *) skb->cb;
- info->urb = urb;
- info->dev = dev;
- urb->transfer_buffer = skb_tail_pointer(skb);
- urb->context = skb;
- } else {
- if (priv->hw_type == P54U_NET2280)
- skb_push(skb, priv->common.tx_hdr_len);
- if (priv->common.fw_interface == FW_LM87) {
- skb_push(skb, 4);
- skb_put(skb, 4);
- }
- skb_reset_tail_pointer(skb);
- skb_trim(skb, 0);
- urb->transfer_buffer = skb_tail_pointer(skb);
- }
- skb_queue_tail(&priv->rx_queue, skb);
- usb_anchor_urb(urb, &priv->submitted);
- if (usb_submit_urb(urb, GFP_ATOMIC)) {
- skb_unlink(skb, &priv->rx_queue);
- usb_unanchor_urb(urb);
- dev_kfree_skb_irq(skb);
- }
-}
-
-static void p54u_tx_cb(struct urb *urb)
-{
- struct sk_buff *skb = urb->context;
- struct ieee80211_hw *dev =
- usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
-
- p54_free_skb(dev, skb);
-}
-
-static void p54u_tx_dummy_cb(struct urb *urb) { }
-
-static void p54u_free_urbs(struct ieee80211_hw *dev)
-{
- struct p54u_priv *priv = dev->priv;
- usb_kill_anchored_urbs(&priv->submitted);
-}
-
-static void p54u_stop(struct ieee80211_hw *dev)
-{
- /*
- * TODO: figure out how to reliably stop the 3887 and net2280 so
- * the hardware is still usable next time we want to start it.
- * until then, we just stop listening to the hardware..
- */
- p54u_free_urbs(dev);
-}
-
-static int p54u_init_urbs(struct ieee80211_hw *dev)
-{
- struct p54u_priv *priv = dev->priv;
- struct urb *entry = NULL;
- struct sk_buff *skb;
- struct p54u_rx_info *info;
- int ret = 0;
-
- while (skb_queue_len(&priv->rx_queue) < 32) {
- skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL);
- if (!skb) {
- ret = -ENOMEM;
- goto err;
- }
- entry = usb_alloc_urb(0, GFP_KERNEL);
- if (!entry) {
- ret = -ENOMEM;
- goto err;
- }
-
- usb_fill_bulk_urb(entry, priv->udev,
- usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA),
- skb_tail_pointer(skb),
- priv->common.rx_mtu + 32, p54u_rx_cb, skb);
- info = (struct p54u_rx_info *) skb->cb;
- info->urb = entry;
- info->dev = dev;
- skb_queue_tail(&priv->rx_queue, skb);
-
- usb_anchor_urb(entry, &priv->submitted);
- ret = usb_submit_urb(entry, GFP_KERNEL);
- if (ret) {
- skb_unlink(skb, &priv->rx_queue);
- usb_unanchor_urb(entry);
- goto err;
- }
- usb_free_urb(entry);
- entry = NULL;
- }
-
- return 0;
-
- err:
- usb_free_urb(entry);
- kfree_skb(skb);
- p54u_free_urbs(dev);
- return ret;
-}
-
-static int p54u_open(struct ieee80211_hw *dev)
-{
- /*
- * TODO: Because we don't know how to reliably stop the 3887 and
- * the isl3886+net2280, other than brutally cut off all
- * communications. We have to reinitialize the urbs on every start.
- */
- return p54u_init_urbs(dev);
-}
-
-static __le32 p54u_lm87_chksum(const __le32 *data, size_t length)
-{
- u32 chk = 0;
-
- length >>= 2;
- while (length--) {
- chk ^= le32_to_cpu(*data++);
- chk = (chk >> 5) ^ (chk << 3);
- }
-
- return cpu_to_le32(chk);
-}
-
-static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb)
-{
- struct p54u_priv *priv = dev->priv;
- struct urb *data_urb;
- struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr);
-
- data_urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!data_urb) {
- p54_free_skb(dev, skb);
- return;
- }
-
- hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len);
- hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id;
-
- usb_fill_bulk_urb(data_urb, priv->udev,
- usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
- hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ?
- p54u_tx_cb : p54u_tx_dummy_cb, skb);
- data_urb->transfer_flags |= URB_ZERO_PACKET;
-
- usb_anchor_urb(data_urb, &priv->submitted);
- if (usb_submit_urb(data_urb, GFP_ATOMIC)) {
- usb_unanchor_urb(data_urb);
- p54_free_skb(dev, skb);
- }
- usb_free_urb(data_urb);
-}
-
-static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb)
-{
- struct p54u_priv *priv = dev->priv;
- struct urb *int_urb = NULL, *data_urb = NULL;
- struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr);
- struct net2280_reg_write *reg = NULL;
- int err = -ENOMEM;
-
- reg = kmalloc(sizeof(*reg), GFP_ATOMIC);
- if (!reg)
- goto out;
-
- int_urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!int_urb)
- goto out;
-
- data_urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!data_urb)
- goto out;
-
- reg->port = cpu_to_le16(NET2280_DEV_U32);
- reg->addr = cpu_to_le32(P54U_DEV_BASE);
- reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA);
-
- memset(hdr, 0, sizeof(*hdr));
- hdr->len = cpu_to_le16(skb->len);
- hdr->device_addr = ((struct p54_hdr *) skb->data)->req_id;
-
- usb_fill_bulk_urb(int_urb, priv->udev,
- usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg),
- p54u_tx_dummy_cb, dev);
-
- /*
- * URB_FREE_BUFFER triggers a code path in the USB subsystem that will
- * free what is inside the transfer_buffer after the last reference to
- * the int_urb is dropped.
- */
- int_urb->transfer_flags |= URB_FREE_BUFFER | URB_ZERO_PACKET;
- reg = NULL;
-
- usb_fill_bulk_urb(data_urb, priv->udev,
- usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
- hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ?
- p54u_tx_cb : p54u_tx_dummy_cb, skb);
- data_urb->transfer_flags |= URB_ZERO_PACKET;
-
- usb_anchor_urb(int_urb, &priv->submitted);
- err = usb_submit_urb(int_urb, GFP_ATOMIC);
- if (err) {
- usb_unanchor_urb(int_urb);
- goto out;
- }
-
- usb_anchor_urb(data_urb, &priv->submitted);
- err = usb_submit_urb(data_urb, GFP_ATOMIC);
- if (err) {
- usb_unanchor_urb(data_urb);
- goto out;
- }
-out:
- usb_free_urb(int_urb);
- usb_free_urb(data_urb);
-
- if (err) {
- kfree(reg);
- p54_free_skb(dev, skb);
- }
-}
-
-static int p54u_write(struct p54u_priv *priv,
- struct net2280_reg_write *buf,
- enum net2280_op_type type,
- __le32 addr, __le32 val)
-{
- unsigned int ep;
- int alen;
-
- if (type & 0x0800)
- ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV);
- else
- ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_BRG);
-
- buf->port = cpu_to_le16(type);
- buf->addr = addr;
- buf->val = val;
-
- return usb_bulk_msg(priv->udev, ep, buf, sizeof(*buf), &alen, 1000);
-}
-
-static int p54u_read(struct p54u_priv *priv, void *buf,
- enum net2280_op_type type,
- __le32 addr, __le32 *val)
-{
- struct net2280_reg_read *read = buf;
- __le32 *reg = buf;
- unsigned int ep;
- int alen, err;
-
- if (type & 0x0800)
- ep = P54U_PIPE_DEV;
- else
- ep = P54U_PIPE_BRG;
-
- read->port = cpu_to_le16(type);
- read->addr = addr;
-
- err = usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep),
- read, sizeof(*read), &alen, 1000);
- if (err)
- return err;
-
- err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, ep),
- reg, sizeof(*reg), &alen, 1000);
- if (err)
- return err;
-
- *val = *reg;
- return 0;
-}
-
-static int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep,
- void *data, size_t len)
-{
- int alen;
- return usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep),
- data, len, &alen, 2000);
-}
-
-static int p54u_device_reset(struct ieee80211_hw *dev)
-{
- struct p54u_priv *priv = dev->priv;
- int ret, lock = (priv->intf->condition != USB_INTERFACE_BINDING);
-
- if (lock) {
- ret = usb_lock_device_for_reset(priv->udev, priv->intf);
- if (ret < 0) {
- dev_err(&priv->udev->dev, "(p54usb) unable to lock "
- "device for reset (%d)!\n", ret);
- return ret;
- }
- }
-
- ret = usb_reset_device(priv->udev);
- if (lock)
- usb_unlock_device(priv->udev);
-
- if (ret)
- dev_err(&priv->udev->dev, "(p54usb) unable to reset "
- "device (%d)!\n", ret);
-
- return ret;
-}
-
-static const char p54u_romboot_3887[] = "~~~~";
-static int p54u_firmware_reset_3887(struct ieee80211_hw *dev)
-{
- struct p54u_priv *priv = dev->priv;
- u8 *buf;
- int ret;
-
- buf = kmemdup(p54u_romboot_3887, 4, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- ret = p54u_bulk_msg(priv, P54U_PIPE_DATA,
- buf, 4);
- kfree(buf);
- if (ret)
- dev_err(&priv->udev->dev, "(p54usb) unable to jump to "
- "boot ROM (%d)!\n", ret);
-
- return ret;
-}
-
-static const char p54u_firmware_upload_3887[] = "<\r";
-static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
-{
- struct p54u_priv *priv = dev->priv;
- int err, alen;
- u8 carry = 0;
- u8 *buf, *tmp;
- const u8 *data;
- unsigned int left, remains, block_size;
- struct x2_header *hdr;
- unsigned long timeout;
-
- err = p54u_firmware_reset_3887(dev);
- if (err)
- return err;
-
- tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- left = block_size = min_t(size_t, P54U_FW_BLOCK, priv->fw->size);
- strcpy(buf, p54u_firmware_upload_3887);
- left -= strlen(p54u_firmware_upload_3887);
- tmp += strlen(p54u_firmware_upload_3887);
-
- data = priv->fw->data;
- remains = priv->fw->size;
-
- hdr = (struct x2_header *)(buf + strlen(p54u_firmware_upload_3887));
- memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE);
- hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR);
- hdr->fw_length = cpu_to_le32(priv->fw->size);
- hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr,
- sizeof(u32)*2));
- left -= sizeof(*hdr);
- tmp += sizeof(*hdr);
-
- while (remains) {
- while (left--) {
- if (carry) {
- *tmp++ = carry;
- carry = 0;
- remains--;
- continue;
- }
- switch (*data) {
- case '~':
- *tmp++ = '}';
- carry = '^';
- break;
- case '}':
- *tmp++ = '}';
- carry = ']';
- break;
- default:
- *tmp++ = *data;
- remains--;
- break;
- }
- data++;
- }
-
- err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size);
- if (err) {
- dev_err(&priv->udev->dev, "(p54usb) firmware "
- "upload failed!\n");
- goto err_upload_failed;
- }
-
- tmp = buf;
- left = block_size = min((unsigned int)P54U_FW_BLOCK, remains);
- }
-
- *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, priv->fw->data,
- priv->fw->size));
- err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32));
- if (err) {
- dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
- goto err_upload_failed;
- }
- timeout = jiffies + msecs_to_jiffies(1000);
- while (!(err = usb_bulk_msg(priv->udev,
- usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) {
- if (alen > 2 && !memcmp(buf, "OK", 2))
- break;
-
- if (alen > 5 && !memcmp(buf, "ERROR", 5)) {
- err = -EINVAL;
- break;
- }
-
- if (time_after(jiffies, timeout)) {
- dev_err(&priv->udev->dev, "(p54usb) firmware boot "
- "timed out!\n");
- err = -ETIMEDOUT;
- break;
- }
- }
- if (err) {
- dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
- goto err_upload_failed;
- }
-
- buf[0] = 'g';
- buf[1] = '\r';
- err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2);
- if (err) {
- dev_err(&priv->udev->dev, "(p54usb) firmware boot failed!\n");
- goto err_upload_failed;
- }
-
- timeout = jiffies + msecs_to_jiffies(1000);
- while (!(err = usb_bulk_msg(priv->udev,
- usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) {
- if (alen > 0 && buf[0] == 'g')
- break;
-
- if (time_after(jiffies, timeout)) {
- err = -ETIMEDOUT;
- break;
- }
- }
- if (err)
- goto err_upload_failed;
-
-err_upload_failed:
- kfree(buf);
- return err;
-}
-
-static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
-{
- struct p54u_priv *priv = dev->priv;
- const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE;
- int err, alen;
- void *buf;
- __le32 reg;
- unsigned int remains, offset;
- const u8 *data;
-
- buf = kmalloc(512, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
-#define P54U_WRITE(type, addr, data) \
- do {\
- err = p54u_write(priv, buf, type,\
- cpu_to_le32((u32)(unsigned long)addr), data);\
- if (err) \
- goto fail;\
- } while (0)
-
-#define P54U_READ(type, addr) \
- do {\
- err = p54u_read(priv, buf, type,\
- cpu_to_le32((u32)(unsigned long)addr), ®);\
- if (err)\
- goto fail;\
- } while (0)
-
- /* power down net2280 bridge */
- P54U_READ(NET2280_BRG_U32, NET2280_GPIOCTL);
- reg |= cpu_to_le32(P54U_BRG_POWER_DOWN);
- reg &= cpu_to_le32(~P54U_BRG_POWER_UP);
- P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg);
-
- mdelay(100);
-
- /* power up bridge */
- reg |= cpu_to_le32(P54U_BRG_POWER_UP);
- reg &= cpu_to_le32(~P54U_BRG_POWER_DOWN);
- P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg);
-
- mdelay(100);
-
- P54U_WRITE(NET2280_BRG_U32, NET2280_DEVINIT,
- cpu_to_le32(NET2280_CLK_30Mhz |
- NET2280_PCI_ENABLE |
- NET2280_PCI_SOFT_RESET));
-
- mdelay(20);
-
- P54U_WRITE(NET2280_BRG_CFG_U16, PCI_COMMAND,
- cpu_to_le32(PCI_COMMAND_MEMORY |
- PCI_COMMAND_MASTER));
-
- P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_0,
- cpu_to_le32(NET2280_BASE));
-
- P54U_READ(NET2280_BRG_CFG_U16, PCI_STATUS);
- reg |= cpu_to_le32(PCI_STATUS_REC_MASTER_ABORT);
- P54U_WRITE(NET2280_BRG_CFG_U16, PCI_STATUS, reg);
-
- // TODO: we really need this?
- P54U_READ(NET2280_BRG_U32, NET2280_RELNUM);
-
- P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_RSP,
- cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE));
- P54U_WRITE(NET2280_BRG_U32, NET2280_EPC_RSP,
- cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE));
-
- P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_2,
- cpu_to_le32(NET2280_BASE2));
-
- /* finally done setting up the bridge */
-
- P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | PCI_COMMAND,
- cpu_to_le32(PCI_COMMAND_MEMORY |
- PCI_COMMAND_MASTER));
-
- P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | 0x40 /* TRDY timeout */, 0);
- P54U_WRITE(NET2280_DEV_CFG_U32, 0x10000 | PCI_BASE_ADDRESS_0,
- cpu_to_le32(P54U_DEV_BASE));
-
- P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0);
- P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
- cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
-
- /* do romboot */
- P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, 0);
-
- P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat);
- reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
- reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT);
- reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);
- P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
-
- mdelay(20);
-
- reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
- P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
-
- mdelay(20);
-
- reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
- P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
-
- mdelay(100);
-
- P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
- P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
-
- /* finally, we can upload firmware now! */
- remains = priv->fw->size;
- data = priv->fw->data;
- offset = ISL38XX_DEV_FIRMWARE_ADDR;
-
- while (remains) {
- unsigned int block_len = min(remains, (unsigned int)512);
- memcpy(buf, data, block_len);
-
- err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len);
- if (err) {
- dev_err(&priv->udev->dev, "(p54usb) firmware block "
- "upload failed\n");
- goto fail;
- }
-
- P54U_WRITE(NET2280_DEV_U32, &devreg->direct_mem_base,
- cpu_to_le32(0xc0000f00));
-
- P54U_WRITE(NET2280_DEV_U32,
- 0x0020 | (unsigned long)&devreg->direct_mem_win, 0);
- P54U_WRITE(NET2280_DEV_U32,
- 0x0020 | (unsigned long)&devreg->direct_mem_win,
- cpu_to_le32(1));
-
- P54U_WRITE(NET2280_DEV_U32,
- 0x0024 | (unsigned long)&devreg->direct_mem_win,
- cpu_to_le32(block_len));
- P54U_WRITE(NET2280_DEV_U32,
- 0x0028 | (unsigned long)&devreg->direct_mem_win,
- cpu_to_le32(offset));
-
- P54U_WRITE(NET2280_DEV_U32, &devreg->dma_addr,
- cpu_to_le32(NET2280_EPA_FIFO_PCI_ADDR));
- P54U_WRITE(NET2280_DEV_U32, &devreg->dma_len,
- cpu_to_le32(block_len >> 2));
- P54U_WRITE(NET2280_DEV_U32, &devreg->dma_ctrl,
- cpu_to_le32(ISL38XX_DMA_MASTER_CONTROL_TRIGGER));
-
- mdelay(10);
-
- P54U_READ(NET2280_DEV_U32,
- 0x002C | (unsigned long)&devreg->direct_mem_win);
- if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) ||
- !(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) {
- dev_err(&priv->udev->dev, "(p54usb) firmware DMA "
- "transfer failed\n");
- goto fail;
- }
-
- P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_STAT,
- cpu_to_le32(NET2280_FIFO_FLUSH));
-
- remains -= block_len;
- data += block_len;
- offset += block_len;
- }
-
- /* do ramboot */
- P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat);
- reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
- reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);
- reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT);
- P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
-
- mdelay(20);
-
- reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
- P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
-
- reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
- P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
-
- mdelay(100);
-
- P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
- P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
-
- /* start up the firmware */
- P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable,
- cpu_to_le32(ISL38XX_INT_IDENT_INIT));
-
- P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
- cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
-
- P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1,
- cpu_to_le32(NET2280_PCI_INTA_INTERRUPT_ENABLE |
- NET2280_USB_INTERRUPT_ENABLE));
-
- P54U_WRITE(NET2280_DEV_U32, &devreg->dev_int,
- cpu_to_le32(ISL38XX_DEV_INT_RESET));
-
- err = usb_interrupt_msg(priv->udev,
- usb_rcvbulkpipe(priv->udev, P54U_PIPE_INT),
- buf, sizeof(__le32), &alen, 1000);
- if (err || alen != sizeof(__le32))
- goto fail;
-
- P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
- P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
-
- if (!(reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT)))
- err = -EINVAL;
-
- P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0);
- P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
- cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
-
-#undef P54U_WRITE
-#undef P54U_READ
-
-fail:
- kfree(buf);
- return err;
-}
-
-static int p54_find_type(struct p54u_priv *priv)
-{
- int i;
-
- for (i = 0; i < __NUM_P54U_HWTYPES; i++)
- if (p54u_fwlist[i].type == priv->hw_type)
- break;
- if (i == __NUM_P54U_HWTYPES)
- return -EOPNOTSUPP;
-
- return i;
-}
-
-static int p54u_start_ops(struct p54u_priv *priv)
-{
- struct ieee80211_hw *dev = priv->common.hw;
- int ret;
-
- ret = p54_parse_firmware(dev, priv->fw);
- if (ret)
- goto err_out;
-
- ret = p54_find_type(priv);
- if (ret < 0)
- goto err_out;
-
- if (priv->common.fw_interface != p54u_fwlist[ret].intf) {
- dev_err(&priv->udev->dev, "wrong firmware, please get "
- "a firmware for \"%s\" and try again.\n",
- p54u_fwlist[ret].hw);
- ret = -ENODEV;
- goto err_out;
- }
-
- ret = priv->upload_fw(dev);
- if (ret)
- goto err_out;
-
- ret = p54u_open(dev);
- if (ret)
- goto err_out;
-
- ret = p54_read_eeprom(dev);
- if (ret)
- goto err_stop;
-
- p54u_stop(dev);
-
- ret = p54_register_common(dev, &priv->udev->dev);
- if (ret)
- goto err_stop;
-
- return 0;
-
-err_stop:
- p54u_stop(dev);
-
-err_out:
- /*
- * p54u_disconnect will do the rest of the
- * cleanup
- */
- return ret;
-}
-
-static void p54u_load_firmware_cb(const struct firmware *firmware,
- void *context)
-{
- struct p54u_priv *priv = context;
- struct usb_device *udev = priv->udev;
- int err;
-
- complete(&priv->fw_wait_load);
- if (firmware) {
- priv->fw = firmware;
- err = p54u_start_ops(priv);
- } else {
- err = -ENOENT;
- dev_err(&udev->dev, "Firmware not found.\n");
- }
-
- if (err) {
- struct device *parent = priv->udev->dev.parent;
-
- dev_err(&udev->dev, "failed to initialize device (%d)\n", err);
-
- if (parent)
- device_lock(parent);
-
- device_release_driver(&udev->dev);
- /*
- * At this point p54u_disconnect has already freed
- * the "priv" context. Do not use it anymore!
- */
- priv = NULL;
-
- if (parent)
- device_unlock(parent);
- }
-
- usb_put_dev(udev);
-}
-
-static int p54u_load_firmware(struct ieee80211_hw *dev,
- struct usb_interface *intf)
-{
- struct usb_device *udev = interface_to_usbdev(intf);
- struct p54u_priv *priv = dev->priv;
- struct device *device = &udev->dev;
- int err, i;
-
- BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES);
-
- init_completion(&priv->fw_wait_load);
- i = p54_find_type(priv);
- if (i < 0)
- return i;
-
- dev_info(&priv->udev->dev, "Loading firmware file %s\n",
- p54u_fwlist[i].fw);
-
- usb_get_dev(udev);
- err = request_firmware_nowait(THIS_MODULE, 1, p54u_fwlist[i].fw,
- device, GFP_KERNEL, priv,
- p54u_load_firmware_cb);
- if (err) {
- dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
- "(%d)!\n", p54u_fwlist[i].fw, err);
- usb_put_dev(udev);
- }
-
- return err;
-}
-
-static int p54u_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *udev = interface_to_usbdev(intf);
- struct ieee80211_hw *dev;
- struct p54u_priv *priv;
- int err;
- unsigned int i, recognized_pipes;
-
- dev = p54_init_common(sizeof(*priv));
-
- if (!dev) {
- dev_err(&udev->dev, "(p54usb) ieee80211 alloc failed\n");
- return -ENOMEM;
- }
-
- priv = dev->priv;
- priv->hw_type = P54U_INVALID_HW;
-
- SET_IEEE80211_DEV(dev, &intf->dev);
- usb_set_intfdata(intf, dev);
- priv->udev = udev;
- priv->intf = intf;
- skb_queue_head_init(&priv->rx_queue);
- init_usb_anchor(&priv->submitted);
-
- usb_get_dev(udev);
-
- /* really lazy and simple way of figuring out if we're a 3887 */
- /* TODO: should just stick the identification in the device table */
- i = intf->altsetting->desc.bNumEndpoints;
- recognized_pipes = 0;
- while (i--) {
- switch (intf->altsetting->endpoint[i].desc.bEndpointAddress) {
- case P54U_PIPE_DATA:
- case P54U_PIPE_MGMT:
- case P54U_PIPE_BRG:
- case P54U_PIPE_DEV:
- case P54U_PIPE_DATA | USB_DIR_IN:
- case P54U_PIPE_MGMT | USB_DIR_IN:
- case P54U_PIPE_BRG | USB_DIR_IN:
- case P54U_PIPE_DEV | USB_DIR_IN:
- case P54U_PIPE_INT | USB_DIR_IN:
- recognized_pipes++;
- }
- }
- priv->common.open = p54u_open;
- priv->common.stop = p54u_stop;
- if (recognized_pipes < P54U_PIPE_NUMBER) {
-#ifdef CONFIG_PM
- /* ISL3887 needs a full reset on resume */
- udev->reset_resume = 1;
-#endif /* CONFIG_PM */
- err = p54u_device_reset(dev);
-
- priv->hw_type = P54U_3887;
- dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr);
- priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr);
- priv->common.tx = p54u_tx_lm87;
- priv->upload_fw = p54u_upload_firmware_3887;
- } else {
- priv->hw_type = P54U_NET2280;
- dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr);
- priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr);
- priv->common.tx = p54u_tx_net2280;
- priv->upload_fw = p54u_upload_firmware_net2280;
- }
- err = p54u_load_firmware(dev, intf);
- if (err) {
- usb_put_dev(udev);
- p54_free_common(dev);
- }
- return err;
-}
-
-static void p54u_disconnect(struct usb_interface *intf)
-{
- struct ieee80211_hw *dev = usb_get_intfdata(intf);
- struct p54u_priv *priv;
-
- if (!dev)
- return;
-
- priv = dev->priv;
- wait_for_completion(&priv->fw_wait_load);
- p54_unregister_common(dev);
-
- usb_put_dev(interface_to_usbdev(intf));
- release_firmware(priv->fw);
- p54_free_common(dev);
-}
-
-static int p54u_pre_reset(struct usb_interface *intf)
-{
- struct ieee80211_hw *dev = usb_get_intfdata(intf);
-
- if (!dev)
- return -ENODEV;
-
- p54u_stop(dev);
- return 0;
-}
-
-static int p54u_resume(struct usb_interface *intf)
-{
- struct ieee80211_hw *dev = usb_get_intfdata(intf);
- struct p54u_priv *priv;
-
- if (!dev)
- return -ENODEV;
-
- priv = dev->priv;
- if (unlikely(!(priv->upload_fw && priv->fw)))
- return 0;
-
- return priv->upload_fw(dev);
-}
-
-static int p54u_post_reset(struct usb_interface *intf)
-{
- struct ieee80211_hw *dev = usb_get_intfdata(intf);
- struct p54u_priv *priv;
- int err;
-
- err = p54u_resume(intf);
- if (err)
- return err;
-
- /* reinitialize old device state */
- priv = dev->priv;
- if (priv->common.mode != NL80211_IFTYPE_UNSPECIFIED)
- ieee80211_restart_hw(dev);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-
-static int p54u_suspend(struct usb_interface *intf, pm_message_t message)
-{
- return p54u_pre_reset(intf);
-}
-
-#endif /* CONFIG_PM */
-
-static struct usb_driver p54u_driver = {
- .name = "p54usb",
- .id_table = p54u_table,
- .probe = p54u_probe,
- .disconnect = p54u_disconnect,
- .pre_reset = p54u_pre_reset,
- .post_reset = p54u_post_reset,
-#ifdef CONFIG_PM
- .suspend = p54u_suspend,
- .resume = p54u_resume,
- .reset_resume = p54u_resume,
-#endif /* CONFIG_PM */
- .soft_unbind = 1,
- .disable_hub_initiated_lpm = 1,
-};
-
-module_usb_driver(p54u_driver);
+++ /dev/null
-#ifndef P54USB_H
-#define P54USB_H
-
-/*
- * Defines for USB based mac80211 Prism54 driver
- *
- * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
- *
- * Based on the islsm (softmac prism54) driver, which is:
- * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-/* for isl3886 register definitions used on ver 1 devices */
-#include "p54pci.h"
-#include <linux/usb/net2280.h>
-
-/* pci */
-#define NET2280_BASE 0x10000000
-#define NET2280_BASE2 0x20000000
-
-/* gpio */
-#define P54U_BRG_POWER_UP (1 << GPIO0_DATA)
-#define P54U_BRG_POWER_DOWN (1 << GPIO1_DATA)
-
-/* devinit */
-#define NET2280_CLK_4Mhz (15 << LOCAL_CLOCK_FREQUENCY)
-#define NET2280_CLK_30Mhz (2 << LOCAL_CLOCK_FREQUENCY)
-#define NET2280_CLK_60Mhz (1 << LOCAL_CLOCK_FREQUENCY)
-#define NET2280_CLK_STOP (0 << LOCAL_CLOCK_FREQUENCY)
-#define NET2280_PCI_ENABLE (1 << PCI_ENABLE)
-#define NET2280_PCI_SOFT_RESET (1 << PCI_SOFT_RESET)
-
-/* endpoints */
-#define NET2280_CLEAR_NAK_OUT_PACKETS_MODE (1 << CLEAR_NAK_OUT_PACKETS_MODE)
-#define NET2280_FIFO_FLUSH (1 << FIFO_FLUSH)
-
-/* irq */
-#define NET2280_USB_INTERRUPT_ENABLE (1 << USB_INTERRUPT_ENABLE)
-#define NET2280_PCI_INTA_INTERRUPT (1 << PCI_INTA_INTERRUPT)
-#define NET2280_PCI_INTA_INTERRUPT_ENABLE (1 << PCI_INTA_INTERRUPT_ENABLE)
-
-/* registers */
-#define NET2280_DEVINIT 0x00
-#define NET2280_USBIRQENB1 0x24
-#define NET2280_IRQSTAT1 0x2c
-#define NET2280_FIFOCTL 0x38
-#define NET2280_GPIOCTL 0x50
-#define NET2280_RELNUM 0x88
-#define NET2280_EPA_RSP 0x324
-#define NET2280_EPA_STAT 0x32c
-#define NET2280_EPB_STAT 0x34c
-#define NET2280_EPC_RSP 0x364
-#define NET2280_EPC_STAT 0x36c
-#define NET2280_EPD_STAT 0x38c
-
-#define NET2280_EPA_CFG 0x320
-#define NET2280_EPB_CFG 0x340
-#define NET2280_EPC_CFG 0x360
-#define NET2280_EPD_CFG 0x380
-#define NET2280_EPE_CFG 0x3A0
-#define NET2280_EPF_CFG 0x3C0
-#define P54U_DEV_BASE 0x40000000
-
-struct net2280_tx_hdr {
- __le32 device_addr;
- __le16 len;
- __le16 follower; /* ? */
- u8 padding[8];
-} __packed;
-
-struct lm87_tx_hdr {
- __le32 device_addr;
- __le32 chksum;
-} __packed;
-
-/* Some flags for the isl hardware registers controlling DMA inside the
- * chip */
-#define ISL38XX_DMA_STATUS_DONE 0x00000001
-#define ISL38XX_DMA_STATUS_READY 0x00000002
-#define NET2280_EPA_FIFO_PCI_ADDR 0x20000000
-#define ISL38XX_DMA_MASTER_CONTROL_TRIGGER 0x00000004
-
-enum net2280_op_type {
- NET2280_BRG_U32 = 0x001F,
- NET2280_BRG_CFG_U32 = 0x000F,
- NET2280_BRG_CFG_U16 = 0x0003,
- NET2280_DEV_U32 = 0x080F,
- NET2280_DEV_CFG_U32 = 0x088F,
- NET2280_DEV_CFG_U16 = 0x0883
-};
-
-struct net2280_reg_write {
- __le16 port;
- __le32 addr;
- __le32 val;
-} __packed;
-
-struct net2280_reg_read {
- __le16 port;
- __le32 addr;
-} __packed;
-
-#define P54U_FW_BLOCK 2048
-
-#define X2_SIGNATURE "x2 "
-#define X2_SIGNATURE_SIZE 4
-
-struct x2_header {
- u8 signature[X2_SIGNATURE_SIZE];
- __le32 fw_load_addr;
- __le32 fw_length;
- __le32 crc;
-} __packed;
-
-/* pipes 3 and 4 are not used by the driver */
-#define P54U_PIPE_NUMBER 9
-
-enum p54u_pipe_addr {
- P54U_PIPE_DATA = 0x01,
- P54U_PIPE_MGMT = 0x02,
- P54U_PIPE_3 = 0x03,
- P54U_PIPE_4 = 0x04,
- P54U_PIPE_BRG = 0x0d,
- P54U_PIPE_DEV = 0x0e,
- P54U_PIPE_INT = 0x0f
-};
-
-struct p54u_rx_info {
- struct urb *urb;
- struct ieee80211_hw *dev;
-};
-
-enum p54u_hw_type {
- P54U_INVALID_HW,
- P54U_NET2280,
- P54U_3887,
-
- /* keep last */
- __NUM_P54U_HWTYPES,
-};
-
-struct p54u_priv {
- struct p54_common common;
- struct usb_device *udev;
- struct usb_interface *intf;
- int (*upload_fw)(struct ieee80211_hw *dev);
-
- enum p54u_hw_type hw_type;
- spinlock_t lock;
- struct sk_buff_head rx_queue;
- struct usb_anchor submitted;
- const struct firmware *fw;
-
- /* asynchronous firmware callback */
- struct completion fw_wait_load;
-};
-
-#endif /* P54USB_H */
+++ /dev/null
-/*
- * Common code for mac80211 Prism54 drivers
- *
- * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
- * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
- * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
- *
- * Based on:
- * - the islsm (softmac prism54) driver, which is:
- * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
- * - stlc45xx driver
- * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/export.h>
-#include <linux/firmware.h>
-#include <linux/etherdevice.h>
-#include <asm/div64.h>
-
-#include <net/mac80211.h>
-
-#include "p54.h"
-#include "lmac.h"
-
-#ifdef P54_MM_DEBUG
-static void p54_dump_tx_queue(struct p54_common *priv)
-{
- unsigned long flags;
- struct ieee80211_tx_info *info;
- struct p54_tx_info *range;
- struct sk_buff *skb;
- struct p54_hdr *hdr;
- unsigned int i = 0;
- u32 prev_addr;
- u32 largest_hole = 0, free;
-
- spin_lock_irqsave(&priv->tx_queue.lock, flags);
- wiphy_debug(priv->hw->wiphy, "/ --- tx queue dump (%d entries) ---\n",
- skb_queue_len(&priv->tx_queue));
-
- prev_addr = priv->rx_start;
- skb_queue_walk(&priv->tx_queue, skb) {
- info = IEEE80211_SKB_CB(skb);
- range = (void *) info->rate_driver_data;
- hdr = (void *) skb->data;
-
- free = range->start_addr - prev_addr;
- wiphy_debug(priv->hw->wiphy,
- "| [%02d] => [skb:%p skb_len:0x%04x "
- "hdr:{flags:%02x len:%04x req_id:%04x type:%02x} "
- "mem:{start:%04x end:%04x, free:%d}]\n",
- i++, skb, skb->len,
- le16_to_cpu(hdr->flags), le16_to_cpu(hdr->len),
- le32_to_cpu(hdr->req_id), le16_to_cpu(hdr->type),
- range->start_addr, range->end_addr, free);
-
- prev_addr = range->end_addr;
- largest_hole = max(largest_hole, free);
- }
- free = priv->rx_end - prev_addr;
- largest_hole = max(largest_hole, free);
- wiphy_debug(priv->hw->wiphy,
- "\\ --- [free: %d], largest free block: %d ---\n",
- free, largest_hole);
- spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
-}
-#endif /* P54_MM_DEBUG */
-
-/*
- * So, the firmware is somewhat stupid and doesn't know what places in its
- * memory incoming data should go to. By poking around in the firmware, we
- * can find some unused memory to upload our packets to. However, data that we
- * want the card to TX needs to stay intact until the card has told us that
- * it is done with it. This function finds empty places we can upload to and
- * marks allocated areas as reserved if necessary. p54_find_and_unlink_skb or
- * p54_free_skb frees allocated areas.
- */
-static int p54_assign_address(struct p54_common *priv, struct sk_buff *skb)
-{
- struct sk_buff *entry, *target_skb = NULL;
- struct ieee80211_tx_info *info;
- struct p54_tx_info *range;
- struct p54_hdr *data = (void *) skb->data;
- unsigned long flags;
- u32 last_addr = priv->rx_start;
- u32 target_addr = priv->rx_start;
- u16 len = priv->headroom + skb->len + priv->tailroom + 3;
-
- info = IEEE80211_SKB_CB(skb);
- range = (void *) info->rate_driver_data;
- len = (range->extra_len + len) & ~0x3;
-
- spin_lock_irqsave(&priv->tx_queue.lock, flags);
- if (unlikely(skb_queue_len(&priv->tx_queue) == 32)) {
- /*
- * The tx_queue is now really full.
- *
- * TODO: check if the device has crashed and reset it.
- */
- spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
- return -EBUSY;
- }
-
- skb_queue_walk(&priv->tx_queue, entry) {
- u32 hole_size;
- info = IEEE80211_SKB_CB(entry);
- range = (void *) info->rate_driver_data;
- hole_size = range->start_addr - last_addr;
-
- if (!target_skb && hole_size >= len) {
- target_skb = entry->prev;
- hole_size -= len;
- target_addr = last_addr;
- break;
- }
- last_addr = range->end_addr;
- }
- if (unlikely(!target_skb)) {
- if (priv->rx_end - last_addr >= len) {
- target_skb = priv->tx_queue.prev;
- if (!skb_queue_empty(&priv->tx_queue)) {
- info = IEEE80211_SKB_CB(target_skb);
- range = (void *)info->rate_driver_data;
- target_addr = range->end_addr;
- }
- } else {
- spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
- return -ENOSPC;
- }
- }
-
- info = IEEE80211_SKB_CB(skb);
- range = (void *) info->rate_driver_data;
- range->start_addr = target_addr;
- range->end_addr = target_addr + len;
- data->req_id = cpu_to_le32(target_addr + priv->headroom);
- if (IS_DATA_FRAME(skb) &&
- unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON))
- priv->beacon_req_id = data->req_id;
-
- __skb_queue_after(&priv->tx_queue, target_skb, skb);
- spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
- return 0;
-}
-
-static void p54_tx_pending(struct p54_common *priv)
-{
- struct sk_buff *skb;
- int ret;
-
- skb = skb_dequeue(&priv->tx_pending);
- if (unlikely(!skb))
- return ;
-
- ret = p54_assign_address(priv, skb);
- if (unlikely(ret))
- skb_queue_head(&priv->tx_pending, skb);
- else
- priv->tx(priv->hw, skb);
-}
-
-static void p54_wake_queues(struct p54_common *priv)
-{
- unsigned long flags;
- unsigned int i;
-
- if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
- return ;
-
- p54_tx_pending(priv);
-
- spin_lock_irqsave(&priv->tx_stats_lock, flags);
- for (i = 0; i < priv->hw->queues; i++) {
- if (priv->tx_stats[i + P54_QUEUE_DATA].len <
- priv->tx_stats[i + P54_QUEUE_DATA].limit)
- ieee80211_wake_queue(priv->hw, i);
- }
- spin_unlock_irqrestore(&priv->tx_stats_lock, flags);
-}
-
-static int p54_tx_qos_accounting_alloc(struct p54_common *priv,
- struct sk_buff *skb,
- const u16 p54_queue)
-{
- struct p54_tx_queue_stats *queue;
- unsigned long flags;
-
- if (WARN_ON(p54_queue >= P54_QUEUE_NUM))
- return -EINVAL;
-
- queue = &priv->tx_stats[p54_queue];
-
- spin_lock_irqsave(&priv->tx_stats_lock, flags);
- if (unlikely(queue->len >= queue->limit && IS_QOS_QUEUE(p54_queue))) {
- spin_unlock_irqrestore(&priv->tx_stats_lock, flags);
- return -ENOSPC;
- }
-
- queue->len++;
- queue->count++;
-
- if (unlikely(queue->len == queue->limit && IS_QOS_QUEUE(p54_queue))) {
- u16 ac_queue = p54_queue - P54_QUEUE_DATA;
- ieee80211_stop_queue(priv->hw, ac_queue);
- }
-
- spin_unlock_irqrestore(&priv->tx_stats_lock, flags);
- return 0;
-}
-
-static void p54_tx_qos_accounting_free(struct p54_common *priv,
- struct sk_buff *skb)
-{
- if (IS_DATA_FRAME(skb)) {
- unsigned long flags;
-
- spin_lock_irqsave(&priv->tx_stats_lock, flags);
- priv->tx_stats[GET_HW_QUEUE(skb)].len--;
- spin_unlock_irqrestore(&priv->tx_stats_lock, flags);
-
- if (unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON)) {
- if (priv->beacon_req_id == GET_REQ_ID(skb)) {
- /* this is the active beacon set anymore */
- priv->beacon_req_id = 0;
- }
- complete(&priv->beacon_comp);
- }
- }
- p54_wake_queues(priv);
-}
-
-void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb)
-{
- struct p54_common *priv = dev->priv;
- if (unlikely(!skb))
- return ;
-
- skb_unlink(skb, &priv->tx_queue);
- p54_tx_qos_accounting_free(priv, skb);
- ieee80211_free_txskb(dev, skb);
-}
-EXPORT_SYMBOL_GPL(p54_free_skb);
-
-static struct sk_buff *p54_find_and_unlink_skb(struct p54_common *priv,
- const __le32 req_id)
-{
- struct sk_buff *entry;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->tx_queue.lock, flags);
- skb_queue_walk(&priv->tx_queue, entry) {
- struct p54_hdr *hdr = (struct p54_hdr *) entry->data;
-
- if (hdr->req_id == req_id) {
- __skb_unlink(entry, &priv->tx_queue);
- spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
- p54_tx_qos_accounting_free(priv, entry);
- return entry;
- }
- }
- spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
- return NULL;
-}
-
-void p54_tx(struct p54_common *priv, struct sk_buff *skb)
-{
- skb_queue_tail(&priv->tx_pending, skb);
- p54_tx_pending(priv);
-}
-
-static int p54_rssi_to_dbm(struct p54_common *priv, int rssi)
-{
- if (priv->rxhw != 5) {
- return ((rssi * priv->cur_rssi->mul) / 64 +
- priv->cur_rssi->add) / 4;
- } else {
- /*
- * TODO: find the correct formula
- */
- return rssi / 2 - 110;
- }
-}
-
-/*
- * Even if the firmware is capable of dealing with incoming traffic,
- * while dozing, we have to prepared in case mac80211 uses PS-POLL
- * to retrieve outstanding frames from our AP.
- * (see comment in net/mac80211/mlme.c @ line 1993)
- */
-static void p54_pspoll_workaround(struct p54_common *priv, struct sk_buff *skb)
-{
- struct ieee80211_hdr *hdr = (void *) skb->data;
- struct ieee80211_tim_ie *tim_ie;
- u8 *tim;
- u8 tim_len;
- bool new_psm;
-
- /* only beacons have a TIM IE */
- if (!ieee80211_is_beacon(hdr->frame_control))
- return;
-
- if (!priv->aid)
- return;
-
- /* only consider beacons from the associated BSSID */
- if (!ether_addr_equal_64bits(hdr->addr3, priv->bssid))
- return;
-
- tim = p54_find_ie(skb, WLAN_EID_TIM);
- if (!tim)
- return;
-
- tim_len = tim[1];
- tim_ie = (struct ieee80211_tim_ie *) &tim[2];
-
- new_psm = ieee80211_check_tim(tim_ie, tim_len, priv->aid);
- if (new_psm != priv->powersave_override) {
- priv->powersave_override = new_psm;
- p54_set_ps(priv);
- }
-}
-
-static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb)
-{
- struct p54_rx_data *hdr = (struct p54_rx_data *) skb->data;
- struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
- u16 freq = le16_to_cpu(hdr->freq);
- size_t header_len = sizeof(*hdr);
- u32 tsf32;
- u8 rate = hdr->rate & 0xf;
-
- /*
- * If the device is in a unspecified state we have to
- * ignore all data frames. Else we could end up with a
- * nasty crash.
- */
- if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
- return 0;
-
- if (!(hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_IN_FCS_GOOD)))
- return 0;
-
- if (hdr->decrypt_status == P54_DECRYPT_OK)
- rx_status->flag |= RX_FLAG_DECRYPTED;
- if ((hdr->decrypt_status == P54_DECRYPT_FAIL_MICHAEL) ||
- (hdr->decrypt_status == P54_DECRYPT_FAIL_TKIP))
- rx_status->flag |= RX_FLAG_MMIC_ERROR;
-
- rx_status->signal = p54_rssi_to_dbm(priv, hdr->rssi);
- if (hdr->rate & 0x10)
- rx_status->flag |= RX_FLAG_SHORTPRE;
- if (priv->hw->conf.chandef.chan->band == IEEE80211_BAND_5GHZ)
- rx_status->rate_idx = (rate < 4) ? 0 : rate - 4;
- else
- rx_status->rate_idx = rate;
-
- rx_status->freq = freq;
- rx_status->band = priv->hw->conf.chandef.chan->band;
- rx_status->antenna = hdr->antenna;
-
- tsf32 = le32_to_cpu(hdr->tsf32);
- if (tsf32 < priv->tsf_low32)
- priv->tsf_high32++;
- rx_status->mactime = ((u64)priv->tsf_high32) << 32 | tsf32;
- priv->tsf_low32 = tsf32;
-
- /* LMAC API Page 10/29 - s_lm_data_in - clock
- * "usec accurate timestamp of hardware clock
- * at end of frame (before OFDM SIFS EOF padding"
- */
- rx_status->flag |= RX_FLAG_MACTIME_END;
-
- if (hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN))
- header_len += hdr->align[0];
-
- skb_pull(skb, header_len);
- skb_trim(skb, le16_to_cpu(hdr->len));
- if (unlikely(priv->hw->conf.flags & IEEE80211_CONF_PS))
- p54_pspoll_workaround(priv, skb);
-
- ieee80211_rx_irqsafe(priv->hw, skb);
-
- ieee80211_queue_delayed_work(priv->hw, &priv->work,
- msecs_to_jiffies(P54_STATISTICS_UPDATE));
-
- return -1;
-}
-
-static void p54_rx_frame_sent(struct p54_common *priv, struct sk_buff *skb)
-{
- struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
- struct p54_frame_sent *payload = (struct p54_frame_sent *) hdr->data;
- struct ieee80211_tx_info *info;
- struct p54_hdr *entry_hdr;
- struct p54_tx_data *entry_data;
- struct sk_buff *entry;
- unsigned int pad = 0, frame_len;
- int count, idx;
-
- entry = p54_find_and_unlink_skb(priv, hdr->req_id);
- if (unlikely(!entry))
- return ;
-
- frame_len = entry->len;
- info = IEEE80211_SKB_CB(entry);
- entry_hdr = (struct p54_hdr *) entry->data;
- entry_data = (struct p54_tx_data *) entry_hdr->data;
- priv->stats.dot11ACKFailureCount += payload->tries - 1;
-
- /*
- * Frames in P54_QUEUE_FWSCAN and P54_QUEUE_BEACON are
- * generated by the driver. Therefore tx_status is bogus
- * and we don't want to confuse the mac80211 stack.
- */
- if (unlikely(entry_data->hw_queue < P54_QUEUE_FWSCAN)) {
- dev_kfree_skb_any(entry);
- return ;
- }
-
- /*
- * Clear manually, ieee80211_tx_info_clear_status would
- * clear the counts too and we need them.
- */
- memset(&info->status.ack_signal, 0,
- sizeof(struct ieee80211_tx_info) -
- offsetof(struct ieee80211_tx_info, status.ack_signal));
- BUILD_BUG_ON(offsetof(struct ieee80211_tx_info,
- status.ack_signal) != 20);
-
- if (entry_hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN))
- pad = entry_data->align[0];
-
- /* walk through the rates array and adjust the counts */
- count = payload->tries;
- for (idx = 0; idx < 4; idx++) {
- if (count >= info->status.rates[idx].count) {
- count -= info->status.rates[idx].count;
- } else if (count > 0) {
- info->status.rates[idx].count = count;
- count = 0;
- } else {
- info->status.rates[idx].idx = -1;
- info->status.rates[idx].count = 0;
- }
- }
-
- if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
- !(payload->status & P54_TX_FAILED))
- info->flags |= IEEE80211_TX_STAT_ACK;
- if (payload->status & P54_TX_PSM_CANCELLED)
- info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
- info->status.ack_signal = p54_rssi_to_dbm(priv,
- (int)payload->ack_rssi);
-
- /* Undo all changes to the frame. */
- switch (entry_data->key_type) {
- case P54_CRYPTO_TKIPMICHAEL: {
- u8 *iv = (u8 *)(entry_data->align + pad +
- entry_data->crypt_offset);
-
- /* Restore the original TKIP IV. */
- iv[2] = iv[0];
- iv[0] = iv[1];
- iv[1] = (iv[0] | 0x20) & 0x7f; /* WEPSeed - 8.3.2.2 */
-
- frame_len -= 12; /* remove TKIP_MMIC + TKIP_ICV */
- break;
- }
- case P54_CRYPTO_AESCCMP:
- frame_len -= 8; /* remove CCMP_MIC */
- break;
- case P54_CRYPTO_WEP:
- frame_len -= 4; /* remove WEP_ICV */
- break;
- }
-
- skb_trim(entry, frame_len);
- skb_pull(entry, sizeof(*hdr) + pad + sizeof(*entry_data));
- ieee80211_tx_status_irqsafe(priv->hw, entry);
-}
-
-static void p54_rx_eeprom_readback(struct p54_common *priv,
- struct sk_buff *skb)
-{
- struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
- struct p54_eeprom_lm86 *eeprom = (struct p54_eeprom_lm86 *) hdr->data;
- struct sk_buff *tmp;
-
- if (!priv->eeprom)
- return ;
-
- if (priv->fw_var >= 0x509) {
- memcpy(priv->eeprom, eeprom->v2.data,
- le16_to_cpu(eeprom->v2.len));
- } else {
- memcpy(priv->eeprom, eeprom->v1.data,
- le16_to_cpu(eeprom->v1.len));
- }
-
- priv->eeprom = NULL;
- tmp = p54_find_and_unlink_skb(priv, hdr->req_id);
- dev_kfree_skb_any(tmp);
- complete(&priv->eeprom_comp);
-}
-
-static void p54_rx_stats(struct p54_common *priv, struct sk_buff *skb)
-{
- struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
- struct p54_statistics *stats = (struct p54_statistics *) hdr->data;
- struct sk_buff *tmp;
- struct ieee80211_channel *chan;
- unsigned int i, rssi, tx, cca, dtime, dtotal, dcca, dtx, drssi, unit;
- u32 tsf32;
-
- if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
- return ;
-
- tsf32 = le32_to_cpu(stats->tsf32);
- if (tsf32 < priv->tsf_low32)
- priv->tsf_high32++;
- priv->tsf_low32 = tsf32;
-
- priv->stats.dot11RTSFailureCount = le32_to_cpu(stats->rts_fail);
- priv->stats.dot11RTSSuccessCount = le32_to_cpu(stats->rts_success);
- priv->stats.dot11FCSErrorCount = le32_to_cpu(stats->rx_bad_fcs);
-
- priv->noise = p54_rssi_to_dbm(priv, le32_to_cpu(stats->noise));
-
- /*
- * STSW450X LMAC API page 26 - 3.8 Statistics
- * "The exact measurement period can be derived from the
- * timestamp member".
- */
- dtime = tsf32 - priv->survey_raw.timestamp;
-
- /*
- * STSW450X LMAC API page 26 - 3.8.1 Noise histogram
- * The LMAC samples RSSI, CCA and transmit state at regular
- * periods (typically 8 times per 1k [as in 1024] usec).
- */
- cca = le32_to_cpu(stats->sample_cca);
- tx = le32_to_cpu(stats->sample_tx);
- rssi = 0;
- for (i = 0; i < ARRAY_SIZE(stats->sample_noise); i++)
- rssi += le32_to_cpu(stats->sample_noise[i]);
-
- dcca = cca - priv->survey_raw.cached_cca;
- drssi = rssi - priv->survey_raw.cached_rssi;
- dtx = tx - priv->survey_raw.cached_tx;
- dtotal = dcca + drssi + dtx;
-
- /*
- * update statistics when more than a second is over since the
- * last call, or when a update is badly needed.
- */
- if (dtotal && (priv->update_stats || dtime >= USEC_PER_SEC) &&
- dtime >= dtotal) {
- priv->survey_raw.timestamp = tsf32;
- priv->update_stats = false;
- unit = dtime / dtotal;
-
- if (dcca) {
- priv->survey_raw.cca += dcca * unit;
- priv->survey_raw.cached_cca = cca;
- }
- if (dtx) {
- priv->survey_raw.tx += dtx * unit;
- priv->survey_raw.cached_tx = tx;
- }
- if (drssi) {
- priv->survey_raw.rssi += drssi * unit;
- priv->survey_raw.cached_rssi = rssi;
- }
-
- /* 1024 usec / 8 times = 128 usec / time */
- if (!(priv->phy_ps || priv->phy_idle))
- priv->survey_raw.active += dtotal * unit;
- else
- priv->survey_raw.active += (dcca + dtx) * unit;
- }
-
- chan = priv->curchan;
- if (chan) {
- struct survey_info *survey = &priv->survey[chan->hw_value];
- survey->noise = clamp(priv->noise, -128, 127);
- survey->time = priv->survey_raw.active;
- survey->time_tx = priv->survey_raw.tx;
- survey->time_busy = priv->survey_raw.tx +
- priv->survey_raw.cca;
- do_div(survey->time, 1024);
- do_div(survey->time_tx, 1024);
- do_div(survey->time_busy, 1024);
- }
-
- tmp = p54_find_and_unlink_skb(priv, hdr->req_id);
- dev_kfree_skb_any(tmp);
- complete(&priv->stat_comp);
-}
-
-static void p54_rx_trap(struct p54_common *priv, struct sk_buff *skb)
-{
- struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
- struct p54_trap *trap = (struct p54_trap *) hdr->data;
- u16 event = le16_to_cpu(trap->event);
- u16 freq = le16_to_cpu(trap->frequency);
-
- switch (event) {
- case P54_TRAP_BEACON_TX:
- break;
- case P54_TRAP_RADAR:
- wiphy_info(priv->hw->wiphy, "radar (freq:%d MHz)\n", freq);
- break;
- case P54_TRAP_NO_BEACON:
- if (priv->vif)
- ieee80211_beacon_loss(priv->vif);
- break;
- case P54_TRAP_SCAN:
- break;
- case P54_TRAP_TBTT:
- break;
- case P54_TRAP_TIMER:
- break;
- case P54_TRAP_FAA_RADIO_OFF:
- wiphy_rfkill_set_hw_state(priv->hw->wiphy, true);
- break;
- case P54_TRAP_FAA_RADIO_ON:
- wiphy_rfkill_set_hw_state(priv->hw->wiphy, false);
- break;
- default:
- wiphy_info(priv->hw->wiphy, "received event:%x freq:%d\n",
- event, freq);
- break;
- }
-}
-
-static int p54_rx_control(struct p54_common *priv, struct sk_buff *skb)
-{
- struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
-
- switch (le16_to_cpu(hdr->type)) {
- case P54_CONTROL_TYPE_TXDONE:
- p54_rx_frame_sent(priv, skb);
- break;
- case P54_CONTROL_TYPE_TRAP:
- p54_rx_trap(priv, skb);
- break;
- case P54_CONTROL_TYPE_BBP:
- break;
- case P54_CONTROL_TYPE_STAT_READBACK:
- p54_rx_stats(priv, skb);
- break;
- case P54_CONTROL_TYPE_EEPROM_READBACK:
- p54_rx_eeprom_readback(priv, skb);
- break;
- default:
- wiphy_debug(priv->hw->wiphy,
- "not handling 0x%02x type control frame\n",
- le16_to_cpu(hdr->type));
- break;
- }
- return 0;
-}
-
-/* returns zero if skb can be reused */
-int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb)
-{
- struct p54_common *priv = dev->priv;
- u16 type = le16_to_cpu(*((__le16 *)skb->data));
-
- if (type & P54_HDR_FLAG_CONTROL)
- return p54_rx_control(priv, skb);
- else
- return p54_rx_data(priv, skb);
-}
-EXPORT_SYMBOL_GPL(p54_rx);
-
-static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb,
- struct ieee80211_tx_info *info,
- struct ieee80211_sta *sta,
- u8 *queue, u32 *extra_len, u16 *flags, u16 *aid,
- bool *burst_possible)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-
- if (ieee80211_is_data_qos(hdr->frame_control))
- *burst_possible = true;
- else
- *burst_possible = false;
-
- if (!(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ))
- *flags |= P54_HDR_FLAG_DATA_OUT_SEQNR;
-
- if (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)
- *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL;
-
- if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
- *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL;
-
- *queue = skb_get_queue_mapping(skb) + P54_QUEUE_DATA;
-
- switch (priv->mode) {
- case NL80211_IFTYPE_MONITOR:
- /*
- * We have to set P54_HDR_FLAG_DATA_OUT_PROMISC for
- * every frame in promiscuous/monitor mode.
- * see STSW45x0C LMAC API - page 12.
- */
- *aid = 0;
- *flags |= P54_HDR_FLAG_DATA_OUT_PROMISC;
- break;
- case NL80211_IFTYPE_STATION:
- *aid = 1;
- break;
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
- if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
- *aid = 0;
- *queue = P54_QUEUE_CAB;
- return;
- }
-
- if (unlikely(ieee80211_is_mgmt(hdr->frame_control))) {
- if (ieee80211_is_probe_resp(hdr->frame_control)) {
- *aid = 0;
- *flags |= P54_HDR_FLAG_DATA_OUT_TIMESTAMP |
- P54_HDR_FLAG_DATA_OUT_NOCANCEL;
- return;
- } else if (ieee80211_is_beacon(hdr->frame_control)) {
- *aid = 0;
-
- if (info->flags & IEEE80211_TX_CTL_INJECTED) {
- /*
- * Injecting beacons on top of a AP is
- * not a good idea... nevertheless,
- * it should be doable.
- */
-
- return;
- }
-
- *flags |= P54_HDR_FLAG_DATA_OUT_TIMESTAMP;
- *queue = P54_QUEUE_BEACON;
- *extra_len = IEEE80211_MAX_TIM_LEN;
- return;
- }
- }
-
- if (sta)
- *aid = sta->aid;
- break;
- }
-}
-
-static u8 p54_convert_algo(u32 cipher)
-{
- switch (cipher) {
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- return P54_CRYPTO_WEP;
- case WLAN_CIPHER_SUITE_TKIP:
- return P54_CRYPTO_TKIPMICHAEL;
- case WLAN_CIPHER_SUITE_CCMP:
- return P54_CRYPTO_AESCCMP;
- default:
- return 0;
- }
-}
-
-void p54_tx_80211(struct ieee80211_hw *dev,
- struct ieee80211_tx_control *control,
- struct sk_buff *skb)
-{
- struct p54_common *priv = dev->priv;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct p54_tx_info *p54info;
- struct p54_hdr *hdr;
- struct p54_tx_data *txhdr;
- unsigned int padding, len, extra_len = 0;
- int i, j, ridx;
- u16 hdr_flags = 0, aid = 0;
- u8 rate, queue = 0, crypt_offset = 0;
- u8 cts_rate = 0x20;
- u8 rc_flags;
- u8 calculated_tries[4];
- u8 nrates = 0, nremaining = 8;
- bool burst_allowed = false;
-
- p54_tx_80211_header(priv, skb, info, control->sta, &queue, &extra_len,
- &hdr_flags, &aid, &burst_allowed);
-
- if (p54_tx_qos_accounting_alloc(priv, skb, queue)) {
- ieee80211_free_txskb(dev, skb);
- return;
- }
-
- padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3;
- len = skb->len;
-
- if (info->control.hw_key) {
- crypt_offset = ieee80211_get_hdrlen_from_skb(skb);
- if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
- u8 *iv = (u8 *)(skb->data + crypt_offset);
- /*
- * The firmware excepts that the IV has to have
- * this special format
- */
- iv[1] = iv[0];
- iv[0] = iv[2];
- iv[2] = 0;
- }
- }
-
- txhdr = (struct p54_tx_data *) skb_push(skb, sizeof(*txhdr) + padding);
- hdr = (struct p54_hdr *) skb_push(skb, sizeof(*hdr));
-
- if (padding)
- hdr_flags |= P54_HDR_FLAG_DATA_ALIGN;
- hdr->type = cpu_to_le16(aid);
- hdr->rts_tries = info->control.rates[0].count;
-
- /*
- * we register the rates in perfect order, and
- * RTS/CTS won't happen on 5 GHz
- */
- cts_rate = info->control.rts_cts_rate_idx;
-
- memset(&txhdr->rateset, 0, sizeof(txhdr->rateset));
-
- /* see how many rates got used */
- for (i = 0; i < dev->max_rates; i++) {
- if (info->control.rates[i].idx < 0)
- break;
- nrates++;
- }
-
- /* limit tries to 8/nrates per rate */
- for (i = 0; i < nrates; i++) {
- /*
- * The magic expression here is equivalent to 8/nrates for
- * all values that matter, but avoids division and jumps.
- * Note that nrates can only take the values 1 through 4.
- */
- calculated_tries[i] = min_t(int, ((15 >> nrates) | 1) + 1,
- info->control.rates[i].count);
- nremaining -= calculated_tries[i];
- }
-
- /* if there are tries left, distribute from back to front */
- for (i = nrates - 1; nremaining > 0 && i >= 0; i--) {
- int tmp = info->control.rates[i].count - calculated_tries[i];
-
- if (tmp <= 0)
- continue;
- /* RC requested more tries at this rate */
-
- tmp = min_t(int, tmp, nremaining);
- calculated_tries[i] += tmp;
- nremaining -= tmp;
- }
-
- ridx = 0;
- for (i = 0; i < nrates && ridx < 8; i++) {
- /* we register the rates in perfect order */
- rate = info->control.rates[i].idx;
- if (info->band == IEEE80211_BAND_5GHZ)
- rate += 4;
-
- /* store the count we actually calculated for TX status */
- info->control.rates[i].count = calculated_tries[i];
-
- rc_flags = info->control.rates[i].flags;
- if (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) {
- rate |= 0x10;
- cts_rate |= 0x10;
- }
- if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
- burst_allowed = false;
- rate |= 0x40;
- } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
- rate |= 0x20;
- burst_allowed = false;
- }
- for (j = 0; j < calculated_tries[i] && ridx < 8; j++) {
- txhdr->rateset[ridx] = rate;
- ridx++;
- }
- }
-
- if (burst_allowed)
- hdr_flags |= P54_HDR_FLAG_DATA_OUT_BURST;
-
- /* TODO: enable bursting */
- hdr->flags = cpu_to_le16(hdr_flags);
- hdr->tries = ridx;
- txhdr->rts_rate_idx = 0;
- if (info->control.hw_key) {
- txhdr->key_type = p54_convert_algo(info->control.hw_key->cipher);
- txhdr->key_len = min((u8)16, info->control.hw_key->keylen);
- memcpy(txhdr->key, info->control.hw_key->key, txhdr->key_len);
- if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
- /* reserve space for the MIC key */
- len += 8;
- memcpy(skb_put(skb, 8), &(info->control.hw_key->key
- [NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]), 8);
- }
- /* reserve some space for ICV */
- len += info->control.hw_key->icv_len;
- memset(skb_put(skb, info->control.hw_key->icv_len), 0,
- info->control.hw_key->icv_len);
- } else {
- txhdr->key_type = 0;
- txhdr->key_len = 0;
- }
- txhdr->crypt_offset = crypt_offset;
- txhdr->hw_queue = queue;
- txhdr->backlog = priv->tx_stats[queue].len - 1;
- memset(txhdr->durations, 0, sizeof(txhdr->durations));
- txhdr->tx_antenna = 2 & priv->tx_diversity_mask;
- if (priv->rxhw == 5) {
- txhdr->longbow.cts_rate = cts_rate;
- txhdr->longbow.output_power = cpu_to_le16(priv->output_power);
- } else {
- txhdr->normal.output_power = priv->output_power;
- txhdr->normal.cts_rate = cts_rate;
- }
- if (padding)
- txhdr->align[0] = padding;
-
- hdr->len = cpu_to_le16(len);
- /* modifies skb->cb and with it info, so must be last! */
- p54info = (void *) info->rate_driver_data;
- p54info->extra_len = extra_len;
-
- p54_tx(priv, skb);
-}