diff options
Diffstat (limited to 'drivers/net/wireless/p54/eeprom.c')
-rw-r--r-- | drivers/net/wireless/p54/eeprom.c | 236 |
1 files changed, 190 insertions, 46 deletions
diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c index 78347041ec40..54cc0bba66b9 100644 --- a/drivers/net/wireless/p54/eeprom.c +++ b/drivers/net/wireless/p54/eeprom.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
24 | 24 | ||
25 | #include <net/mac80211.h> | 25 | #include <net/mac80211.h> |
26 | #include <linux/crc-ccitt.h> | ||
26 | 27 | ||
27 | #include "p54.h" | 28 | #include "p54.h" |
28 | #include "eeprom.h" | 29 | #include "eeprom.h" |
@@ -54,6 +55,17 @@ static struct ieee80211_rate p54_arates[] = { | |||
54 | { .bitrate = 540, .hw_value = 11, }, | 55 | { .bitrate = 540, .hw_value = 11, }, |
55 | }; | 56 | }; |
56 | 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 | |||
57 | #define CHAN_HAS_CAL BIT(0) | 69 | #define CHAN_HAS_CAL BIT(0) |
58 | #define CHAN_HAS_LIMIT BIT(1) | 70 | #define CHAN_HAS_LIMIT BIT(1) |
59 | #define CHAN_HAS_CURVE BIT(2) | 71 | #define CHAN_HAS_CURVE BIT(2) |
@@ -86,13 +98,27 @@ static int p54_get_band_from_freq(u16 freq) | |||
86 | return -1; | 98 | return -1; |
87 | } | 99 | } |
88 | 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 | |||
89 | static int p54_compare_channels(const void *_a, | 106 | static int p54_compare_channels(const void *_a, |
90 | const void *_b) | 107 | const void *_b) |
91 | { | 108 | { |
92 | const struct p54_channel_entry *a = _a; | 109 | const struct p54_channel_entry *a = _a; |
93 | const struct p54_channel_entry *b = _b; | 110 | const struct p54_channel_entry *b = _b; |
94 | 111 | ||
95 | 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; | ||
96 | } | 122 | } |
97 | 123 | ||
98 | static int p54_fill_band_bitrates(struct ieee80211_hw *dev, | 124 | static int p54_fill_band_bitrates(struct ieee80211_hw *dev, |
@@ -144,25 +170,26 @@ static int p54_generate_band(struct ieee80211_hw *dev, | |||
144 | 170 | ||
145 | for (i = 0, j = 0; (j < list->band_channel_num[band]) && | 171 | for (i = 0, j = 0; (j < list->band_channel_num[band]) && |
146 | (i < list->entries); i++) { | 172 | (i < list->entries); i++) { |
173 | struct p54_channel_entry *chan = &list->channels[i]; | ||
147 | 174 | ||
148 | if (list->channels[i].band != band) | 175 | if (chan->band != band) |
149 | continue; | 176 | continue; |
150 | 177 | ||
151 | if (list->channels[i].data != CHAN_HAS_ALL) { | 178 | if (chan->data != CHAN_HAS_ALL) { |
152 | wiphy_err(dev->wiphy, | 179 | wiphy_err(dev->wiphy, "%s%s%s is/are missing for " |
153 | "%s%s%s is/are missing for channel:%d [%d MHz].\n", | 180 | "channel:%d [%d MHz].\n", |
154 | (list->channels[i].data & CHAN_HAS_CAL ? "" : | 181 | (chan->data & CHAN_HAS_CAL ? "" : |
155 | " [iqauto calibration data]"), | 182 | " [iqauto calibration data]"), |
156 | (list->channels[i].data & CHAN_HAS_LIMIT ? "" : | 183 | (chan->data & CHAN_HAS_LIMIT ? "" : |
157 | " [output power limits]"), | 184 | " [output power limits]"), |
158 | (list->channels[i].data & CHAN_HAS_CURVE ? "" : | 185 | (chan->data & CHAN_HAS_CURVE ? "" : |
159 | " [curve data]"), | 186 | " [curve data]"), |
160 | list->channels[i].index, list->channels[i].freq); | 187 | chan->index, chan->freq); |
161 | continue; | 188 | continue; |
162 | } | 189 | } |
163 | 190 | ||
164 | tmp->channels[j].band = list->channels[i].band; | 191 | tmp->channels[j].band = chan->band; |
165 | tmp->channels[j].center_freq = list->channels[i].freq; | 192 | tmp->channels[j].center_freq = chan->freq; |
166 | j++; | 193 | j++; |
167 | } | 194 | } |
168 | 195 | ||
@@ -260,8 +287,10 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev) | |||
260 | list->max_entries = max_channel_num; | 287 | list->max_entries = max_channel_num; |
261 | list->channels = kzalloc(sizeof(struct p54_channel_entry) * | 288 | list->channels = kzalloc(sizeof(struct p54_channel_entry) * |
262 | max_channel_num, GFP_KERNEL); | 289 | max_channel_num, GFP_KERNEL); |
263 | if (!list->channels) | 290 | if (!list->channels) { |
291 | ret = -ENOMEM; | ||
264 | goto free; | 292 | goto free; |
293 | } | ||
265 | 294 | ||
266 | for (i = 0; i < max_channel_num; i++) { | 295 | for (i = 0; i < max_channel_num; i++) { |
267 | if (i < priv->iq_autocal_len) { | 296 | if (i < priv->iq_autocal_len) { |
@@ -288,7 +317,7 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev) | |||
288 | } | 317 | } |
289 | } | 318 | } |
290 | 319 | ||
291 | /* sort the list by the channel index */ | 320 | /* sort the channel list by frequency */ |
292 | sort(list->channels, list->entries, sizeof(struct p54_channel_entry), | 321 | sort(list->channels, list->entries, sizeof(struct p54_channel_entry), |
293 | p54_compare_channels, NULL); | 322 | p54_compare_channels, NULL); |
294 | 323 | ||
@@ -407,33 +436,121 @@ static int p54_convert_rev1(struct ieee80211_hw *dev, | |||
407 | static const char *p54_rf_chips[] = { "INVALID-0", "Duette3", "Duette2", | 436 | static const char *p54_rf_chips[] = { "INVALID-0", "Duette3", "Duette2", |
408 | "Frisbee", "Xbow", "Longbow", "INVALID-6", "INVALID-7" }; | 437 | "Frisbee", "Xbow", "Longbow", "INVALID-6", "INVALID-7" }; |
409 | 438 | ||
410 | static void p54_parse_rssical(struct ieee80211_hw *dev, void *data, int len, | 439 | static int p54_parse_rssical(struct ieee80211_hw *dev, |
411 | u16 type) | 440 | u8 *data, int len, u16 type) |
412 | { | 441 | { |
413 | struct p54_common *priv = dev->priv; | 442 | struct p54_common *priv = dev->priv; |
414 | int offset = (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) ? 2 : 0; | 443 | struct p54_rssi_db_entry *entry; |
415 | int entry_size = sizeof(struct pda_rssi_cal_entry) + offset; | 444 | size_t db_len, entries; |
416 | int num_entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2; | 445 | int offset = 0, i; |
417 | 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; | ||
418 | 460 | ||
419 | if (len != (entry_size * num_entries)) { | 461 | entries = (len - offset) / |
420 | wiphy_err(dev->wiphy, | 462 | sizeof(struct pda_rssi_cal_ext_entry); |
421 | "unknown rssi calibration data packing type:(%x) len:%d.\n", | ||
422 | type, len); | ||
423 | 463 | ||
424 | print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE, | 464 | if ((len - offset) % sizeof(struct pda_rssi_cal_ext_entry) || |
425 | data, len); | 465 | entries <= 0) { |
466 | wiphy_err(dev->wiphy, "invalid rssi database.\n"); | ||
467 | goto err_data; | ||
468 | } | ||
469 | } | ||
426 | 470 | ||
427 | wiphy_err(dev->wiphy, "please report this issue.\n"); | 471 | db_len = sizeof(*entry) * entries; |
428 | 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 = 0; | ||
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 | } | ||
429 | } | 508 | } |
430 | 509 | ||
431 | for (i = 0; i < num_entries; i++) { | 510 | /* sort the list by channel frequency */ |
432 | struct pda_rssi_cal_entry *cal = data + | 511 | sort(entry, entries, sizeof(*entry), p54_compare_rssichan, NULL); |
433 | (offset + i * entry_size); | 512 | return 0; |
434 | priv->rssical_db[i].mul = (s16) le16_to_cpu(cal->mul); | 513 | |
435 | 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 | } | ||
436 | } | 551 | } |
552 | |||
553 | return found < 0 ? &p54_rssi_default : &entry[found]; | ||
437 | } | 554 | } |
438 | 555 | ||
439 | static void p54_parse_default_country(struct ieee80211_hw *dev, | 556 | static void p54_parse_default_country(struct ieee80211_hw *dev, |
@@ -540,6 +657,7 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) | |||
540 | int err; | 657 | int err; |
541 | u8 *end = (u8 *)eeprom + len; | 658 | u8 *end = (u8 *)eeprom + len; |
542 | u16 synth = 0; | 659 | u16 synth = 0; |
660 | u16 crc16 = ~0; | ||
543 | 661 | ||
544 | wrap = (struct eeprom_pda_wrap *) eeprom; | 662 | wrap = (struct eeprom_pda_wrap *) eeprom; |
545 | entry = (void *)wrap->data + le16_to_cpu(wrap->len); | 663 | entry = (void *)wrap->data + le16_to_cpu(wrap->len); |
@@ -623,21 +741,30 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) | |||
623 | case PDR_RSSI_LINEAR_APPROXIMATION: | 741 | case PDR_RSSI_LINEAR_APPROXIMATION: |
624 | case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND: | 742 | case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND: |
625 | case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED: | 743 | case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED: |
626 | p54_parse_rssical(dev, entry->data, data_len, | 744 | err = p54_parse_rssical(dev, entry->data, data_len, |
627 | le16_to_cpu(entry->code)); | 745 | le16_to_cpu(entry->code)); |
746 | if (err) | ||
747 | goto err; | ||
628 | break; | 748 | break; |
629 | case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM: { | 749 | case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2: { |
630 | __le16 *src = (void *) entry->data; | 750 | struct pda_custom_wrapper *pda = (void *) entry->data; |
631 | s16 *dst = (void *) &priv->rssical_db; | 751 | __le16 *src; |
752 | u16 *dst; | ||
632 | int i; | 753 | int i; |
633 | 754 | ||
634 | if (data_len != sizeof(priv->rssical_db)) { | 755 | if (priv->rssi_db || data_len < sizeof(*pda)) |
635 | err = -EINVAL; | 756 | break; |
636 | goto err; | 757 | |
637 | } | 758 | priv->rssi_db = p54_convert_db(pda, data_len); |
638 | for (i = 0; i < sizeof(priv->rssical_db) / | 759 | if (!priv->rssi_db) |
639 | 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++) | ||
640 | *(dst++) = (s16) le16_to_cpu(*(src++)); | 766 | *(dst++) = (s16) le16_to_cpu(*(src++)); |
767 | |||
641 | } | 768 | } |
642 | break; | 769 | break; |
643 | case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: { | 770 | case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: { |
@@ -655,16 +782,29 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) | |||
655 | } | 782 | } |
656 | break; | 783 | break; |
657 | case PDR_END: | 784 | case PDR_END: |
658 | /* make it overrun */ | 785 | crc16 = ~crc_ccitt(crc16, (u8 *) entry, sizeof(*entry)); |
659 | entry_len = len; | 786 | if (crc16 != le16_to_cpup((__le16 *)entry->data)) { |
787 | wiphy_err(dev->wiphy, "eeprom failed checksum " | ||
788 | "test!\n"); | ||
789 | err = -ENOMSG; | ||
790 | goto err; | ||
791 | } else { | ||
792 | goto good_eeprom; | ||
793 | } | ||
660 | break; | 794 | break; |
661 | default: | 795 | default: |
662 | break; | 796 | break; |
663 | } | 797 | } |
664 | 798 | ||
665 | entry = (void *)entry + (entry_len + 1)*2; | 799 | crc16 = crc_ccitt(crc16, (u8 *)entry, (entry_len + 1) * 2); |
800 | entry = (void *)entry + (entry_len + 1) * 2; | ||
666 | } | 801 | } |
667 | 802 | ||
803 | wiphy_err(dev->wiphy, "unexpected end of eeprom data.\n"); | ||
804 | err = -ENODATA; | ||
805 | goto err; | ||
806 | |||
807 | good_eeprom: | ||
668 | if (!synth || !priv->iq_autocal || !priv->output_limit || | 808 | if (!synth || !priv->iq_autocal || !priv->output_limit || |
669 | !priv->curve_data) { | 809 | !priv->curve_data) { |
670 | wiphy_err(dev->wiphy, | 810 | wiphy_err(dev->wiphy, |
@@ -700,6 +840,8 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) | |||
700 | SET_IEEE80211_PERM_ADDR(dev, perm_addr); | 840 | SET_IEEE80211_PERM_ADDR(dev, perm_addr); |
701 | } | 841 | } |
702 | 842 | ||
843 | priv->cur_rssi = &p54_rssi_default; | ||
844 | |||
703 | 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", |
704 | dev->wiphy->perm_addr, priv->version, | 846 | dev->wiphy->perm_addr, priv->version, |
705 | p54_rf_chips[priv->rxhw]); | 847 | p54_rf_chips[priv->rxhw]); |
@@ -710,9 +852,11 @@ err: | |||
710 | kfree(priv->iq_autocal); | 852 | kfree(priv->iq_autocal); |
711 | kfree(priv->output_limit); | 853 | kfree(priv->output_limit); |
712 | kfree(priv->curve_data); | 854 | kfree(priv->curve_data); |
855 | kfree(priv->rssi_db); | ||
713 | priv->iq_autocal = NULL; | 856 | priv->iq_autocal = NULL; |
714 | priv->output_limit = NULL; | 857 | priv->output_limit = NULL; |
715 | priv->curve_data = NULL; | 858 | priv->curve_data = NULL; |
859 | priv->rssi_db = NULL; | ||
716 | 860 | ||
717 | wiphy_err(dev->wiphy, "eeprom parse failed!\n"); | 861 | wiphy_err(dev->wiphy, "eeprom parse failed!\n"); |
718 | return err; | 862 | return err; |