p54: fix rssi auto calibration
authorChristian Lamparter <chunkeey@web.de>
Sat, 23 Aug 2008 20:15:25 +0000 (22:15 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 29 Aug 2008 20:24:08 +0000 (16:24 -0400)
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>
drivers/net/wireless/p54/p54common.c
drivers/net/wireless/p54/p54common.h

index 29be3dc8ee093fa4cd7dbcf89c4dcc11184058df..4a6b5363ea4b78d52c8d3826d2d39e183abd2b93 100644 (file)
@@ -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;
@@ -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;
@@ -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) {
@@ -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;
@@ -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)
@@ -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:
index 9bc2a1cf4b57672b05f32c04914552a83535f4fb..a79c1a146917c9669fc86be2a3ded825034618e6 100644 (file)
@@ -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));
 
@@ -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;
@@ -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 {