p54: longbow frontend support
authorChristian Lamparter <chunkeey@web.de>
Sun, 11 Jan 2009 00:14:18 +0000 (01:14 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 29 Jan 2009 21:00:24 +0000 (16:00 -0500)
This patch adds support for longbow RF chip.

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 ec0d109..9fc0c9e 100644 (file)
@@ -651,7 +651,7 @@ static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
        }
 
        priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK;
-       if (priv->rxhw == 4)
+       if (priv->rxhw == PDR_SYNTH_FRONTEND_XBOW)
                p54_init_xbow_synth(dev);
        if (!(synth & PDR_SYNTH_24_GHZ_DISABLED))
                dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &band_2GHz;
@@ -704,7 +704,14 @@ static int p54_rssi_to_dbm(struct ieee80211_hw *dev, int rssi)
        struct p54_common *priv = dev->priv;
        int band = dev->conf.channel->band;
 
-       return ((rssi * priv->rssical_db[band].mul) / 64 +
+       if (priv->rxhw != PDR_SYNTH_FRONTEND_LONGBOW)
+               return ((rssi * priv->rssical_db[band].mul) / 64 +
+                        priv->rssical_db[band].add) / 4;
+       else
+               /*
+                * TODO: find the correct formula
+                */
+               return ((rssi * priv->rssical_db[band].mul) / 64 +
                         priv->rssical_db[band].add) / 4;
 }
 
@@ -1612,8 +1619,13 @@ static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
        memset(txhdr->durations, 0, sizeof(txhdr->durations));
        txhdr->tx_antenna = ((info->antenna_sel_tx == 0) ?
                2 : info->antenna_sel_tx - 1) & priv->tx_diversity_mask;
-       txhdr->output_power = priv->output_power;
-       txhdr->cts_rate = cts_rate;
+       if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {
+               txhdr->longbow.cts_rate = cts_rate;
+               txhdr->longbow.output_power = cpu_to_le16(priv->output_power);
+       } else {
+               txhdr->normal.output_power = priv->output_power;
+               txhdr->normal.cts_rate = cts_rate;
+       }
        if (padding)
                txhdr->align[0] = padding;
 
@@ -1715,47 +1727,77 @@ static int p54_scan(struct ieee80211_hw *dev, u16 mode, u16 dwell)
 {
        struct p54_common *priv = dev->priv;
        struct sk_buff *skb;
-       struct p54_scan *chan;
+       struct p54_hdr *hdr;
+       struct p54_scan_head *head;
+       struct p54_iq_autocal_entry *iq_autocal;
+       union p54_scan_body_union *body;
+       struct p54_scan_tail_rate *rate;
+       struct pda_rssi_cal_entry *rssi;
        unsigned int i;
        void *entry;
        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),
+       skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*head) +
+                           2 + sizeof(*iq_autocal) + sizeof(*body) +
+                           sizeof(*rate) + 2 * sizeof(*rssi),
                            P54_CONTROL_TYPE_SCAN, GFP_ATOMIC);
        if (!skb)
                return -ENOMEM;
 
-       chan = (struct p54_scan *) skb_put(skb, sizeof(*chan));
-       memset(chan->scan_params, 0, sizeof(chan->scan_params));
-       chan->mode = cpu_to_le16(mode);
-       chan->dwell = cpu_to_le16(dwell);
+       head = (struct p54_scan_head *) skb_put(skb, sizeof(*head));
+       memset(head->scan_params, 0, sizeof(head->scan_params));
+       head->mode = cpu_to_le16(mode);
+       head->dwell = cpu_to_le16(dwell);
+       head->freq = freq;
 
+       if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {
+               __le16 *pa_power_points = (__le16 *) skb_put(skb, 2);
+               *pa_power_points = cpu_to_le16(0x0c);
+       }
+
+       iq_autocal = (void *) skb_put(skb, sizeof(*iq_autocal));
        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));
+               memcpy(iq_autocal, &priv->iq_autocal[i].params,
+                      sizeof(struct p54_iq_autocal_entry));
                break;
        }
        if (i == priv->iq_autocal_len)
                goto err;
 
