android_kernel_xiaomi_sm8350/drivers/net/wireless/p54/p54common.c
Bruno Randolf 566bfe5a8b mac80211: use hardware flags for signal/noise units
trying to clean up the signal/noise code. the previous code in mac80211 had
confusing names for the related variables, did not have much definition of
what units of signal and noise were provided and used implicit mechanisms from
the wireless extensions.

this patch introduces hardware capability flags to let the hardware specify
clearly if it can provide signal and noise level values and which units it can
provide. this also anticipates possible new units like RCPI in the future.

for signal:

  IEEE80211_HW_SIGNAL_UNSPEC - unspecified, unknown, hw specific
  IEEE80211_HW_SIGNAL_DB     - dB difference to unspecified reference point
  IEEE80211_HW_SIGNAL_DBM    - dBm, difference to 1mW

for noise we currently only have dBm:

  IEEE80211_HW_NOISE_DBM     - dBm, difference to 1mW

if IEEE80211_HW_SIGNAL_UNSPEC or IEEE80211_HW_SIGNAL_DB is used the driver has
to provide the maximum value (max_signal) it reports in order for applications
to make sense of the signal values.

i tried my best to find out for each driver what it can provide and update it
but i'm not sure (?) for some of them and used the more conservative guess in
doubt. this can be fixed easily after this patch has been merged by changing
the hardware flags of the driver.

DRIVER          SIGNAL    MAX	NOISE   QUAL
-----------------------------------------------------------------
adm8211         unspec(?) 100   n/a     missing
at76_usb        unspec(?) (?)   unused  missing
ath5k           dBm             dBm     percent rssi
b43legacy       dBm             dBm     percent jssi(?)
b43             dBm             dBm     percent jssi(?)
iwl-3945        dBm             dBm     percent snr+more
iwl-4965        dBm             dBm     percent snr+more
p54             unspec    127   n/a     missing
rt2x00          dBm	        n/a     percent rssi+tx/rx frame success
  rt2400        dBm             n/a
  rt2500pci     dBm             n/a
  rt2500usb     dBm             n/a
  rt61pci       dBm             n/a
  rt73usb       dBm             n/a
rtl8180         unspec(?) 65    n/a     (?)
rtl8187         unspec(?) 65    (?)     noise(?)
zd1211          dB(?)     100   n/a     percent

drivers/net/wireless/ath5k/base.c:      Changes-licensed-under: 3-Clause-BSD

Signed-off-by: Bruno Randolf <br1@einfach.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2008-05-14 16:29:49 -04:00

1049 lines
29 KiB
C

