p54: prepare the eeprom parser routines for longbow
authorChristian Lamparter <chunkeey@web.de>
Sun, 11 Jan 2009 00:10:33 +0000 (01:10 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 29 Jan 2009 21:00:22 +0000 (16:00 -0500)
This patch adds support to upload pre-calculated calibration data to the firmware.

Signed-off-by: Christian Lamparter <chunkeey@web.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/p54/p54.h
drivers/net/wireless/p54/p54common.c
drivers/net/wireless/p54/p54common.h

index a06c2a6..ac11efd 100644 (file)
@@ -87,6 +87,14 @@ struct p54_rssi_linear_approximation {
        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
 
 #define ISL38XX_DEV_FIRMWARE_ADDR 0x20000
@@ -115,9 +123,8 @@ struct p54_common {
        u8 tx_diversity_mask;
        struct pda_iq_autocal_entry *iq_autocal;
        unsigned int iq_autocal_len;
-       struct pda_channel_output_limit *output_limit;
-       unsigned int output_limit_len;
-       struct pda_pa_curve_data *curve_data;
+       struct p54_cal_database *output_limit;
+       struct p54_cal_database *curve_data;
        struct p54_rssi_linear_approximation rssical_db[IEEE80211_NUM_BANDS];
        unsigned int filter_flags;
        bool use_short_slot;
index 85aff16..ec0d109 100644 (file)
@@ -272,13 +272,19 @@ static int p54_convert_rev0(struct ieee80211_hw *dev,
        unsigned int i, j;
        void *source, *target;
 
-       priv->curve_data = kmalloc(cd_len, GFP_KERNEL);
+       priv->curve_data = kmalloc(sizeof(*priv->curve_data) + cd_len,
+                                  GFP_KERNEL);
        if (!priv->curve_data)
                return -ENOMEM;
 
-       memcpy(priv->curve_data, curve_data, sizeof(*curve_data));
+       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 = priv->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);
@@ -318,13 +324,19 @@ static int p54_convert_rev1(struct ieee80211_hw *dev,
        unsigned int i, j;
        void *source, *target;
 
-       priv->curve_data = kmalloc(cd_len, GFP_KERNEL);
+       priv->curve_data = kzalloc(cd_len + sizeof(*priv->curve_data),
+                                  GFP_KERNEL);
        if (!priv->curve_data)
                return -ENOMEM;
 
-       memcpy(priv->curve_data, curve_data, sizeof(*curve_data));
+       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 = priv->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);
@@ -406,6 +418,71 @@ static void p54_parse_default_country(struct ieee80211_hw *dev,
        }
 }
 
+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) {
+               printk(KERN_ERR "%s: unknown output power db revision:%x\n",
+                      wiphy_name(dev->wiphy), 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;
+}
+
 static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
 {
        struct p54_common *priv = dev->priv;
@@ -431,30 +508,17 @@ static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
 
                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 (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;
+                       if (priv->output_limit)
+                               break;
+                       err = p54_convert_output_limits(dev, entry->data,
+                                                       data_len);
+                       if (err)
                                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: {
                        struct pda_pa_curve_data *curve_data =
@@ -506,6 +570,8 @@ static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
                        }
                        break;
                case PDR_HARDWARE_PLATFORM_COMPONENT_ID:
+                       if (data_len < 2)
+                               break;
                        priv->version = *(u8 *)(entry->data + 1);
                        break;
                case PDR_RSSI_LINEAR_APPROXIMATION:
@@ -514,6 +580,34 @@ static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
                        p54_parse_rssical(dev, entry->data, data_len,
                                          le16_to_cpu(entry->code));
                        break;
+               case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM: {
+                       __le16 *src = (void *) entry->data;
+                       s16 *dst = (void *) &priv->rssical_db;
+                       int i;
+
+                       if (data_len != sizeof(priv->rssical_db)) {
+                               err = -EINVAL;
+                               goto err;
+                       }
+                       for (i = 0; i < sizeof(priv->rssical_db) /
+                                       sizeof(*src); 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:
                        /* make it overrun */
                        entry_len = len;
@@ -1624,8 +1718,8 @@ static int p54_scan(struct ieee80211_hw *dev, u16 mode, u16 dwell)
        struct p54_scan *chan;
        unsigned int i;
        void *entry;
-       __le16 freq = cpu_to_le16(dev->conf.channel->center_freq);
        int band = dev->conf.channel->band;
+       __le16 freq = cpu_to_le16(dev->conf.channel->center_freq);
 
        skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*chan),
                            P54_CONTROL_TYPE_SCAN, GFP_ATOMIC);
@@ -1633,7 +1727,7 @@ static int p54_scan(struct ieee80211_hw *dev, u16 mode, u16 dwell)
                return -ENOMEM;
 
        chan = (struct p54_scan *) skb_put(skb, sizeof(*chan));
-       memset(chan->padding1, 0, sizeof(chan->padding1));
+       memset(chan->scan_params, 0, sizeof(chan->scan_params));
        chan->mode = cpu_to_le16(mode);
        chan->dwell = cpu_to_le16(dwell);
 
@@ -1648,41 +1742,45 @@ static int p54_scan(struct ieee80211_hw *dev, u16 mode, u16 dwell)
        if (i == priv->iq_autocal_len)
                goto err;
 
-       for (i = 0; i < priv->output_limit_len; i++) {
-               if (priv->output_limit[i].freq != freq)
+       for (i = 0; i < priv->output_limit->entries; i++) {
+               struct pda_channel_output_limit *limits;
+               __le16 *entry_freq = (void *) (priv->output_limit->data +
+                       priv->output_limit->entry_size * i);
+
+               if (*entry_freq != freq)
                        continue;
 
+               limits = (void *) entry_freq;
                chan->val_barker = 0x38;
-               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;
+               chan->val_bpsk = chan->dup_bpsk = limits->val_bpsk;
+               chan->val_qpsk = chan->dup_qpsk = limits->val_qpsk;
+               chan->val_16qam = chan->dup_16qam = limits->val_16qam;
+               chan->val_64qam = chan->dup_64qam = limits->val_64qam;
                break;
        }
-       if (i == priv->output_limit_len)
+       if (i == priv->output_limit->entries)
                goto err;
 
-       entry = priv->curve_data->data;
-       for (i = 0; i < priv->curve_data->channels; i++) {
+       entry = (void *)(priv->curve_data->data + priv->curve_data->offset);
+       for (i = 0; i < priv->curve_data->entries; i++) {
+               struct pda_pa_curve_data *curve_data;
                if (*((__le16 *)entry) != freq) {
-                       entry += sizeof(__le16);
-                       entry += sizeof(struct p54_pa_curve_data_sample) *
-                                priv->curve_data->points_per_channel;
+                       entry += priv->curve_data->entry_size;
                        continue;
                }
 
                entry += sizeof(__le16);
                chan->pa_points_per_curve = 8;
                memset(chan->curve_data, 0, sizeof(*chan->curve_data));
+               curve_data = (void *) priv->curve_data->data;
+
                memcpy(chan->curve_data, entry,
                       sizeof(struct p54_pa_curve_data_sample) *
-                      min((u8)8, priv->curve_data->points_per_channel));
+                      min_t(u8, 8, curve_data->points_per_channel));
                break;
        }
+       if (i == priv->curve_data->entries)
+               goto err;
 
        if (priv->fw_var < 0x500) {
                chan->v1_rssi.mul = cpu_to_le16(priv->rssical_db[band].mul);
index bcfb75a..d9aa255 100644 (file)
@@ -186,6 +186,14 @@ struct pda_country {
        u8 flags;
 } __attribute__ ((packed));
 
+struct pda_custom_wrapper {
+       __le16 entries;
+       __le16 entry_size;
+       __le16 offset;
+       __le16 len;
+       u8 data[0];
+} __attribute__ ((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
@@ -231,8 +239,13 @@ struct pda_country {
 /* reserved range (0x2000 - 0x7fff) */
 
 /* customer range (0x8000 - 0xffff) */
-#define PDR_BASEBAND_REGISTERS                 0x8000
-#define PDR_PER_CHANNEL_BASEBAND_REGISTERS     0x8001
+#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_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM    0xBEEF
+#define PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM             0xB05D
 
 /* PDR definitions for default country & country list */
 #define PDR_COUNTRY_CERT_CODE          0x80
@@ -434,7 +447,7 @@ struct p54_setup_mac {
 struct p54_scan {
        __le16 mode;
        __le16 dwell;
-       u8 padding1[20];
+       u8 scan_params[20];
        struct pda_iq_autocal_entry iq_autocal;
        u8 pa_points_per_curve;
        u8 val_barker;