diff options
Diffstat (limited to 'drivers/net/wireless/p54/eeprom.c')
-rw-r--r-- | drivers/net/wireless/p54/eeprom.c | 211 |
1 files changed, 169 insertions, 42 deletions
diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c index 35b09aa0529b..13d750da9301 100644 --- a/drivers/net/wireless/p54/eeprom.c +++ b/drivers/net/wireless/p54/eeprom.c | |||
@@ -55,6 +55,17 @@ static struct ieee80211_rate p54_arates[] = { | |||
55 | { .bitrate = 540, .hw_value = 11, }, | 55 | { .bitrate = 540, .hw_value = 11, }, |
56 | }; | 56 | }; |
57 | 57 | ||
58 | static struct p54_rssi_db_entry p54_rssi_default = { | ||
59 | /* | ||
60 | * The defaults are taken from usb-logs of the | ||
61 | * vendor driver. So, they should be safe to | ||
62 | * use in case we can't get a match from the | ||
63 | * rssi <-> dBm conversion database. | ||
64 | */ | ||
65 | .mul = 130, | ||
66 | .add = -398, | ||
67 | }; | ||
68 | |||
58 | #define CHAN_HAS_CAL BIT(0) | 69 | #define CHAN_HAS_CAL BIT(0) |
59 | #define CHAN_HAS_LIMIT BIT(1) | 70 | #define CHAN_HAS_LIMIT BIT(1) |
60 | #define CHAN_HAS_CURVE BIT(2) | 71 | #define CHAN_HAS_CURVE BIT(2) |
@@ -87,13 +98,27 @@ static int p54_get_band_from_freq(u16 freq) | |||
87 | return -1; | 98 | return -1; |
88 | } | 99 | } |
89 | 100 | ||
101 | static int same_band(u16 freq, u16 freq2) | ||
102 | { | ||
103 | return p54_get_band_from_freq(freq) == p54_get_band_from_freq(freq2); | ||
104 | } | ||
105 | |||
90 | static int p54_compare_channels(const void *_a, | 106 | static int p54_compare_channels(const void *_a, |
91 | const void *_b) | 107 | const void *_b) |
92 | { | 108 | { |
93 | const struct p54_channel_entry *a = _a; | 109 | const struct p54_channel_entry *a = _a; |
94 | const struct p54_channel_entry *b = _b; | 110 | const struct p54_channel_entry *b = _b; |
95 | 111 | ||
96 | return a->index - b->index; | 112 | return a->freq - b->freq; |
113 | } | ||
114 | |||
115 | static int p54_compare_rssichan(const void *_a, | ||
116 | const void *_b) | ||
117 | { | ||
118 | const struct p54_rssi_db_entry *a = _a; | ||
119 | const struct p54_rssi_db_entry *b = _b; | ||
120 | |||
121 | return a->freq - b->freq; | ||
97 | } | 122 | } |
98 | 123 | ||
99 | static int p54_fill_band_bitrates(struct ieee80211_hw *dev, | 124 | static int p54_fill_band_bitrates(struct ieee80211_hw *dev, |
@@ -145,25 +170,26 @@ static int p54_generate_band(struct ieee80211_hw *dev, | |||
145 | 170 | ||
146 | for (i = 0, j = 0; (j < list->band_channel_num[band]) && | 171 | for (i = 0, j = 0; (j < list->band_channel_num[band]) && |
147 | (i < list->entries); i++) { | 172 | (i < list->entries); i++) { |
173 | struct p54_channel_entry *chan = &list->channels[i]; | ||
148 | 174 | ||
149 | if (list->channels[i].band != band) | 175 | if (chan->band != band) |
150 | continue; | 176 | continue; |
151 | 177 | ||
152 | if (list->channels[i].data != CHAN_HAS_ALL) { | 178 | if (chan->data != CHAN_HAS_ALL) { |
153 | wiphy_err(dev->wiphy, | 179 | wiphy_err(dev->wiphy, "%s%s%s is/are missing for " |
154 | "%s%s%s is/are missing for channel:%d [%d MHz].\n", | 180 | "channel:%d [%d MHz].\n", |
155 | (list->channels[i].data & CHAN_HAS_CAL ? "" : | 181 | (chan->data & CHAN_HAS_CAL ? "" : |
156 | " [iqauto calibration data]"), | 182 | " [iqauto calibration data]"), |
157 | (list->channels[i].data & CHAN_HAS_LIMIT ? "" : | 183 | (chan->data & CHAN_HAS_LIMIT ? "" : |
158 | " [output power limits]"), | 184 | " [output power limits]"), |
159 | (list->channels[i].data & CHAN_HAS_CURVE ? "" : | 185 | (chan->data & CHAN_HAS_CURVE ? "" : |
160 | " [curve data]"), | 186 | " [curve data]"), |
161 | list->channels[i].index, list->channels[i].freq); | 187 | chan->index, chan->freq); |
162 | continue; | 188 | continue; |
163 | } | 189 | } |
164 | 190 | ||
165 | tmp->channels[j].band = list->channels[i].band; | 191 | tmp->channels[j].band = chan->band; |
166 | tmp->channels[j].center_freq = list->channels[i].freq; | 192 | tmp->channels[j].center_freq = chan->freq; |
167 | j++; | 193 | j++; |
168 | } | 194 | } |
169 | 195 | ||
@@ -291,7 +317,7 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev) | |||
291 | } | 317 | } |
292 | } | 318 | } |
293 | 319 | ||
294 | /* sort the list by the channel index */ | 320 | /* sort the channel list by frequency */ |
295 | sort(list->channels, list->entries, sizeof(struct p54_channel_entry), | 321 | sort(list->channels, list->entries, sizeof(struct p54_channel_entry), |
296 | p54_compare_channels, NULL); | 322 | p54_compare_channels, NULL); |
297 | 323 | ||
@@ -410,33 +436,121 @@ static int p54_convert_rev1(struct ieee80211_hw *dev, | |||
410 | static const char *p54_rf_chips[] = { "INVALID-0", "Duette3", "Duette2", | 436 | static const char *p54_rf_chips[] = { "INVALID-0", "Duette3", "Duette2", |
411 | "Frisbee", "Xbow", "Longbow", "INVALID-6", "INVALID-7" }; | 437 | "Frisbee", "Xbow", "Longbow", "INVALID-6", "INVALID-7" }; |
412 | 438 | ||
413 | static void p54_parse_rssical(struct ieee80211_hw *dev, void *data, int len, | 439 | static int p54_parse_rssical(struct ieee80211_hw *dev, |
414 | u16 type) | 440 | u8 *data, int len, u16 type) |
415 | { | 441 | { |
416 | struct p54_common *priv = dev->priv; | 442 | struct p54_common *priv = dev->priv; |
417 | int offset = (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) ? 2 : 0; | 443 | struct p54_rssi_db_entry *entry; |
418 | int entry_size = sizeof(struct pda_rssi_cal_entry) + offset; | 444 | size_t db_len, entries; |
419 | int num_entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2; | 445 | int offset = 0, i; |
420 | int i; | 446 | |
447 | if (type != PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) { | ||
448 | entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2; | ||
449 | if (len != sizeof(struct pda_rssi_cal_entry) * entries) { | ||
450 | wiphy_err(dev->wiphy, "rssical size mismatch.\n"); | ||
451 | goto err_data; | ||
452 | } | ||
453 | } else { | ||
454 | /* | ||
455 | * Some devices (Dell 1450 USB, Xbow 5GHz card, etc...) | ||
456 | * have an empty two byte header. | ||
457 | */ | ||
458 | if (*((__le16 *)&data[offset]) == cpu_to_le16(0)) | ||
459 | offset += 2; | ||
421 | 460 | ||
422 | if (len != (entry_size * num_entries)) { | 461 | entries = (len - offset) / |
423 | wiphy_err(dev->wiphy, | 462 | sizeof(struct pda_rssi_cal_ext_entry); |
424 | "unknown rssi calibration data packing type:(%x) len:%d.\n", | ||
425 | type, len); | ||
426 | 463 | ||
427 | print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE, | 464 | if ((len - offset) % sizeof(struct pda_rssi_cal_ext_entry) || |
428 | data, len); | 465 | entries <= 0) { |
466 | wiphy_err(dev->wiphy, "invalid rssi database.\n"); | ||
467 | goto err_data; | ||
468 | } | ||
469 | } | ||
429 | 470 | ||
430 | wiphy_err(dev->wiphy, "please report this issue.\n"); | 471 | db_len = sizeof(*entry) * entries; |
431 | return; | 472 | priv->rssi_db = kzalloc(db_len + sizeof(*priv->rssi_db), GFP_KERNEL); |
473 | if (!priv->rssi_db) | ||
474 | return -ENOMEM; | ||
475 | |||
476 | priv->rssi_db->offset = 0; | ||
477 | priv->rssi_db->entries = entries; | ||
478 | priv->rssi_db->entry_size = sizeof(*entry); | ||
479 | priv->rssi_db->len = db_len; | ||
480 | |||
481 | entry = (void *)((unsigned long)priv->rssi_db->data + priv->rssi_db->offset); | ||
482 | if (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) { | ||
483 | struct pda_rssi_cal_ext_entry *cal = (void *) &data[offset]; | ||
484 | |||
485 | for (i = 0; i < entries; i++) { | ||
486 | entry[i].freq = le16_to_cpu(cal[i].freq); | ||
487 | entry[i].mul = (s16) le16_to_cpu(cal[i].mul); | ||
488 | entry[i].add = (s16) le16_to_cpu(cal[i].add); | ||
489 | } | ||
490 | } else { | ||
491 | struct pda_rssi_cal_entry *cal = (void *) &data[offset]; | ||
492 | |||
493 | for (i = 0; i < entries; i++) { | ||
494 | u16 freq; | ||
495 | switch (i) { | ||
496 | case IEEE80211_BAND_2GHZ: | ||
497 | freq = 2437; | ||
498 | break; | ||
499 | case IEEE80211_BAND_5GHZ: | ||
500 | freq = 5240; | ||
501 | break; | ||
502 | } | ||
503 | |||
504 | entry[i].freq = freq; | ||
505 | entry[i].mul = (s16) le16_to_cpu(cal[i].mul); | ||
506 | entry[i].add = (s16) le16_to_cpu(cal[i].add); | ||
507 | } | ||
432 | } | 508 | } |
433 | 509 | ||
434 | for (i = 0; i < num_entries; i++) { | 510 | /* sort the list by channel frequency */ |
435 | struct pda_rssi_cal_entry *cal = data + | 511 | sort(entry, entries, sizeof(*entry), p54_compare_rssichan, NULL); |
436 | (offset + i * entry_size); | 512 | return 0; |
437 | priv->rssical_db[i].mul = (s16) le16_to_cpu(cal->mul); | 513 | |
438 | priv->rssical_db[i].add = (s16) le16_to_cpu(cal->add); | 514 | err_data: |
515 | wiphy_err(dev->wiphy, | ||
516 | "rssi calibration data packing type:(%x) len:%d.\n", | ||
517 | type, len); | ||
518 | |||
519 | print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE, data, len); | ||
520 | |||
521 | wiphy_err(dev->wiphy, "please report this issue.\n"); | ||
522 | return -EINVAL; | ||
523 | } | ||
524 | |||
525 | struct p54_rssi_db_entry *p54_rssi_find(struct p54_common *priv, const u16 freq) | ||
526 | { | ||
527 | struct p54_rssi_db_entry *entry; | ||
528 | int i, found = -1; | ||
529 | |||
530 | if (!priv->rssi_db) | ||
531 | return &p54_rssi_default; | ||
532 | |||
533 | entry = (void *)(priv->rssi_db->data + priv->rssi_db->offset); | ||
534 | for (i = 0; i < priv->rssi_db->entries; i++) { | ||
535 | if (!same_band(freq, entry[i].freq)) | ||
536 | continue; | ||
537 | |||
538 | if (found == -1) { | ||
539 | found = i; | ||
540 | continue; | ||
541 | } | ||
542 | |||
543 | /* nearest match */ | ||
544 | if (abs(freq - entry[i].freq) < | ||
545 | abs(freq - entry[found].freq)) { | ||
546 | found = i; | ||
547 | continue; | ||
548 | } else { | ||
549 | break; | ||
550 | } | ||
439 | } | 551 | } |
552 | |||
553 | return found < 0 ? &p54_rssi_default : &entry[found]; | ||
440 | } | 554 | } |
441 | 555 | ||
442 | static void p54_parse_default_country(struct ieee80211_hw *dev, | 556 | static void p54_parse_default_country(struct ieee80211_hw *dev, |
@@ -627,21 +741,30 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) | |||
627 | case PDR_RSSI_LINEAR_APPROXIMATION: | 741 | case PDR_RSSI_LINEAR_APPROXIMATION: |
628 | case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND: | 742 | case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND: |
629 | case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED: | 743 | case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED: |
630 | p54_parse_rssical(dev, entry->data, data_len, | 744 | err = p54_parse_rssical(dev, entry->data, data_len, |
631 | le16_to_cpu(entry->code)); | 745 | le16_to_cpu(entry->code)); |
746 | if (err) | ||
747 | goto err; | ||
632 | break; | 748 | break; |
633 | case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM: { | 749 | case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2: { |
634 | __le16 *src = (void *) entry->data; | 750 | struct pda_custom_wrapper *pda = (void *) entry->data; |
635 | s16 *dst = (void *) &priv->rssical_db; | 751 | __le16 *src; |
752 | u16 *dst; | ||
636 | int i; | 753 | int i; |
637 | 754 | ||
638 | if (data_len != sizeof(priv->rssical_db)) { | 755 | if (priv->rssi_db || data_len < sizeof(*pda)) |
639 | err = -EINVAL; | 756 | break; |
640 | goto err; | 757 | |
641 | } | 758 | priv->rssi_db = p54_convert_db(pda, data_len); |
642 | for (i = 0; i < sizeof(priv->rssical_db) / | 759 | if (!priv->rssi_db) |
643 | sizeof(*src); i++) | 760 | break; |
761 | |||
762 | src = (void *) priv->rssi_db->data; | ||
763 | dst = (void *) priv->rssi_db->data; | ||
764 | |||
765 | for (i = 0; i < priv->rssi_db->entries; i++) | ||
644 | *(dst++) = (s16) le16_to_cpu(*(src++)); | 766 | *(dst++) = (s16) le16_to_cpu(*(src++)); |
767 | |||
645 | } | 768 | } |
646 | break; | 769 | break; |
647 | case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: { | 770 | case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: { |
@@ -717,6 +840,8 @@ good_eeprom: | |||
717 | SET_IEEE80211_PERM_ADDR(dev, perm_addr); | 840 | SET_IEEE80211_PERM_ADDR(dev, perm_addr); |
718 | } | 841 | } |
719 | 842 | ||
843 | priv->cur_rssi = &p54_rssi_default; | ||
844 | |||
720 | wiphy_info(dev->wiphy, "hwaddr %pM, MAC:isl38%02x RF:%s\n", | 845 | wiphy_info(dev->wiphy, "hwaddr %pM, MAC:isl38%02x RF:%s\n", |
721 | dev->wiphy->perm_addr, priv->version, | 846 | dev->wiphy->perm_addr, priv->version, |
722 | p54_rf_chips[priv->rxhw]); | 847 | p54_rf_chips[priv->rxhw]); |
@@ -727,9 +852,11 @@ err: | |||
727 | kfree(priv->iq_autocal); | 852 | kfree(priv->iq_autocal); |
728 | kfree(priv->output_limit); | 853 | kfree(priv->output_limit); |
729 | kfree(priv->curve_data); | 854 | kfree(priv->curve_data); |
855 | kfree(priv->rssi_db); | ||
730 | priv->iq_autocal = NULL; | 856 | priv->iq_autocal = NULL; |
731 | priv->output_limit = NULL; | 857 | priv->output_limit = NULL; |
732 | priv->curve_data = NULL; | 858 | priv->curve_data = NULL; |
859 | priv->rssi_db = NULL; | ||
733 | 860 | ||
734 | wiphy_err(dev->wiphy, "eeprom parse failed!\n"); | 861 | wiphy_err(dev->wiphy, "eeprom parse failed!\n"); |
735 | return err; | 862 | return err; |