/*
* Common code for mac80211 Prism54 drivers
*
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
* Copyright (c) 2007, 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.
*
* 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/init.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>
#include <net/mac80211.h>
#include "p54.h"
#include "p54common.h"
MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
MODULE_DESCRIPTION("Softmac Prism54 common code");
MODULE_LICENSE("GPL");
MODULE_ALIAS("prism54common");
static struct ieee80211_rate p54_rates[] = {
{ .bitrate = 10, .hw_value = 0, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .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_channel p54_channels[] = {
{ .center_freq = 2412, .hw_value = 1, },
{ .center_freq = 2417, .hw_value = 2, },
{ .center_freq = 2422, .hw_value = 3, },
{ .center_freq = 2427, .hw_value = 4, },
{ .center_freq = 2432, .hw_value = 5, },
{ .center_freq = 2437, .hw_value = 6, },
{ .center_freq = 2442, .hw_value = 7, },
{ .center_freq = 2447, .hw_value = 8, },
{ .center_freq = 2452, .hw_value = 9, },
{ .center_freq = 2457, .hw_value = 10, },
{ .center_freq = 2462, .hw_value = 11, },
{ .center_freq = 2467, .hw_value = 12, },
{ .center_freq = 2472, .hw_value = 13, },
{ .center_freq = 2484, .hw_value = 14, },
};
static struct ieee80211_supported_band band_2GHz = {
.channels = p54_channels,
.n_channels = ARRAY_SIZE(p54_channels),
.bitrates = p54_rates,
.n_bitrates = ARRAY_SIZE(p54_rates),
};
void p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
{
struct p54_common *priv = dev->priv;
struct bootrec_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;
if (priv->rx_start)
return;
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:
switch (be32_to_cpu(*(__be32 *)bootrec->data)) {
case FW_FMAC:
printk(KERN_INFO "p54: FreeMAC firmware\n");
break;
case FW_LM20:
printk(KERN_INFO "p54: LM20 firmware\n");
break;
case FW_LM86:
printk(KERN_INFO "p54: LM86 firmware\n");
break;
case FW_LM87:
printk(KERN_INFO "p54: LM87 firmware - not supported yet!\n");
break;
default:
printk(KERN_INFO "p54: unknown firmware\n");
break;
}
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:
priv->rx_start = le32_to_cpu(((__le32 *)bootrec->data)[1]);
/* FIXME add sanity checking */
priv->rx_end = le32_to_cpu(((__le32 *)bootrec->data)[2]) - 0x3500;
break;
case BR_CODE_EXPOSED_IF:
exp_if = (struct bootrec_exp_if *) bootrec->data;
for (i = 0; i < (len * sizeof(*exp_if) / 4); i++)
if (exp_if[i].if_id == cpu_to_le16(0x1a))
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)
printk(KERN_INFO "p54: FW rev %s - Softmac protocol %x.%x\n",
fw_version, priv->fw_var >> 8, priv->fw_var & 0xff);
if (priv->fw_var >= 0x300) {
/* Firmware supports QoS, use it! */
priv->tx_stats[0].limit = 3;
priv->tx_stats[1].limit = 4;
priv->tx_stats[2].limit = 3;
priv->tx_stats[3].limit = 1;
dev->queues = 4;
}
}
EXPORT_SYMBOL_GPL(p54_parse_firmware);
static int p54_convert_rev0_to_rev1(struct ieee80211_hw *dev,
struct pda_pa_curve_data *curve_data)
{
struct p54_common *priv = dev->priv;
struct pda_pa_curve_data_sample_rev1 *rev1;
struct pda_pa_curve_data_sample_rev0 *rev0;
size_t cd_len = sizeof(*curve_data) +
(curve_data->points_per_channel*sizeof(*rev1) + 2) *
curve_data->channels;
unsigned int i, j;
void *source, *target;
priv->curve_data = kmalloc(cd_len, GFP_KERNEL);
if (!priv->curve_data)
return -ENOMEM;
memcpy(priv->curve_data, curve_data, sizeof(*curve_data));
source = curve_data->data;
target = priv->curve_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++) {
rev1 = target;
rev0 = source;
rev1->rf_power = rev0->rf_power;
rev1->pa_detector = rev0->pa_detector;
rev1->data_64qam = rev0->pcv;
/* "invent" the points for the other modulations */
#define SUB(x,y) (u8)((x) - (y)) > (x) ? 0 : (x) - (y)
rev1->data_16qam = SUB(rev0->pcv, 12);
rev1->data_qpsk = SUB(rev1->data_16qam, 12);
rev1->data_bpsk = SUB(rev1->data_qpsk, 12);
rev1->data_barker= SUB(rev1->data_bpsk, 14);
#undef SUB
target += sizeof(*rev1);
source += sizeof(*rev0);
}
}
return 0;
}
int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
{
struct p54_common *priv = dev->priv;
struct eeprom_pda_wrap *wrap = NULL;
struct pda_entry *entry;
unsigned int data_len, entry_len;
void *tmp;
int err;
u8 *end = (u8 *)eeprom + len;
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:
SET_IEEE80211_PERM_ADDR(dev, entry->data);
break;
case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS:
if (data_len < 2) {
err = -EINVAL;
goto err;
}
if (2 + entry->data[1]*sizeof(*priv->output_limit) > data_len) {
err = -EINVAL;
goto err;
}
priv->output_limit = kmalloc(entry->data[1] *
sizeof(*priv->output_limit), GFP_KERNEL);
if (!priv->output_limit) {
err = -ENOMEM;
goto err;
}
memcpy(priv->output_limit, &entry->data[2],
entry->data[1]*sizeof(*priv->output_limit));
priv->output_limit_len = entry->data[1];
break;
case PDR_PRISM_PA_CAL_CURVE_DATA:
if (data_len < sizeof(struct pda_pa_curve_data)) {
err = -EINVAL;
goto err;
}
if (((struct pda_pa_curve_data *)entry->data)->cal_method_rev) {
priv->curve_data = kmalloc(data_len, GFP_KERNEL);
if (!priv->curve_data) {
err = -ENOMEM;
goto err;
}
memcpy(priv->curve_data, entry->data, data_len);
} else {
err = p54_convert_rev0_to_rev1(dev, (struct pda_pa_curve_data *)entry->data);
if (err)
goto err;
}
break;
case PDR_PRISM_ZIF_TX_IQ_CALIBRATION:
priv->iq_autocal = kmalloc(data_len, GFP_KERNEL);
if (!priv->iq_autocal) {
err = -ENOMEM;
goto err;
}
memcpy(priv->iq_autocal, entry->data, data_len);
priv->iq_autocal_len = data_len / sizeof(struct pda_iq_autocal_entry);
break;
case PDR_INTERFACE_LIST:
tmp = entry->data;
while ((u8 *)tmp < entry->data + data_len) {
struct bootrec_exp_if *exp_if = tmp;
if (le16_to_cpu(exp_if->if_id) == 0xF)
priv->rxhw = exp_if->variant & cpu_to_le16(0x07);
tmp += sizeof(struct bootrec_exp_if);
}
break;
case PDR_HARDWARE_PLATFORM_COMPONENT_ID:
priv->version = *(u8 *)(entry->data + 1);
break;
case PDR_END:
/* make it overrun */
entry_len = len;
break;
default:
printk(KERN_INFO "p54: unknown eeprom code : 0x%x\n",
le16_to_cpu(entry->code));
break;
}
entry = (void *)entry + (entry_len + 1)*2;
}
if (!priv->iq_autocal || !priv->output_limit || !priv->curve_data) {
printk(KERN_ERR "p54: not all required entries found in eeprom!\n");
err = -EINVAL;
goto err;
}
return 0;
err:
if (priv->iq_autocal) {
kfree(priv->iq_autocal);
priv->iq_autocal = NULL;
}
if (priv->output_limit) {
kfree(priv->output_limit);
priv->output_limit = NULL;
}
if (priv->curve_data) {
kfree(priv->curve_data);
priv->curve_data = NULL;
}
printk(KERN_ERR "p54: eeprom parse failed!\n");
return err;
}
EXPORT_SYMBOL_GPL(p54_parse_eeprom);
void p54_fill_eeprom_readback(struct p54_control_hdr *hdr)
{
struct p54_eeprom_lm86 *eeprom_hdr;
hdr->magic1 = cpu_to_le16(0x8000);
hdr->len = cpu_to_le16(sizeof(*eeprom_hdr) + 0x2000);
hdr->type = cpu_to_le16(P54_CONTROL_TYPE_EEPROM_READBACK);
hdr->retry1 = hdr->retry2 = 0;
eeprom_hdr = (struct p54_eeprom_lm86 *) hdr->data;
eeprom_hdr->offset = 0x0;
eeprom_hdr->len = cpu_to_le16(0x2000);
}
EXPORT_SYMBOL_GPL(p54_fill_eeprom_readback);
static void p54_rx_data(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54_rx_hdr *hdr = (struct p54_rx_hdr *) skb->data;
struct ieee80211_rx_status rx_status = {0};
u16 freq = le16_to_cpu(hdr->freq);
rx_status.signal = hdr->rssi;
/* XX correct? */
rx_status.rate_idx = hdr->rate & 0xf;
rx_status.freq = freq;
rx_status.band = IEEE80211_BAND_2GHZ;
rx_status.antenna = hdr->antenna;
rx_status.mactime = le64_to_cpu(hdr->timestamp);
rx_status.flag |= RX_FLAG_TSFT;
skb_pull(skb, sizeof(*hdr));
skb_trim(skb, le16_to_cpu(hdr->len));
ieee80211_rx_irqsafe(dev, skb, &rx_status);
}
static void inline p54_wake_free_queues(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
int i;
/* ieee80211_start_queues is great if all queues are really empty.
* But, what if some are full? */
for (i = 0; i < dev->queues; i++)
if (priv->tx_stats[i].len < priv->tx_stats[i].limit)
ieee80211_wake_queue(dev, i);
}
static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54_common *priv = dev->priv;
struct p54_control_hdr *hdr = (struct p54_control_hdr *) skb->data;
struct p54_frame_sent_hdr *payload = (struct p54_frame_sent_hdr *) hdr->data;
struct sk_buff *entry = (struct sk_buff *) priv->tx_queue.next;
u32 addr = le32_to_cpu(hdr->req_id) - 0x70;
struct memrecord *range = NULL;
u32 freed = 0;
u32 last_addr = priv->rx_start;
while (entry != (struct sk_buff *)&priv->tx_queue) {
range = (struct memrecord *)&entry->cb;
if (range->start_addr == addr) {
struct ieee80211_tx_status status;
struct p54_control_hdr *entry_hdr;
struct p54_tx_control_allocdata *entry_data;
int pad = 0;
if (entry->next != (struct sk_buff *)&priv->tx_queue)
freed = ((struct memrecord *)&entry->next->cb)->start_addr - last_addr;
else
freed = priv->rx_end - last_addr;
last_addr = range->end_addr;
__skb_unlink(entry, &priv->tx_queue);
if (!range->control) {
kfree_skb(entry);
break;
}
memset(&status, 0, sizeof(status));
memcpy(&status.control, range->control,
sizeof(status.control));
kfree(range->control);
priv->tx_stats[status.control.queue].len--;
entry_hdr = (struct p54_control_hdr *) entry->data;
entry_data = (struct p54_tx_control_allocdata *) entry_hdr->data;
if ((entry_hdr->magic1 & cpu_to_le16(0x4000)) != 0)
pad = entry_data->align[0];
if (!(status.control.flags & IEEE80211_TXCTL_NO_ACK)) {
if (!(payload->status & 0x01))
status.flags |= IEEE80211_TX_STATUS_ACK;
else
status.excessive_retries = 1;
}
status.retry_count = payload->retries - 1;
status.ack_signal = le16_to_cpu(payload->ack_rssi);
skb_pull(entry, sizeof(*hdr) + pad + sizeof(*entry_data));
ieee80211_tx_status_irqsafe(dev, entry, &status);
break;
} else
last_addr = range->end_addr;
entry = entry->next;
}
if (freed >= IEEE80211_MAX_RTS_THRESHOLD + 0x170 +
sizeof(struct p54_control_hdr))
p54_wake_free_queues(dev);
}
static void p54_rx_control(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54_control_hdr *hdr = (struct p54_control_hdr *) skb->data;
switch (le16_to_cpu(hdr->type)) {
case P54_CONTROL_TYPE_TXDONE:
p54_rx_frame_sent(dev, skb);
break;
case P54_CONTROL_TYPE_BBP:
break;
default:
printk(KERN_DEBUG "%s: not handling 0x%02x type control frame\n",
wiphy_name(dev->wiphy), le16_to_cpu(hdr->type));
break;
}
}
/* returns zero if skb can be reused */
int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb)
{
u8 type = le16_to_cpu(*((__le16 *)skb->data)) >> 8;
switch (type) {
case 0x00:
case 0x01:
p54_rx_data(dev, skb);
return -1;
case 0x4d:
/* TODO: do something better... but then again, I've never seen this happen */
printk(KERN_ERR "%s: Received fault. Probably need to restart hardware now..\n",
wiphy_name(dev->wiphy));
break;
case 0x80:
p54_rx_control(dev, skb);
break;
default:
printk(KERN_ERR "%s: unknown frame RXed (0x%02x)\n",
wiphy_name(dev->wiphy), type);
break;
}
return 0;
}
EXPORT_SYMBOL_GPL(p54_rx);
/*
* 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_rx_frame_sent frees
* allocated areas.
*/
static void p54_assign_address(struct ieee80211_hw *dev, struct sk_buff *skb,
struct p54_control_hdr *data, u32 len,
struct ieee80211_tx_control *control)
{
struct p54_common *priv = dev->priv;
struct sk_buff *entry = priv->tx_queue.next;
struct sk_buff *target_skb = NULL;
struct memrecord *range;
u32 last_addr = priv->rx_start;
u32 largest_hole = 0;
u32 target_addr = priv->rx_start;
unsigned long flags;
unsigned int left;
len = (len + 0x170 + 3) & ~0x3; /* 0x70 headroom, 0x100 tailroom */
spin_lock_irqsave(&priv->tx_queue.lock, flags);
left = skb_queue_len(&priv->tx_queue);
while (left--) {
u32 hole_size;
range = (struct memrecord *)&entry->cb;
hole_size = range->start_addr - last_addr;
if (!target_skb && hole_size >= len) {
target_skb = entry->prev;
hole_size -= len;
target_addr = last_addr;
}
largest_hole = max(largest_hole, hole_size);
last_addr = range->end_addr;
entry = entry->next;
}
if (!target_skb && priv->rx_end - last_addr >= len) {
target_skb = priv->tx_queue.prev;
largest_hole = max(largest_hole, priv->rx_end - last_addr - len);
if (!skb_queue_empty(&priv->tx_queue)) {
range = (struct memrecord *)&target_skb->cb;
target_addr = range->end_addr;
}
} else
largest_hole = max(largest_hole, priv->rx_end - last_addr);
if (skb) {
range = (struct memrecord *)&skb->cb;
range->start_addr = target_addr;
range->end_addr = target_addr + len;
range->control = control;
__skb_queue_after(&priv->tx_queue, target_skb, skb);
if (largest_hole < IEEE80211_MAX_RTS_THRESHOLD + 0x170 +
sizeof(struct p54_control_hdr))
ieee80211_stop_queues(dev);
}
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
data->req_id = cpu_to_le32(target_addr + 0x70);
}
static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb,
struct ieee80211_tx_control *control)
{
struct ieee80211_tx_queue_stats *current_queue;
struct p54_common *priv = dev->priv;
struct p54_control_hdr *hdr;
struct p54_tx_control_allocdata *txhdr;
struct ieee80211_tx_control *control_copy;
size_t padding, len;
u8 rate;
current_queue = &priv->tx_stats[control->queue];
if (unlikely(current_queue->len > current_queue->limit))
return NETDEV_TX_BUSY;
current_queue->len++;
current_queue->count++;
if (current_queue->len == current_queue->limit)
ieee80211_stop_queue(dev, control->queue);
padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3;
len = skb->len;
control_copy = kmalloc(sizeof(*control), GFP_ATOMIC);
if (control_copy)
memcpy(control_copy, control, sizeof(*control));
txhdr = (struct p54_tx_control_allocdata *)
skb_push(skb, sizeof(*txhdr) + padding);
hdr = (struct p54_control_hdr *) skb_push(skb, sizeof(*hdr));
if (padding)
hdr->magic1 = cpu_to_le16(0x4010);
else
hdr->magic1 = cpu_to_le16(0x0010);
hdr->len = cpu_to_le16(len);
hdr->type = (control->flags & IEEE80211_TXCTL_NO_ACK) ? 0 : cpu_to_le16(1);
hdr->retry1 = hdr->retry2 = control->retry_limit;
p54_assign_address(dev, skb, hdr, skb->len, control_copy);
memset(txhdr->wep_key, 0x0, 16);
txhdr->padding = 0;
txhdr->padding2 = 0;
/* TODO: add support for alternate retry TX rates */
rate = control->tx_rate->hw_value;
if (control->flags & IEEE80211_TXCTL_SHORT_PREAMBLE)
rate |= 0x10;
if (control->flags & IEEE80211_TXCTL_USE_RTS_CTS)
rate |= 0x40;
else if (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)
rate |= 0x20;
memset(txhdr->rateset, rate, 8);
txhdr->wep_key_present = 0;
txhdr->wep_key_len = 0;
txhdr->frame_type = cpu_to_le32(control->queue + 4);
txhdr->magic4 = 0;
txhdr->antenna = (control->antenna_sel_tx == 0) ?
2 : control->antenna_sel_tx - 1;
txhdr->output_power = 0x7f; // HW Maximum
txhdr->magic5 = (control->flags & IEEE80211_TXCTL_NO_ACK) ?
0 : ((rate > 0x3) ? cpu_to_le32(0x33) : cpu_to_le32(0x23));
if (padding)
txhdr->align[0] = padding;
priv->tx(dev, hdr, skb->len, 0);
return 0;
}
static int p54_set_filter(struct ieee80211_hw *dev, u16 filter_type,
const u8 *dst, const u8 *src, u8 antenna,
u32 magic3, u32 magic8, u32 magic9)
{
struct p54_common *priv = dev->priv;
struct p54_control_hdr *hdr;
struct p54_tx_control_filter *filter;
hdr = kzalloc(sizeof(*hdr) + sizeof(*filter) +
priv->tx_hdr_len, GFP_ATOMIC);
if (!hdr)
return -ENOMEM;
hdr = (void *)hdr + priv->tx_hdr_len;
filter = (struct p54_tx_control_filter *) hdr->data;
hdr->magic1 = cpu_to_le16(0x8001);
hdr->len = cpu_to_le16(sizeof(*filter));
p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + sizeof(*filter), NULL);
hdr->type = cpu_to_le16(P54_CONTROL_TYPE_FILTER_SET);
filter->filter_type = cpu_to_le16(filter_type);
memcpy(filter->dst, dst, ETH_ALEN);
if (!src)
memset(filter->src, ~0, ETH_ALEN);
else
memcpy(filter->src, src, ETH_ALEN);
filter->antenna = antenna;
filter->magic3 = cpu_to_le32(magic3);
filter->rx_addr = cpu_to_le32(priv->rx_end);
filter->max_rx = cpu_to_le16(0x0620); /* FIXME: for usb ver 1.. maybe */
filter->rxhw = priv->rxhw;
filter->magic8 = cpu_to_le16(magic8);
filter->magic9 = cpu_to_le16(magic9);
priv->tx(dev, hdr, sizeof(*hdr) + sizeof(*filter), 1);
return 0;
}
static int p54_set_freq(struct ieee80211_hw *dev, __le16 freq)
{
struct p54_common *priv = dev->priv;
struct p54_control_hdr *hdr;
struct p54_tx_control_channel *chan;
unsigned int i;
size_t payload_len = sizeof(*chan) + sizeof(u32)*2 +
sizeof(*chan->curve_data) *
priv->curve_data->points_per_channel;
void *entry;
hdr = kzalloc(sizeof(*hdr) + payload_len +
priv->tx_hdr_len, GFP_KERNEL);
if (!hdr)
return -ENOMEM;
hdr = (void *)hdr + priv->tx_hdr_len;
chan = (struct p54_tx_control_channel *) hdr->data;
hdr->magic1 = cpu_to_le16(0x8001);
hdr->len = cpu_to_le16(sizeof(*chan));
hdr->type = cpu_to_le16(P54_CONTROL_TYPE_CHANNEL_CHANGE);
p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + payload_len, NULL);
chan->magic1 = cpu_to_le16(0x1);
chan->magic2 = cpu_to_le16(0x0);
for (i = 0; i < priv->iq_autocal_len; i++) {
if (priv->iq_autocal[i].freq != freq)
continue;
memcpy(&chan->iq_autocal, &priv->iq_autocal[i],
sizeof(*priv->iq_autocal));
break;
}
if (i == priv->iq_autocal_len)
goto err;
for (i = 0; i < priv->output_limit_len; i++) {
if (priv->output_limit[i].freq != freq)
continue;
chan->val_barker = 0x38;
chan->val_bpsk = priv->output_limit[i].val_bpsk;
chan->val_qpsk = priv->output_limit[i].val_qpsk;
chan->val_16qam = priv->output_limit[i].val_16qam;
chan->val_64qam = priv->output_limit[i].val_64qam;
break;
}
if (i == priv->output_limit_len)
goto err;
chan->pa_points_per_curve = priv->curve_data->points_per_channel;
entry = priv->curve_data->data;
for (i = 0; i < priv->curve_data->channels; i++) {
if (*((__le16 *)entry) != freq) {
entry += sizeof(__le16);
entry += sizeof(struct pda_pa_curve_data_sample_rev1) *
chan->pa_points_per_curve;
continue;
}
entry += sizeof(__le16);
memcpy(chan->curve_data, entry, sizeof(*chan->curve_data) *
chan->pa_points_per_curve);
break;
}
memcpy(hdr->data + payload_len - 4, &chan->val_bpsk, 4);
priv->tx(dev, hdr, sizeof(*hdr) + payload_len, 1);
return 0;
err:
printk(KERN_ERR "%s: frequency change failed\n", wiphy_name(dev->wiphy));
kfree(hdr);
return -EINVAL;
}
static int p54_set_leds(struct ieee80211_hw *dev, int mode, int link, int act)
{
struct p54_common *priv = dev->priv;
struct p54_control_hdr *hdr;
struct p54_tx_control_led *led;
hdr = kzalloc(sizeof(*hdr) + sizeof(*led) +
priv->tx_hdr_len, GFP_KERNEL);
if (!hdr)
return -ENOMEM;
hdr = (void *)hdr + priv->tx_hdr_len;
hdr->magic1 = cpu_to_le16(0x8001);
hdr->len = cpu_to_le16(sizeof(*led));
hdr->type = cpu_to_le16(P54_CONTROL_TYPE_LED);
p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + sizeof(*led), NULL);
led = (struct p54_tx_control_led *) hdr->data;
led->mode = cpu_to_le16(mode);
led->led_permanent = cpu_to_le16(link);
led->led_temporary = cpu_to_le16(act);
led->duration = cpu_to_le16(1000);
priv->tx(dev, hdr, sizeof(*hdr) + sizeof(*led), 1);
return 0;
}
#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)
static void p54_init_vdcf(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
struct p54_control_hdr *hdr;
struct p54_tx_control_vdcf *vdcf;
/* all USB V1 adapters need a extra headroom */
hdr = (void *)priv->cached_vdcf + priv->tx_hdr_len;
hdr->magic1 = cpu_to_le16(0x8001);
hdr->len = cpu_to_le16(sizeof(*vdcf));
hdr->type = cpu_to_le16(P54_CONTROL_TYPE_DCFINIT);
hdr->req_id = cpu_to_le32(priv->rx_start);
vdcf = (struct p54_tx_control_vdcf *) hdr->data;
P54_SET_QUEUE(vdcf->queue[0], 0x0002, 0x0003, 0x0007, 47);
P54_SET_QUEUE(vdcf->queue[1], 0x0002, 0x0007, 0x000f, 94);
P54_SET_QUEUE(vdcf->queue[2], 0x0003, 0x000f, 0x03ff, 0);
P54_SET_QUEUE(vdcf->queue[3], 0x0007, 0x000f, 0x03ff, 0);
}
static void p54_set_vdcf(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
struct p54_control_hdr *hdr;
struct p54_tx_control_vdcf *vdcf;
hdr = (void *)priv->cached_vdcf + priv->tx_hdr_len;
p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + sizeof(*vdcf), NULL);
vdcf = (struct p54_tx_control_vdcf *) hdr->data;
if (dev->conf.flags & IEEE80211_CONF_SHORT_SLOT_TIME) {
vdcf->slottime = 9;
vdcf->magic1 = 0x00;
vdcf->magic2 = 0x10;
} else {
vdcf->slottime = 20;
vdcf->magic1 = 0x0a;
vdcf->magic2 = 0x06;
}
/* (see prism54/isl_oid.h for further details) */
vdcf->frameburst = cpu_to_le16(0);
priv->tx(dev, hdr, sizeof(*hdr) + sizeof(*vdcf), 0);
}
static int p54_start(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
int err;
err = priv->open(dev);
if (!err)
priv->mode = IEEE80211_IF_TYPE_MNTR;
return err;
}
static void p54_stop(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
struct sk_buff *skb;
while ((skb = skb_dequeue(&priv->tx_queue))) {
struct memrecord *range = (struct memrecord *)&skb->cb;
if (range->control)
kfree(range->control);
kfree_skb(skb);
}
priv->stop(dev);
priv->mode = IEEE80211_IF_TYPE_INVALID;
}
static int p54_add_interface(struct ieee80211_hw *dev,
struct ieee80211_if_init_conf *conf)
{
struct p54_common *priv = dev->priv;
if (priv->mode != IEEE80211_IF_TYPE_MNTR)
return -EOPNOTSUPP;
switch (conf->type) {
case IEEE80211_IF_TYPE_STA:
priv->mode = conf->type;
break;
default:
return -EOPNOTSUPP;
}
memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN);
p54_set_filter(dev, 0, priv->mac_addr, NULL, 0, 1, 0, 0xF642);
p54_set_filter(dev, 0, priv->mac_addr, NULL, 1, 0, 0, 0xF642);
switch (conf->type) {
case IEEE80211_IF_TYPE_STA:
p54_set_filter(dev, 1, priv->mac_addr, NULL, 0, 0x15F, 0x1F4, 0);
break;
default:
BUG(); /* impossible */
break;
}
p54_set_leds(dev, 1, 0, 0);
return 0;
}
static void p54_remove_interface(struct ieee80211_hw *dev,
struct ieee80211_if_init_conf *conf)
{
struct p54_common *priv = dev->priv;
priv->mode = IEEE80211_IF_TYPE_MNTR;
memset(priv->mac_addr, 0, ETH_ALEN);
p54_set_filter(dev, 0, priv->mac_addr, NULL, 2, 0, 0, 0);
}
static int p54_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf)
{
int ret;
ret = p54_set_freq(dev, cpu_to_le16(conf->channel->center_freq));
p54_set_vdcf(dev);
return ret;
}
static int p54_config_interface(struct ieee80211_hw *dev,
struct ieee80211_vif *vif,
struct ieee80211_if_conf *conf)
{
struct p54_common *priv = dev->priv;
p54_set_filter(dev, 0, priv->mac_addr, conf->bssid, 0, 1, 0, 0xF642);
p54_set_filter(dev, 0, priv->mac_addr, conf->bssid, 2, 0, 0, 0);
p54_set_leds(dev, 1, !is_multicast_ether_addr(conf->bssid), 0);
memcpy(priv->bssid, conf->bssid, ETH_ALEN);
return 0;
}
static void p54_configure_filter(struct ieee80211_hw *dev,
unsigned int changed_flags,
unsigned int *total_flags,
int mc_count, struct dev_mc_list *mclist)
{
struct p54_common *priv = dev->priv;
*total_flags &= FIF_BCN_PRBRESP_PROMISC;
if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
p54_set_filter(dev, 0, priv->mac_addr,
NULL, 2, 0, 0, 0);
else
p54_set_filter(dev, 0, priv->mac_addr,
priv->bssid, 2, 0, 0, 0);
}
}
static int p54_conf_tx(struct ieee80211_hw *dev, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
struct p54_common *priv = dev->priv;
struct p54_tx_control_vdcf *vdcf;
vdcf = (struct p54_tx_control_vdcf *)(((struct p54_control_hdr *)
((void *)priv->cached_vdcf + priv->tx_hdr_len))->data);
if ((params) && !(queue > 4)) {
P54_SET_QUEUE(vdcf->queue[queue], params->aifs,
params->cw_min, params->cw_max, params->txop);
} else
return -EINVAL;
p54_set_vdcf(dev);
return 0;
}
static int p54_get_stats(struct ieee80211_hw *dev,
struct ieee80211_low_level_stats *stats)
{
/* TODO */
return 0;
}
static int p54_get_tx_stats(struct ieee80211_hw *dev,
struct ieee80211_tx_queue_stats *stats)
{
struct p54_common *priv = dev->priv;
memcpy(stats, &priv->tx_stats, sizeof(stats[0]) * dev->queues);
return 0;
}
static const struct ieee80211_ops p54_ops = {
.tx = p54_tx,
.start = p54_start,
.stop = p54_stop,
.add_interface = p54_add_interface,
.remove_interface = p54_remove_interface,
.config = p54_config,
.config_interface = p54_config_interface,
.configure_filter = p54_configure_filter,
.conf_tx = p54_conf_tx,
.get_stats = p54_get_stats,
.get_tx_stats = p54_get_tx_stats
};
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->mode = IEEE80211_IF_TYPE_INVALID;
skb_queue_head_init(&priv->tx_queue);
dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &band_2GHz;
dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | /* not sure */
IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_SIGNAL_UNSPEC;
dev->channel_change_time = 1000; /* TODO: find actual value */
dev->max_signal = 127;
priv->tx_stats[0].limit = 5;
dev->queues = 1;
dev->extra_tx_headroom = sizeof(struct p54_control_hdr) + 4 +
sizeof(struct p54_tx_control_allocdata);
priv->cached_vdcf = kzalloc(sizeof(struct p54_tx_control_vdcf) +
priv->tx_hdr_len + sizeof(struct p54_control_hdr), GFP_KERNEL);
if (!priv->cached_vdcf) {
ieee80211_free_hw(dev);
return NULL;
}
p54_init_vdcf(dev);
return dev;
}
EXPORT_SYMBOL_GPL(p54_init_common);
void p54_free_common(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
kfree(priv->iq_autocal);
kfree(priv->output_limit);
kfree(priv->curve_data);
kfree(priv->cached_vdcf);
}
EXPORT_SYMBOL_GPL(p54_free_common);
static int __init p54_init(void)
{
return 0;
}
static void __exit p54_exit(void)
{
}
module_init(p54_init);
module_exit(p54_exit);