Skip to content

Commit

Permalink
p54: fix rssi auto calibration
Browse files Browse the repository at this point in the history
Ever wondered why the signal was so bad with p54 compared to madwifi, or intel?

Well, if you have revision 1 rssi calibration curve points in your EEPROM, then wonder no more.
The firmware wants a extra 1 byte padding for every curve point. But someone forgot to put
them into the EEPROM's data structure...

So now, big question: what happens when we blindly "memcpy" these data points?

Signed-off-by: Christian Lamparter <chunkeey@web.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Christian Lamparter authored and John W. Linville committed Aug 29, 2008
1 parent a3ec233 commit 154e3af
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 52 deletions.
140 changes: 92 additions & 48 deletions drivers/net/wireless/p54/p54common.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,14 @@ void p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
}
EXPORT_SYMBOL_GPL(p54_parse_firmware);

static int p54_convert_rev0_to_rev1(struct ieee80211_hw *dev,
struct pda_pa_curve_data *curve_data)
static int p54_convert_rev0(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;
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(*rev1) + 2) *
(curve_data->points_per_channel*sizeof(*dst) + 2) *
curve_data->channels;
unsigned int i, j;
void *source, *target;
Expand All @@ -180,27 +180,63 @@ static int p54_convert_rev0_to_rev1(struct ieee80211_hw *dev,
*((__le16 *)target) = *freq;
target += sizeof(__le16);
for (j = 0; j < curve_data->points_per_channel; j++) {
rev1 = target;
rev0 = source;
dst = target;
src = source;

rev1->rf_power = rev0->rf_power;
rev1->pa_detector = rev0->pa_detector;
rev1->data_64qam = rev0->pcv;
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)
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);
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(*rev1);
source += sizeof(*rev0);
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 = 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++) {
memcpy(target, source, sizeof(*src));

target += sizeof(*dst);
source += sizeof(*src);
}
source++;
}

return 0;
}

int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
{
struct p54_common *priv = dev->priv;
Expand Down Expand Up @@ -250,27 +286,32 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
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)) {
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;
}

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;
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:
printk(KERN_ERR "p54: 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 = kmalloc(data_len, GFP_KERNEL);
if (!priv->iq_autocal) {
Expand Down Expand Up @@ -672,12 +713,9 @@ static int p54_set_freq(struct ieee80211_hw *dev, __le16 freq)
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 +
hdr = kzalloc(sizeof(*hdr) + sizeof(*chan) +
priv->tx_hdr_len, GFP_KERNEL);
if (!hdr)
return -ENOMEM;
Expand All @@ -689,10 +727,10 @@ static int p54_set_freq(struct ieee80211_hw *dev, __le16 freq)
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);
p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + sizeof(*chan));

chan->magic1 = cpu_to_le16(0x1);
chan->magic2 = cpu_to_le16(0x0);
chan->flags = cpu_to_le16(0x1);
chan->dwell = cpu_to_le16(0x0);

for (i = 0; i < priv->iq_autocal_len; i++) {
if (priv->iq_autocal[i].freq != freq)
Expand All @@ -710,35 +748,41 @@ static int p54_set_freq(struct ieee80211_hw *dev, __le16 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;
chan->val_bpsk = chan->dup_bpsk =
priv->output_limit[i].val_bpsk;
chan->val_qpsk = chan->dup_qpsk =
priv->output_limit[i].val_qpsk;
chan->val_16qam = chan->dup_16qam =
priv->output_limit[i].val_16qam;
chan->val_64qam = chan->dup_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;
entry += sizeof(struct p54_pa_curve_data_sample) *
priv->curve_data->points_per_channel;
continue;
}

entry += sizeof(__le16);
chan->pa_points_per_curve =
min(priv->curve_data->points_per_channel, (u8) 8);

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);
chan->rssical_mul = cpu_to_le16(130);
chan->rssical_add = cpu_to_le16(0xfe70); /* -400 */

priv->tx(dev, hdr, sizeof(*hdr) + payload_len, 1);
priv->tx(dev, hdr, sizeof(*hdr) + sizeof(*chan), 1);
return 0;

err:
Expand Down
23 changes: 19 additions & 4 deletions drivers/net/wireless/p54/p54common.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@ struct pda_pa_curve_data_sample_rev1 {
u8 data_qpsk;
u8 data_16qam;
u8 data_64qam;
} __attribute__ ((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;
} __attribute__ ((packed));

Expand Down Expand Up @@ -212,8 +222,8 @@ struct p54_tx_control_filter {
} __attribute__ ((packed));

struct p54_tx_control_channel {
__le16 magic1;
__le16 magic2;
__le16 flags;
__le16 dwell;
u8 padding1[20];
struct pda_iq_autocal_entry iq_autocal;
u8 pa_points_per_curve;
Expand All @@ -222,8 +232,13 @@ struct p54_tx_control_channel {
u8 val_qpsk;
u8 val_16qam;
u8 val_64qam;
struct pda_pa_curve_data_sample_rev1 curve_data[0];
/* additional padding/data after curve_data */
struct pda_pa_curve_data_sample_rev1 curve_data[8];
u8 dup_bpsk;
u8 dup_qpsk;
u8 dup_16qam;
u8 dup_64qam;
__le16 rssical_mul;
__le16 rssical_add;
} __attribute__ ((packed));

struct p54_tx_control_led {
Expand Down

0 comments on commit 154e3af

Please sign in to comment.