p54: move under intersil vendor directory
Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Cette révision appartient à :
71
drivers/net/wireless/intersil/p54/Kconfig
Fichier normal
71
drivers/net/wireless/intersil/p54/Kconfig
Fichier normal
@@ -0,0 +1,71 @@
|
||||
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
|
7
drivers/net/wireless/intersil/p54/Makefile
Fichier normal
7
drivers/net/wireless/intersil/p54/Makefile
Fichier normal
@@ -0,0 +1,7 @@
|
||||
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
|
982
drivers/net/wireless/intersil/p54/eeprom.c
Fichier normal
982
drivers/net/wireless/intersil/p54/eeprom.c
Fichier normal
@@ -0,0 +1,982 @@
|
||||
/*
|
||||
* 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);
|
245
drivers/net/wireless/intersil/p54/eeprom.h
Fichier normal
245
drivers/net/wireless/intersil/p54/eeprom.h
Fichier normal
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* 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 */
|
764
drivers/net/wireless/intersil/p54/fwio.c
Fichier normal
764
drivers/net/wireless/intersil/p54/fwio.c
Fichier normal
@@ -0,0 +1,764 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
161
drivers/net/wireless/intersil/p54/led.c
Fichier normal
161
drivers/net/wireless/intersil/p54/led.c
Fichier normal
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
562
drivers/net/wireless/intersil/p54/lmac.h
Fichier normal
562
drivers/net/wireless/intersil/p54/lmac.h
Fichier normal
@@ -0,0 +1,562 @@
|
||||
/*
|
||||
* 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 */
|
867
drivers/net/wireless/intersil/p54/main.c
Fichier normal
867
drivers/net/wireless/intersil/p54/main.c
Fichier normal
@@ -0,0 +1,867 @@
|
||||
/*
|
||||
* 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);
|
281
drivers/net/wireless/intersil/p54/p54.h
Fichier normal
281
drivers/net/wireless/intersil/p54/p54.h
Fichier normal
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* 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 */
|
703
drivers/net/wireless/intersil/p54/p54pci.c
Fichier normal
703
drivers/net/wireless/intersil/p54/p54pci.c
Fichier normal
@@ -0,0 +1,703 @@
|
||||
|
||||
/*
|
||||
* 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);
|
112
drivers/net/wireless/intersil/p54/p54pci.h
Fichier normal
112
drivers/net/wireless/intersil/p54/p54pci.h
Fichier normal
@@ -0,0 +1,112 @@
|
||||
#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 */
|
720
drivers/net/wireless/intersil/p54/p54spi.c
Fichier normal
720
drivers/net/wireless/intersil/p54/p54spi.c
Fichier normal
@@ -0,0 +1,720 @@
|
||||
/*
|
||||
* 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");
|
125
drivers/net/wireless/intersil/p54/p54spi.h
Fichier normal
125
drivers/net/wireless/intersil/p54/p54spi.h
Fichier normal
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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 */
|
679
drivers/net/wireless/intersil/p54/p54spi_eeprom.h
Fichier normal
679
drivers/net/wireless/intersil/p54/p54spi_eeprom.h
Fichier normal
@@ -0,0 +1,679 @@
|
||||
/*
|
||||
* 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 */
|
||||
|
1149
drivers/net/wireless/intersil/p54/p54usb.c
Fichier normal
1149
drivers/net/wireless/intersil/p54/p54usb.c
Fichier normal
Fichier diff supprimé car celui-ci est trop grand
Voir la Diff
162
drivers/net/wireless/intersil/p54/p54usb.h
Fichier normal
162
drivers/net/wireless/intersil/p54/p54usb.h
Fichier normal
@@ -0,0 +1,162 @@
|
||||
#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 */
|
940
drivers/net/wireless/intersil/p54/txrx.c
Fichier normal
940
drivers/net/wireless/intersil/p54/txrx.c
Fichier normal
@@ -0,0 +1,940 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
Référencer dans un nouveau ticket
Bloquer un utilisateur