+       if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW)
+               body = (void *) skb_put(skb, sizeof(body->longbow));
+       else
+               body = (void *) skb_put(skb, sizeof(body->normal));
+
        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);
+                                    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 = 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;
+               if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {
+                       memcpy(&body->longbow.power_limits,
+                              (void *) entry_freq + sizeof(__le16),
+                              priv->output_limit->entry_size);
+               } else {
+                       struct pda_channel_output_limit *limits =
+                              (void *) entry_freq;
+
+                       body->normal.val_barker = 0x38;
+                       body->normal.val_bpsk = body->normal.dup_bpsk =
+                               limits->val_bpsk;
+                       body->normal.val_qpsk = body->normal.dup_qpsk =
+                               limits->val_qpsk;
+                       body->normal.val_16qam = body->normal.dup_16qam =
+                               limits->val_16qam;
+                       body->normal.val_64qam = body->normal.dup_64qam =
+                               limits->val_64qam;
+               }
                break;
        }
        if (i == priv->output_limit->entries)
@@ -1763,34 +1805,59 @@ static int p54_scan(struct ieee80211_hw *dev, u16 mode, u16 dwell)
 
        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 += 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_t(u8, 8, curve_data->points_per_channel));
+               if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {
+                       memcpy(&body->longbow.curve_data,
+                               (void *) entry + sizeof(__le16),
+                               priv->curve_data->entry_size);
+               } else {
+                       struct p54_scan_body *chan = &body->normal;
+                       struct pda_pa_curve_data *curve_data =
+                               (void *) priv->curve_data->data;
+
+                       entry += sizeof(__le16);
+                       chan->pa_points_per_curve = 8;
+                       memset(chan->curve_data, 0, sizeof(*chan->curve_data));
+                       memcpy(chan->curve_data, entry,
+                              sizeof(struct p54_pa_curve_data_sample) *
+                              min((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);
-               chan->v1_rssi.add = cpu_to_le16(priv->rssical_db[band].add);
-       } else {
-               chan->v2.rssi.mul = cpu_to_le16(priv->rssical_db[band].mul);
-               chan->v2.rssi.add = cpu_to_le16(priv->rssical_db[band].add);
-               chan->v2.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);
-               memset(chan->v2.rts_rates, 0, 8);
+       if ((priv->fw_var >= 0x500) && (priv->fw_var < 0x509)) {
+               rate = (void *) skb_put(skb, sizeof(*rate));
+               rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);
+               for (i = 0; i < sizeof(rate->rts_rates); i++)
+                       rate->rts_rates[i] = i;
        }
+
+       rssi = (struct pda_rssi_cal_entry *) skb_put(skb, sizeof(*rssi));
+       rssi->mul = cpu_to_le16(priv->rssical_db[band].mul);
+       rssi->add = cpu_to_le16(priv->rssical_db[band].add);
+       if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) {
+               /* Longbow frontend needs ever more */
+               rssi = (void *) skb_put(skb, sizeof(*rssi));
+               rssi->mul = cpu_to_le16(priv->rssical_db[band].longbow_unkn);
+               rssi->add = cpu_to_le16(priv->rssical_db[band].longbow_unk2);
+       }
+
+       if (priv->fw_var >= 0x509) {
+               rate = (void *) skb_put(skb, sizeof(*rate));
+               rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);
+               for (i = 0; i < sizeof(rate->rts_rates); i++)
+                       rate->rts_rates[i] = i;
+       }
+
+       hdr = (struct p54_hdr *) skb->data;
+       hdr->len = cpu_to_le16(skb->len - sizeof(*hdr));
+
        priv->tx(dev, skb);
        return 0;
 
index d9aa255..def23b1 100644 (file)
@@ -26,6 +26,11 @@ struct bootrec {
 } __attribute__((packed));
 
 #define PDR_SYNTH_FRONTEND_MASK                0x0007
