|
@@ -19,6 +19,7 @@
|
|
|
#include <linux/init.h>
|
|
|
#include <linux/firmware.h>
|
|
|
#include <linux/etherdevice.h>
|
|
|
+#include <linux/sort.h>
|
|
|
|
|
|
#include <net/mac80211.h>
|
|
|
|
|
@@ -41,30 +42,6 @@ static struct ieee80211_rate p54_bgrates[] = {
|
|
|
{ .bitrate = 540, .hw_value = 11, },
|
|
|
};
|
|
|
|
|
|
-static struct ieee80211_channel p54_bgchannels[] = {
|
|
|
- { .center_freq = 2412, .hw_value = 1, },
|
|
|
- { .center_freq = 2417, .hw_value = 2, },
|
|
|
- { .center_freq = 2422, .hw_value = 3, },
|
|
|
- { .center_freq = 2427, .hw_value = 4, },
|
|
|
- { .center_freq = 2432, .hw_value = 5, },
|
|
|
- { .center_freq = 2437, .hw_value = 6, },
|
|
|
- { .center_freq = 2442, .hw_value = 7, },
|
|
|
- { .center_freq = 2447, .hw_value = 8, },
|
|
|
- { .center_freq = 2452, .hw_value = 9, },
|
|
|
- { .center_freq = 2457, .hw_value = 10, },
|
|
|
- { .center_freq = 2462, .hw_value = 11, },
|
|
|
- { .center_freq = 2467, .hw_value = 12, },
|
|
|
- { .center_freq = 2472, .hw_value = 13, },
|
|
|
- { .center_freq = 2484, .hw_value = 14, },
|
|
|
-};
|
|
|
-
|
|
|
-static struct ieee80211_supported_band band_2GHz = {
|
|
|
- .channels = p54_bgchannels,
|
|
|
- .n_channels = ARRAY_SIZE(p54_bgchannels),
|
|
|
- .bitrates = p54_bgrates,
|
|
|
- .n_bitrates = ARRAY_SIZE(p54_bgrates),
|
|
|
-};
|
|
|
-
|
|
|
static struct ieee80211_rate p54_arates[] = {
|
|
|
{ .bitrate = 60, .hw_value = 4, },
|
|
|
{ .bitrate = 90, .hw_value = 5, },
|
|
@@ -76,51 +53,257 @@ static struct ieee80211_rate p54_arates[] = {
|
|
|
{ .bitrate = 540, .hw_value = 11, },
|
|
|
};
|
|
|
|
|
|
-static struct ieee80211_channel p54_achannels[] = {
|
|
|
- { .center_freq = 4920 },
|
|
|
- { .center_freq = 4940 },
|
|
|
- { .center_freq = 4960 },
|
|
|
- { .center_freq = 4980 },
|
|
|
- { .center_freq = 5040 },
|
|
|
- { .center_freq = 5060 },
|
|
|
- { .center_freq = 5080 },
|
|
|
- { .center_freq = 5170 },
|
|
|
- { .center_freq = 5180 },
|
|
|
- { .center_freq = 5190 },
|
|
|
- { .center_freq = 5200 },
|
|
|
- { .center_freq = 5210 },
|
|
|
- { .center_freq = 5220 },
|
|
|
- { .center_freq = 5230 },
|
|
|
- { .center_freq = 5240 },
|
|
|
- { .center_freq = 5260 },
|
|
|
- { .center_freq = 5280 },
|
|
|
- { .center_freq = 5300 },
|
|
|
- { .center_freq = 5320 },
|
|
|
- { .center_freq = 5500 },
|
|
|
- { .center_freq = 5520 },
|
|
|
- { .center_freq = 5540 },
|
|
|
- { .center_freq = 5560 },
|
|
|
- { .center_freq = 5580 },
|
|
|
- { .center_freq = 5600 },
|
|
|
- { .center_freq = 5620 },
|
|
|
- { .center_freq = 5640 },
|
|
|
- { .center_freq = 5660 },
|
|
|
- { .center_freq = 5680 },
|
|
|
- { .center_freq = 5700 },
|
|
|
- { .center_freq = 5745 },
|
|
|
- { .center_freq = 5765 },
|
|
|
- { .center_freq = 5785 },
|
|
|
- { .center_freq = 5805 },
|
|
|
- { .center_freq = 5825 },
|
|
|
+#define CHAN_HAS_CAL BIT(0)
|
|
|
+#define CHAN_HAS_LIMIT BIT(1)
|
|
|
+#define CHAN_HAS_CURVE BIT(2)
|
|
|
+#define CHAN_HAS_ALL (CHAN_HAS_CAL | CHAN_HAS_LIMIT | CHAN_HAS_CURVE)
|
|
|
+
|
|
|
+struct p54_channel_entry {
|
|
|
+ u16 freq;
|
|
|
+ u16 data;
|
|
|
+ int index;
|
|
|
+ enum ieee80211_band band;
|
|
|
};
|
|
|
|
|
|
-static struct ieee80211_supported_band band_5GHz = {
|
|
|
- .channels = p54_achannels,
|
|
|
- .n_channels = ARRAY_SIZE(p54_achannels),
|
|
|
- .bitrates = p54_arates,
|
|
|
- .n_bitrates = ARRAY_SIZE(p54_arates),
|
|
|
+struct p54_channel_list {
|
|
|
+ struct p54_channel_entry *channels;
|
|
|
+ size_t entries;
|
|
|
+ size_t max_entries;
|
|
|
+ size_t band_channel_num[IEEE80211_NUM_BANDS];
|
|
|
};
|
|
|
|
|
|
+static int p54_get_band_from_freq(u16 freq)
|
|
|
+{
|
|
|
+ /* FIXME: sync these values with the 802.11 spec */
|
|
|
+
|
|
|
+ if ((freq >= 2412) && (freq <= 2484))
|
|
|
+ return IEEE80211_BAND_2GHZ;
|
|
|
+
|
|
|
+ if ((freq >= 4920) && (freq <= 5825))
|
|
|
+ return IEEE80211_BAND_5GHZ;
|
|
|
+
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+static int p54_compare_channels(const void *_a,
|
|
|
+ const void *_b)
|
|
|
+{
|
|
|
+ const struct p54_channel_entry *a = _a;
|
|
|
+ const struct p54_channel_entry *b = _b;
|
|
|
+
|
|
|
+ return a->index - b->index;
|
|
|
+}
|
|
|
+
|
|
|
+static int p54_fill_band_bitrates(struct ieee80211_hw *dev,
|
|
|
+ struct ieee80211_supported_band *band_entry,
|
|
|
+ enum ieee80211_band band)
|
|
|
+{
|
|
|
+ /* TODO: generate rate array dynamically */
|
|
|
+
|
|
|
+ switch (band) {
|
|
|
+ case IEEE80211_BAND_2GHZ:
|
|
|
+ band_entry->bitrates = p54_bgrates;
|
|
|
+ band_entry->n_bitrates = ARRAY_SIZE(p54_bgrates);
|
|
|
+ break;
|
|
|
+ case IEEE80211_BAND_5GHZ:
|
|
|
+ band_entry->bitrates = p54_arates;
|
|
|
+ band_entry->n_bitrates = ARRAY_SIZE(p54_arates);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int p54_generate_band(struct ieee80211_hw *dev,
|
|
|
+ struct p54_channel_list *list,
|
|
|
+ enum ieee80211_band band)
|
|
|
+{
|
|
|
+ struct p54_common *priv = dev->priv;
|
|
|
+ struct ieee80211_supported_band *tmp, *old;
|
|
|
+ unsigned int i, j;
|
|
|
+ int ret = -ENOMEM;
|
|
|
+
|
|
|
+ if ((!list->entries) || (!list->band_channel_num[band]))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
|
|
|
+ if (!tmp)
|
|
|
+ goto err_out;
|
|
|
+
|
|
|
+ tmp->channels = kzalloc(sizeof(struct ieee80211_channel) *
|
|
|
+ list->band_channel_num[band], GFP_KERNEL);
|
|
|
+ if (!tmp->channels)
|
|
|
+ goto err_out;
|
|
|
+
|
|
|
+ ret = p54_fill_band_bitrates(dev, tmp, band);
|
|
|
+ if (ret)
|
|
|
+ goto err_out;
|
|
|
+
|
|
|
+ for (i = 0, j = 0; (j < list->band_channel_num[band]) &&
|
|
|
+ (i < list->entries); i++) {
|
|
|
+
|
|
|
+ if (list->channels[i].band != band)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (list->channels[i].data != CHAN_HAS_ALL) {
|
|
|
+ printk(KERN_ERR "%s:%s%s%s is/are missing for "
|
|
|
+ "channel:%d [%d MHz].\n",
|
|
|
+ wiphy_name(dev->wiphy),
|
|
|
+ (list->channels[i].data & CHAN_HAS_CAL ? "" :
|
|
|
+ " [iqauto calibration data]"),
|
|
|
+ (list->channels[i].data & CHAN_HAS_LIMIT ? "" :
|
|
|
+ " [output power limits]"),
|
|
|
+ (list->channels[i].data & CHAN_HAS_CURVE ? "" :
|
|
|
+ " [curve data]"),
|
|
|
+ list->channels[i].index, list->channels[i].freq);
|
|
|
+ }
|
|
|
+
|
|
|
+ tmp->channels[j].band = list->channels[i].band;
|
|
|
+ tmp->channels[j].center_freq = list->channels[i].freq;
|
|
|
+ j++;
|
|
|
+ }
|
|
|
+
|
|
|
+ tmp->n_channels = list->band_channel_num[band];
|
|
|
+ old = priv->band_table[band];
|
|
|
+ priv->band_table[band] = tmp;
|
|
|
+ if (old) {
|
|
|
+ kfree(old->channels);
|
|
|
+ kfree(old);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err_out:
|
|
|
+ if (tmp) {
|
|
|
+ kfree(tmp->channels);
|
|
|
+ kfree(tmp);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void p54_update_channel_param(struct p54_channel_list *list,
|
|
|
+ u16 freq, u16 data)
|
|
|
+{
|
|
|
+ int band, i;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * usually all lists in the eeprom are mostly sorted.
|
|
|
+ * so it's very likely that the entry we are looking for
|
|
|
+ * is right at the end of the list
|
|
|
+ */
|
|
|
+ for (i = list->entries; i >= 0; i--) {
|
|
|
+ if (freq == list->channels[i].freq) {
|
|
|
+ list->channels[i].data |= data;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((i < 0) && (list->entries < list->max_entries)) {
|
|
|
+ /* entry does not exist yet. Initialize a new one. */
|
|
|
+ band = p54_get_band_from_freq(freq);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * filter out frequencies which don't belong into
|
|
|
+ * any supported band.
|
|
|
+ */
|
|
|
+ if (band < 0)
|
|
|
+ return ;
|
|
|
+
|
|
|
+ i = list->entries++;
|
|
|
+ list->band_channel_num[band]++;
|
|
|
+
|
|
|
+ list->channels[i].freq = freq;
|
|
|
+ list->channels[i].data = data;
|
|
|
+ list->channels[i].band = band;
|
|
|
+ list->channels[i].index = ieee80211_frequency_to_channel(freq);
|
|
|
+ /* TODO: parse output_limit and fill max_power */
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int p54_generate_channel_lists(struct ieee80211_hw *dev)
|
|
|
+{
|
|
|
+ struct p54_common *priv = dev->priv;
|
|
|
+ struct p54_channel_list *list;
|
|
|
+ unsigned int i, j, max_channel_num;
|
|
|
+ int ret = -ENOMEM;
|
|
|
+ u16 freq;
|
|
|
+
|
|
|
+ if ((priv->iq_autocal_len != priv->curve_data->entries) ||
|
|
|
+ (priv->iq_autocal_len != priv->output_limit->entries))
|
|
|
+ printk(KERN_ERR "%s: EEPROM is damaged... you may not be able"
|
|
|
+ "to use all channels with this device.\n",
|
|
|
+ wiphy_name(dev->wiphy));
|
|
|
+
|
|
|
+ max_channel_num = max_t(unsigned int, priv->output_limit->entries,
|
|
|
+ priv->iq_autocal_len);
|
|
|
+ max_channel_num = max_t(unsigned int, max_channel_num,
|
|
|
+ priv->curve_data->entries);
|
|
|
+
|
|
|
+ list = kzalloc(sizeof(*list), GFP_KERNEL);
|
|
|
+ if (!list)
|
|
|
+ goto free;
|
|
|
+
|
|
|
+ list->max_entries = max_channel_num;
|
|
|
+ list->channels = kzalloc(sizeof(struct p54_channel_entry) *
|
|
|
+ max_channel_num, GFP_KERNEL);
|
|
|
+ if (!list->channels)
|
|
|
+ goto free;
|
|
|
+
|
|
|
+ for (i = 0; i < max_channel_num; i++) {
|
|
|
+ if (i < priv->iq_autocal_len) {
|
|
|
+ freq = le16_to_cpu(priv->iq_autocal[i].freq);
|
|
|
+ p54_update_channel_param(list, freq, CHAN_HAS_CAL);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (i < priv->output_limit->entries) {
|
|
|
+ freq = le16_to_cpup((__le16 *) (i *
|
|
|
+ priv->output_limit->entry_size +
|
|
|
+ priv->output_limit->offset +
|
|
|
+ priv->output_limit->data));
|
|
|
+
|
|
|
+ p54_update_channel_param(list, freq, CHAN_HAS_LIMIT);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (i < priv->curve_data->entries) {
|
|
|
+ freq = le16_to_cpup((__le16 *) (i *
|
|
|
+ priv->curve_data->entry_size +
|
|
|
+ priv->curve_data->offset +
|
|
|
+ priv->curve_data->data));
|
|
|
+
|
|
|
+ p54_update_channel_param(list, freq, CHAN_HAS_CURVE);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* sort the list by the channel index */
|
|
|
+ sort(list->channels, list->entries, sizeof(struct p54_channel_entry),
|
|
|
+ p54_compare_channels, NULL);
|
|
|
+
|
|
|
+ for (i = 0, j = 0; i < IEEE80211_NUM_BANDS; i++) {
|
|
|
+ if (list->band_channel_num[i]) {
|
|
|
+ ret = p54_generate_band(dev, list, i);
|
|
|
+ if (ret)
|
|
|
+ goto free;
|
|
|
+
|
|
|
+ j++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (j == 0) {
|
|
|
+ /* no useable band available. */
|
|
|
+ ret = -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+free:
|
|
|
+ if (list) {
|
|
|
+ kfree(list->channels);
|
|
|
+ kfree(list);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static int p54_convert_rev0(struct ieee80211_hw *dev,
|
|
|
struct pda_pa_curve_data *curve_data)
|
|
|
{
|
|
@@ -487,13 +670,19 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
|
|
|
goto err;
|
|
|
}
|
|
|
|
|
|
+ err = p54_generate_channel_lists(dev);
|
|
|
+ if (err)
|
|
|
+ goto err;
|
|
|
+
|
|
|
priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK;
|
|
|
if (priv->rxhw == PDR_SYNTH_FRONTEND_XBOW)
|
|
|
p54_init_xbow_synth(priv);
|
|
|
if (!(synth & PDR_SYNTH_24_GHZ_DISABLED))
|
|
|
- dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &band_2GHz;
|
|
|
+ dev->wiphy->bands[IEEE80211_BAND_2GHZ] =
|
|
|
+ priv->band_table[IEEE80211_BAND_2GHZ];
|
|
|
if (!(synth & PDR_SYNTH_5_GHZ_DISABLED))
|
|
|
- dev->wiphy->bands[IEEE80211_BAND_5GHZ] = &band_5GHz;
|
|
|
+ dev->wiphy->bands[IEEE80211_BAND_5GHZ] =
|
|
|
+ priv->band_table[IEEE80211_BAND_5GHZ];
|
|
|
if ((synth & PDR_SYNTH_RX_DIV_MASK) == PDR_SYNTH_RX_DIV_SUPPORTED)
|
|
|
priv->rx_diversity_mask = 3;
|
|
|
if ((synth & PDR_SYNTH_TX_DIV_MASK) == PDR_SYNTH_TX_DIV_SUPPORTED)
|