mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 17:30:02 +00:00
p54: fix rssi auto calibration
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>
This commit is contained in:
parent
a3ec233c90
commit
154e3af17f
2 changed files with 111 additions and 52 deletions
|
@ -155,14 +155,14 @@ void p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(p54_parse_firmware);
|
EXPORT_SYMBOL_GPL(p54_parse_firmware);
|
||||||
|
|
||||||
static int p54_convert_rev0_to_rev1(struct ieee80211_hw *dev,
|
static int p54_convert_rev0(struct ieee80211_hw *dev,
|
||||||
struct pda_pa_curve_data *curve_data)
|
struct pda_pa_curve_data *curve_data)
|
||||||
{
|
{
|
||||||
struct p54_common *priv = dev->priv;
|
struct p54_common *priv = dev->priv;
|
||||||
struct pda_pa_curve_data_sample_rev1 *rev1;
|
struct p54_pa_curve_data_sample *dst;
|
||||||
struct pda_pa_curve_data_sample_rev0 *rev0;
|
struct pda_pa_curve_data_sample_rev0 *src;
|
||||||
size_t cd_len = sizeof(*curve_data) +
|
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;
|
curve_data->channels;
|
||||||
unsigned int i, j;
|
unsigned int i, j;
|
||||||
void *source, *target;
|
void *source, *target;
|
||||||
|
@ -180,27 +180,63 @@ static int p54_convert_rev0_to_rev1(struct ieee80211_hw *dev,
|
||||||
*((__le16 *)target) = *freq;
|
*((__le16 *)target) = *freq;
|
||||||
target += sizeof(__le16);
|
target += sizeof(__le16);
|
||||||
for (j = 0; j < curve_data->points_per_channel; j++) {
|
for (j = 0; j < curve_data->points_per_channel; j++) {
|
||||||
rev1 = target;
|
dst = target;
|
||||||
rev0 = source;
|
src = source;
|
||||||
|
|
||||||
rev1->rf_power = rev0->rf_power;
|
dst->rf_power = src->rf_power;
|
||||||
rev1->pa_detector = rev0->pa_detector;
|
dst->pa_detector = src->pa_detector;
|
||||||
rev1->data_64qam = rev0->pcv;
|
dst->data_64qam = src->pcv;
|
||||||
/* "invent" the points for the other modulations */
|
/* "invent" the points for the other modulations */
|
||||||
#define SUB(x,y) (u8)((x) - (y)) > (x) ? 0 : (x) - (y)
|
#define SUB(x,y) (u8)((x) - (y)) > (x) ? 0 : (x) - (y)
|
||||||
rev1->data_16qam = SUB(rev0->pcv, 12);
|
dst->data_16qam = SUB(src->pcv, 12);
|
||||||
rev1->data_qpsk = SUB(rev1->data_16qam, 12);
|
dst->data_qpsk = SUB(dst->data_16qam, 12);
|
||||||
rev1->data_bpsk = SUB(rev1->data_qpsk, 12);
|
dst->data_bpsk = SUB(dst->data_qpsk, 12);
|
||||||
rev1->data_barker= SUB(rev1->data_bpsk, 14);
|
dst->data_barker = SUB(dst->data_bpsk, 14);
|
||||||
#undef SUB
|
#undef SUB
|
||||||
target += sizeof(*rev1);
|
target += sizeof(*dst);
|
||||||
source += sizeof(*rev0);
|
source += sizeof(*src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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)
|
int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
|
||||||
{
|
{
|
||||||
struct p54_common *priv = dev->priv;
|
struct p54_common *priv = dev->priv;
|
||||||
|
@ -250,27 +286,32 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
|
||||||
entry->data[1]*sizeof(*priv->output_limit));
|
entry->data[1]*sizeof(*priv->output_limit));
|
||||||
priv->output_limit_len = entry->data[1];
|
priv->output_limit_len = entry->data[1];
|
||||||
break;
|
break;
|
||||||
case PDR_PRISM_PA_CAL_CURVE_DATA:
|
case PDR_PRISM_PA_CAL_CURVE_DATA: {
|
||||||
if (data_len < sizeof(struct pda_pa_curve_data)) {
|
struct pda_pa_curve_data *curve_data =
|
||||||
|
(struct pda_pa_curve_data *)entry->data;
|
||||||
|
if (data_len < sizeof(*curve_data)) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((struct pda_pa_curve_data *)entry->data)->cal_method_rev) {
|
switch (curve_data->cal_method_rev) {
|
||||||
priv->curve_data = kmalloc(data_len, GFP_KERNEL);
|
case 0:
|
||||||
if (!priv->curve_data) {
|
err = p54_convert_rev0(dev, curve_data);
|
||||||
err = -ENOMEM;
|
break;
|
||||||
goto err;
|
case 1:
|
||||||
}
|
err = p54_convert_rev1(dev, curve_data);
|
||||||
|
break;
|
||||||
memcpy(priv->curve_data, entry->data, data_len);
|
default:
|
||||||
} else {
|
printk(KERN_ERR "p54: unknown curve data "
|
||||||
err = p54_convert_rev0_to_rev1(dev, (struct pda_pa_curve_data *)entry->data);
|
"revision %d\n",
|
||||||
if (err)
|
curve_data->cal_method_rev);
|
||||||
goto err;
|
err = -ENODEV;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
if (err)
|
||||||
|
goto err;
|
||||||
|
|
||||||
break;
|
}
|
||||||
case PDR_PRISM_ZIF_TX_IQ_CALIBRATION:
|
case PDR_PRISM_ZIF_TX_IQ_CALIBRATION:
|
||||||
priv->iq_autocal = kmalloc(data_len, GFP_KERNEL);
|
priv->iq_autocal = kmalloc(data_len, GFP_KERNEL);
|
||||||
if (!priv->iq_autocal) {
|
if (!priv->iq_autocal) {
|
||||||
|
@ -672,12 +713,9 @@ static int p54_set_freq(struct ieee80211_hw *dev, __le16 freq)
|
||||||
struct p54_control_hdr *hdr;
|
struct p54_control_hdr *hdr;
|
||||||
struct p54_tx_control_channel *chan;
|
struct p54_tx_control_channel *chan;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
size_t payload_len = sizeof(*chan) + sizeof(u32)*2 +
|
|
||||||
sizeof(*chan->curve_data) *
|
|
||||||
priv->curve_data->points_per_channel;
|
|
||||||
void *entry;
|
void *entry;
|
||||||
|
|
||||||
hdr = kzalloc(sizeof(*hdr) + payload_len +
|
hdr = kzalloc(sizeof(*hdr) + sizeof(*chan) +
|
||||||
priv->tx_hdr_len, GFP_KERNEL);
|
priv->tx_hdr_len, GFP_KERNEL);
|
||||||
if (!hdr)
|
if (!hdr)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -689,10 +727,10 @@ static int p54_set_freq(struct ieee80211_hw *dev, __le16 freq)
|
||||||
hdr->magic1 = cpu_to_le16(0x8001);
|
hdr->magic1 = cpu_to_le16(0x8001);
|
||||||
hdr->len = cpu_to_le16(sizeof(*chan));
|
hdr->len = cpu_to_le16(sizeof(*chan));
|
||||||
hdr->type = cpu_to_le16(P54_CONTROL_TYPE_CHANNEL_CHANGE);
|
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->flags = cpu_to_le16(0x1);
|
||||||
chan->magic2 = cpu_to_le16(0x0);
|
chan->dwell = cpu_to_le16(0x0);
|
||||||
|
|
||||||
for (i = 0; i < priv->iq_autocal_len; i++) {
|
for (i = 0; i < priv->iq_autocal_len; i++) {
|
||||||
if (priv->iq_autocal[i].freq != freq)
|
if (priv->iq_autocal[i].freq != freq)
|
||||||
|
@ -710,35 +748,41 @@ static int p54_set_freq(struct ieee80211_hw *dev, __le16 freq)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
chan->val_barker = 0x38;
|
chan->val_barker = 0x38;
|
||||||
chan->val_bpsk = priv->output_limit[i].val_bpsk;
|
chan->val_bpsk = chan->dup_bpsk =
|
||||||
chan->val_qpsk = priv->output_limit[i].val_qpsk;
|
priv->output_limit[i].val_bpsk;
|
||||||
chan->val_16qam = priv->output_limit[i].val_16qam;
|
chan->val_qpsk = chan->dup_qpsk =
|
||||||
chan->val_64qam = priv->output_limit[i].val_64qam;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
if (i == priv->output_limit_len)
|
if (i == priv->output_limit_len)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
chan->pa_points_per_curve = priv->curve_data->points_per_channel;
|
|
||||||
|
|
||||||
entry = priv->curve_data->data;
|
entry = priv->curve_data->data;
|
||||||
for (i = 0; i < priv->curve_data->channels; i++) {
|
for (i = 0; i < priv->curve_data->channels; i++) {
|
||||||
if (*((__le16 *)entry) != freq) {
|
if (*((__le16 *)entry) != freq) {
|
||||||
entry += sizeof(__le16);
|
entry += sizeof(__le16);
|
||||||
entry += sizeof(struct pda_pa_curve_data_sample_rev1) *
|
entry += sizeof(struct p54_pa_curve_data_sample) *
|
||||||
chan->pa_points_per_curve;
|
priv->curve_data->points_per_channel;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
entry += sizeof(__le16);
|
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) *
|
memcpy(chan->curve_data, entry, sizeof(*chan->curve_data) *
|
||||||
chan->pa_points_per_curve);
|
chan->pa_points_per_curve);
|
||||||
break;
|
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;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
|
|
@ -89,6 +89,16 @@ struct pda_pa_curve_data_sample_rev1 {
|
||||||
u8 data_qpsk;
|
u8 data_qpsk;
|
||||||
u8 data_16qam;
|
u8 data_16qam;
|
||||||
u8 data_64qam;
|
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;
|
u8 padding;
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
@ -212,8 +222,8 @@ struct p54_tx_control_filter {
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
struct p54_tx_control_channel {
|
struct p54_tx_control_channel {
|
||||||
__le16 magic1;
|
__le16 flags;
|
||||||
__le16 magic2;
|
__le16 dwell;
|
||||||
u8 padding1[20];
|
u8 padding1[20];
|
||||||
struct pda_iq_autocal_entry iq_autocal;
|
struct pda_iq_autocal_entry iq_autocal;
|
||||||
u8 pa_points_per_curve;
|
u8 pa_points_per_curve;
|
||||||
|
@ -222,8 +232,13 @@ struct p54_tx_control_channel {
|
||||||
u8 val_qpsk;
|
u8 val_qpsk;
|
||||||
u8 val_16qam;
|
u8 val_16qam;
|
||||||
u8 val_64qam;
|
u8 val_64qam;
|
||||||
struct pda_pa_curve_data_sample_rev1 curve_data[0];
|
struct pda_pa_curve_data_sample_rev1 curve_data[8];
|
||||||
/* additional padding/data after curve_data */
|
u8 dup_bpsk;
|
||||||
|
u8 dup_qpsk;
|
||||||
|
u8 dup_16qam;
|
||||||
|
u8 dup_64qam;
|
||||||
|
__le16 rssical_mul;
|
||||||
|
__le16 rssical_add;
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
struct p54_tx_control_led {
|
struct p54_tx_control_led {
|
||||||
|
|
Loading…
Reference in a new issue