diff options
author | Christian Lamparter <chunkeey@googlemail.com> | 2011-02-12 16:32:49 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-02-14 15:52:10 -0500 |
commit | 7a047f4f2f3a812f09f42aa784499a54dc4afcf2 (patch) | |
tree | 4396f0fbc6d4cf296994fef8b9a5a92a86a0b576 /drivers/net/wireless/p54/eeprom.c | |
parent | a3162eed04ae76be710d895978478aa6d849de41 (diff) |
p54: enhance rssi->dBm database import
This patch fixes several shortcomings of the
previous implementation. Features of the
rewrite include:
* handles undocumented "0x0000" word at the
start of the frequency table.
(Affected some early? DELL 1450 USB devices
and my Symbol 5GHz miniPCI card.)
* supports more than just one reference point
per band. (Also needed for the Symbol card.)
* ships with default values in case the eeprom
data is damaged, absent or unsupported.
Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/p54/eeprom.c')
-rw-r--r-- | drivers/net/wireless/p54/eeprom.c | 183 |
1 files changed, 153 insertions, 30 deletions
diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c index 360bc7810f93..f54e15fcd623 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,6 +98,11 @@ 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 | { |
@@ -96,6 +112,15 @@ static int p54_compare_channels(const void *_a, | |||
96 | return a->freq - b->freq; | 112 | return a->freq - b->freq; |
97 | } | 113 | } |
98 | 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; | ||
122 | } | ||
123 | |||
99 | static int p54_fill_band_bitrates(struct ieee80211_hw *dev, | 124 | static int p54_fill_band_bitrates(struct ieee80211_hw *dev, |
100 | struct ieee80211_supported_band *band_entry, | 125 | struct ieee80211_supported_band *band_entry, |
101 | enum ieee80211_band band) | 126 | enum ieee80211_band band) |
@@ -411,33 +436,118 @@ static int p54_convert_rev1(struct ieee80211_hw *dev, | |||
411 | static const char *p54_rf_chips[] = { "INVALID-0", "Duette3", "Duette2", | 436 | static const char *p54_rf_chips[] = { "INVALID-0", "Duette3", "Duette2", |
412 | "Frisbee", "Xbow", "Longbow", "INVALID-6", "INVALID-7" }; | 437 | "Frisbee", "Xbow", "Longbow", "INVALID-6", "INVALID-7" }; |
413 | 438 | ||
414 | static void p54_parse_rssical(struct ieee80211_hw *dev, void *data, int len, | 439 | static int p54_parse_rssical(struct ieee80211_hw *dev, |
415 | u16 type) | 440 | u8 *data, int len, u16 type) |
416 | { | 441 | { |
417 | struct p54_common *priv = dev->priv; | 442 | struct p54_common *priv = dev->priv; |
418 | int offset = (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) ? 2 : 0; | 443 | struct p54_rssi_db_entry *entry; |
419 | int entry_size = sizeof(struct pda_rssi_cal_entry) + offset; | 444 | size_t db_len, entries; |
420 | int num_entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2; | 445 | int offset = 0, i; |
421 | 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; | ||
422 | 460 | ||
423 | if (len != (entry_size * num_entries)) { | 461 | entries = (len - offset) / |
424 | wiphy_err(dev->wiphy, | 462 | sizeof(struct pda_rssi_cal_ext_entry); |
425 | "unknown rssi calibration data packing type:(%x) len:%d.\n", | ||
426 | type, len); | ||
427 | 463 | ||
428 | print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE, | 464 | if ((len - offset) % sizeof(struct pda_rssi_cal_ext_entry) || |
429 | data, len); | 465 | entries <= 0) { |
466 | wiphy_err(dev->wiphy, "invalid rssi database.\n"); | ||
467 | goto err_data; | ||
468 | } | ||
469 | } | ||
430 | 470 | ||
431 | wiphy_err(dev->wiphy, "please report this issue.\n"); | 471 | db_len = sizeof(*entry) * entries; |
432 | 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 | } | ||
433 | } | 508 | } |
434 | 509 | ||
435 | for (i = 0; i < num_entries; i++) { | 510 | /* sort the list by channel frequency */ |
436 | struct pda_rssi_cal_entry *cal = data + | 511 | sort(entry, entries, sizeof(*entry), p54_compare_rssichan, NULL); |
437 | (offset + i * entry_size); | 512 | return 0; |
438 | priv->rssical_db[i].mul = (s16) le16_to_cpu(cal->mul); | 513 | |
439 | 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 = (void *)(priv->rssi_db->data + | ||
528 | priv->rssi_db->offset); | ||
529 | int i, found = -1; | ||
530 | |||
531 | for (i = 0; i < priv->rssi_db->entries; i++) { | ||
532 | if (!same_band(freq, entry[i].freq)) | ||
533 | continue; | ||
534 | |||
535 | if (found == -1) { | ||
536 | found = i; | ||
537 | continue; | ||
538 | } | ||
539 | |||
540 | /* nearest match */ | ||
541 | if (abs(freq - entry[i].freq) < | ||
542 | abs(freq - entry[found].freq)) { | ||
543 | found = i; | ||
544 | continue; | ||
545 | } else { | ||
546 | break; | ||
547 | } | ||
440 | } | 548 | } |
549 | |||
550 | return found < 0 ? &p54_rssi_default : &entry[found]; | ||
441 | } | 551 | } |
442 | 552 | ||
443 | static void p54_parse_default_country(struct ieee80211_hw *dev, | 553 | static void p54_parse_default_country(struct ieee80211_hw *dev, |
@@ -628,21 +738,30 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) | |||
628 | case PDR_RSSI_LINEAR_APPROXIMATION: | 738 | case PDR_RSSI_LINEAR_APPROXIMATION: |
629 | case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND: | 739 | case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND: |
630 | case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED: | 740 | case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED: |
631 | p54_parse_rssical(dev, entry->data, data_len, | 741 | err = p54_parse_rssical(dev, entry->data, data_len, |
632 | le16_to_cpu(entry->code)); | 742 | le16_to_cpu(entry->code)); |
743 | if (err) | ||
744 | goto err; | ||
633 | break; | 745 | break; |
634 | case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM: { | 746 | case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2: { |
635 | __le16 *src = (void *) entry->data; | 747 | struct pda_custom_wrapper *pda = (void *) entry->data; |
636 | s16 *dst = (void *) &priv->rssical_db; | 748 | __le16 *src; |
749 | u16 *dst; | ||
637 | int i; | 750 | int i; |
638 | 751 | ||
639 | if (data_len != sizeof(priv->rssical_db)) { | 752 | if (priv->rssi_db || data_len < sizeof(*pda)) |
640 | err = -EINVAL; | 753 | break; |
641 | goto err; | 754 | |
642 | } | 755 | priv->rssi_db = p54_convert_db(pda, data_len); |
643 | for (i = 0; i < sizeof(priv->rssical_db) / | 756 | if (!priv->rssi_db) |
644 | sizeof(*src); i++) | 757 | break; |
758 | |||
759 | src = (void *) priv->rssi_db->data; | ||
760 | dst = (void *) priv->rssi_db->data; | ||
761 | |||
762 | for (i = 0; i < priv->rssi_db->entries; i++) | ||
645 | *(dst++) = (s16) le16_to_cpu(*(src++)); | 763 | *(dst++) = (s16) le16_to_cpu(*(src++)); |
764 | |||
646 | } | 765 | } |
647 | break; | 766 | break; |
648 | case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: { | 767 | case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: { |
@@ -718,6 +837,8 @@ good_eeprom: | |||
718 | SET_IEEE80211_PERM_ADDR(dev, perm_addr); | 837 | SET_IEEE80211_PERM_ADDR(dev, perm_addr); |
719 | } | 838 | } |
720 | 839 | ||
840 | priv->cur_rssi = &p54_rssi_default; | ||
841 | |||
721 | wiphy_info(dev->wiphy, "hwaddr %pM, MAC:isl38%02x RF:%s\n", | 842 | wiphy_info(dev->wiphy, "hwaddr %pM, MAC:isl38%02x RF:%s\n", |
722 | dev->wiphy->perm_addr, priv->version, | 843 | dev->wiphy->perm_addr, priv->version, |
723 | p54_rf_chips[priv->rxhw]); | 844 | p54_rf_chips[priv->rxhw]); |
@@ -728,9 +849,11 @@ err: | |||
728 | kfree(priv->iq_autocal); | 849 | kfree(priv->iq_autocal); |
729 | kfree(priv->output_limit); | 850 | kfree(priv->output_limit); |
730 | kfree(priv->curve_data); | 851 | kfree(priv->curve_data); |
852 | kfree(priv->rssi_db); | ||
731 | priv->iq_autocal = NULL; | 853 | priv->iq_autocal = NULL; |
732 | priv->output_limit = NULL; | 854 | priv->output_limit = NULL; |
733 | priv->curve_data = NULL; | 855 | priv->curve_data = NULL; |
856 | priv->rssi_db = NULL; | ||
734 | 857 | ||
735 | wiphy_err(dev->wiphy, "eeprom parse failed!\n"); | 858 | wiphy_err(dev->wiphy, "eeprom parse failed!\n"); |
736 | return err; | 859 | return err; |