+#define PDR_SYNTH_FRONTEND_DUETTE3     0x0001
+#define PDR_SYNTH_FRONTEND_DUETTE2     0x0002
+#define PDR_SYNTH_FRONTEND_FRISBEE     0x0003
+#define PDR_SYNTH_FRONTEND_XBOW                0x0004
+#define PDR_SYNTH_FRONTEND_LONGBOW     0x0005
 #define PDR_SYNTH_IQ_CAL_MASK          0x0018
 #define PDR_SYNTH_IQ_CAL_PA_DETECTOR   0x0000
 #define PDR_SYNTH_IQ_CAL_DISABLED      0x0008
@@ -125,9 +130,13 @@ struct eeprom_pda_wrap {
        u8 data[0];
 } __attribute__ ((packed));
 
+struct p54_iq_autocal_entry {
+       __le16 iq_param[4];
+} __attribute__ ((packed));
+
 struct pda_iq_autocal_entry {
         __le16 freq;
-        __le16 iq_param[4];
+       struct p54_iq_autocal_entry params;
 } __attribute__ ((packed));
 
 struct pda_channel_output_limit {
@@ -186,6 +195,21 @@ struct pda_country {
        u8 flags;
 } __attribute__ ((packed));
 
+/*
+ * Warning: Longbow's structures are bogus.
+ */
+struct p54_channel_output_limit_longbow {
+       __le16 rf_power_points[12];
+} __attribute__ ((packed));
+
+struct p54_pa_curve_data_sample_longbow {
+       __le16 rf_power;
+       __le16 pa_detector;
+       struct {
+               __le16 data[4];
+       } points[3] __attribute__ ((packed));
+} __attribute__ ((packed));
+
 struct pda_custom_wrapper {
        __le16 entries;
        __le16 entry_size;
@@ -381,9 +405,18 @@ struct p54_tx_data {
        u8 backlog;
        __le16 durations[4];
        u8 tx_antenna;
-       u8 output_power;
-       u8 cts_rate;
-       u8 unalloc2[3];
+       union {
+               struct {
+                       u8 cts_rate;
+                       __le16 output_power;
+               } __attribute__((packed)) longbow;
+               struct {
+                       u8 output_power;
+                       u8 cts_rate;
+                       u8 unalloc;
+               } __attribute__ ((packed)) normal;
+       } __attribute__ ((packed));
+       u8 unalloc2[2];
        u8 align[0];
 } __attribute__ ((packed));
 
@@ -444,11 +477,14 @@ struct p54_setup_mac {
 #define P54_SCAN_ACTIVE BIT(2)
 #define P54_SCAN_FILTER BIT(3)
 
-struct p54_scan {
+struct p54_scan_head {
        __le16 mode;
        __le16 dwell;
        u8 scan_params[20];
-       struct pda_iq_autocal_entry iq_autocal;
+       __le16 freq;
+} __attribute__ ((packed));
+
+struct p54_scan_body {
        u8 pa_points_per_curve;
        u8 val_barker;
        u8 val_bpsk;
@@ -460,19 +496,23 @@ struct p54_scan {
        u8 dup_qpsk;
        u8 dup_16qam;
        u8 dup_64qam;
-       union {
-               struct pda_rssi_cal_entry v1_rssi;
+} __attribute__ ((packed));
 
-               struct {
-                       __le32 basic_rate_mask;
-                       u8 rts_rates[8];
-                       struct pda_rssi_cal_entry rssi;
-               } v2 __attribute__ ((packed));
-       } __attribute__ ((packed));
+struct p54_scan_body_longbow {
+       struct p54_channel_output_limit_longbow power_limits;
+       struct p54_pa_curve_data_sample_longbow curve_data[8];
+       __le16 unkn[6];         /* maybe more power_limits or rate_mask */
 } __attribute__ ((packed));
 
-#define P54_SCAN_V1_LEN 0x70
-#define P54_SCAN_V2_LEN 0x7c
+union p54_scan_body_union {
+       struct p54_scan_body normal;
+       struct p54_scan_body_longbow longbow;
+} __attribute__ ((packed));
+
+struct p54_scan_tail_rate {
+       __le32 basic_rate_mask;
+       u8 rts_rates[8];
+} __attribute__ ((packed));
 
 struct p54_led {
        __le16 mode;