Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Christian Lamparter and committed by
John W. Linville
7a047f4f a3162eed

+176 -41
+153 -30
drivers/net/wireless/p54/eeprom.c
··· 55 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 69 #define CHAN_HAS_CAL BIT(0) 59 70 #define CHAN_HAS_LIMIT BIT(1) 60 71 #define CHAN_HAS_CURVE BIT(2) ··· 98 87 return -1; 99 88 } 100 89 90 + static int same_band(u16 freq, u16 freq2) 91 + { 92 + return p54_get_band_from_freq(freq) == p54_get_band_from_freq(freq2); 93 + } 94 + 101 95 static int p54_compare_channels(const void *_a, 102 96 const void *_b) 103 97 { 104 98 const struct p54_channel_entry *a = _a; 105 99 const struct p54_channel_entry *b = _b; 100 + 101 + return a->freq - b->freq; 102 + } 103 + 104 + static int p54_compare_rssichan(const void *_a, 105 + const void *_b) 106 + { 107 + const struct p54_rssi_db_entry *a = _a; 108 + const struct p54_rssi_db_entry *b = _b; 106 109 107 110 return a->freq - b->freq; 108 111 } ··· 436 411 static const char *p54_rf_chips[] = { "INVALID-0", "Duette3", "Duette2", 437 412 "Frisbee", "Xbow", "Longbow", "INVALID-6", "INVALID-7" }; 438 413 439 - static void p54_parse_rssical(struct ieee80211_hw *dev, void *data, int len, 440 - u16 type) 414 + static int p54_parse_rssical(struct ieee80211_hw *dev, 415 + u8 *data, int len, u16 type) 441 416 { 442 417 struct p54_common *priv = dev->priv; 443 - int offset = (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) ? 2 : 0; 444 - int entry_size = sizeof(struct pda_rssi_cal_entry) + offset; 445 - int num_entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2; 446 - int i; 418 + struct p54_rssi_db_entry *entry; 419 + size_t db_len, entries; 420 + int offset = 0, i; 447 421 448 - if (len != (entry_size * num_entries)) { 449 - wiphy_err(dev->wiphy, 450 - "unknown rssi calibration data packing type:(%x) len:%d.\n", 451 - type, len); 422 + if (type != PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) { 423 + entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2; 424 + if (len != sizeof(struct pda_rssi_cal_entry) * entries) { 425 + wiphy_err(dev->wiphy, "rssical size mismatch.\n"); 426 + goto err_data; 427 + } 428 + } else { 429 + /* 430 + * Some devices (Dell 1450 USB, Xbow 5GHz card, etc...) 431 + * have an empty two byte header. 432 + */ 433 + if (*((__le16 *)&data[offset]) == cpu_to_le16(0)) 434 + offset += 2; 452 435 453 - print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE, 454 - data, len); 436 + entries = (len - offset) / 437 + sizeof(struct pda_rssi_cal_ext_entry); 455 438 456 - wiphy_err(dev->wiphy, "please report this issue.\n"); 457 - return; 439 + if ((len - offset) % sizeof(struct pda_rssi_cal_ext_entry) || 440 + entries <= 0) { 441 + wiphy_err(dev->wiphy, "invalid rssi database.\n"); 442 + goto err_data; 443 + } 458 444 } 459 445 460 - for (i = 0; i < num_entries; i++) { 461 - struct pda_rssi_cal_entry *cal = data + 462 - (offset + i * entry_size); 463 - priv->rssical_db[i].mul = (s16) le16_to_cpu(cal->mul); 464 - priv->rssical_db[i].add = (s16) le16_to_cpu(cal->add); 446 + db_len = sizeof(*entry) * entries; 447 + priv->rssi_db = kzalloc(db_len + sizeof(*priv->rssi_db), GFP_KERNEL); 448 + if (!priv->rssi_db) 449 + return -ENOMEM; 450 + 451 + priv->rssi_db->offset = 0; 452 + priv->rssi_db->entries = entries; 453 + priv->rssi_db->entry_size = sizeof(*entry); 454 + priv->rssi_db->len = db_len; 455 + 456 + entry = (void *)((unsigned long)priv->rssi_db->data + priv->rssi_db->offset); 457 + if (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) { 458 + struct pda_rssi_cal_ext_entry *cal = (void *) &data[offset]; 459 + 460 + for (i = 0; i < entries; i++) { 461 + entry[i].freq = le16_to_cpu(cal[i].freq); 462 + entry[i].mul = (s16) le16_to_cpu(cal[i].mul); 463 + entry[i].add = (s16) le16_to_cpu(cal[i].add); 464 + } 465 + } else { 466 + struct pda_rssi_cal_entry *cal = (void *) &data[offset]; 467 + 468 + for (i = 0; i < entries; i++) { 469 + u16 freq; 470 + switch (i) { 471 + case IEEE80211_BAND_2GHZ: 472 + freq = 2437; 473 + break; 474 + case IEEE80211_BAND_5GHZ: 475 + freq = 5240; 476 + break; 477 + } 478 + 479 + entry[i].freq = freq; 480 + entry[i].mul = (s16) le16_to_cpu(cal[i].mul); 481 + entry[i].add = (s16) le16_to_cpu(cal[i].add); 482 + } 465 483 } 484 + 485 + /* sort the list by channel frequency */ 486 + sort(entry, entries, sizeof(*entry), p54_compare_rssichan, NULL); 487 + return 0; 488 + 489 + err_data: 490 + wiphy_err(dev->wiphy, 491 + "rssi calibration data packing type:(%x) len:%d.\n", 492 + type, len); 493 + 494 + print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE, data, len); 495 + 496 + wiphy_err(dev->wiphy, "please report this issue.\n"); 497 + return -EINVAL; 498 + } 499 + 500 + struct p54_rssi_db_entry *p54_rssi_find(struct p54_common *priv, const u16 freq) 501 + { 502 + struct p54_rssi_db_entry *entry = (void *)(priv->rssi_db->data + 503 + priv->rssi_db->offset); 504 + int i, found = -1; 505 + 506 + for (i = 0; i < priv->rssi_db->entries; i++) { 507 + if (!same_band(freq, entry[i].freq)) 508 + continue; 509 + 510 + if (found == -1) { 511 + found = i; 512 + continue; 513 + } 514 + 515 + /* nearest match */ 516 + if (abs(freq - entry[i].freq) < 517 + abs(freq - entry[found].freq)) { 518 + found = i; 519 + continue; 520 + } else { 521 + break; 522 + } 523 + } 524 + 525 + return found < 0 ? &p54_rssi_default : &entry[found]; 466 526 } 467 527 468 528 static void p54_parse_default_country(struct ieee80211_hw *dev, ··· 738 628 case PDR_RSSI_LINEAR_APPROXIMATION: 739 629 case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND: 740 630 case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED: 741 - p54_parse_rssical(dev, entry->data, data_len, 742 - le16_to_cpu(entry->code)); 631 + err = p54_parse_rssical(dev, entry->data, data_len, 632 + le16_to_cpu(entry->code)); 633 + if (err) 634 + goto err; 743 635 break; 744 - case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM: { 745 - __le16 *src = (void *) entry->data; 746 - s16 *dst = (void *) &priv->rssical_db; 636 + case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2: { 637 + struct pda_custom_wrapper *pda = (void *) entry->data; 638 + __le16 *src; 639 + u16 *dst; 747 640 int i; 748 641 749 - if (data_len != sizeof(priv->rssical_db)) { 750 - err = -EINVAL; 751 - goto err; 752 - } 753 - for (i = 0; i < sizeof(priv->rssical_db) / 754 - sizeof(*src); i++) 642 + if (priv->rssi_db || data_len < sizeof(*pda)) 643 + break; 644 + 645 + priv->rssi_db = p54_convert_db(pda, data_len); 646 + if (!priv->rssi_db) 647 + break; 648 + 649 + src = (void *) priv->rssi_db->data; 650 + dst = (void *) priv->rssi_db->data; 651 + 652 + for (i = 0; i < priv->rssi_db->entries; i++) 755 653 *(dst++) = (s16) le16_to_cpu(*(src++)); 654 + 756 655 } 757 656 break; 758 657 case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: { ··· 837 718 SET_IEEE80211_PERM_ADDR(dev, perm_addr); 838 719 } 839 720 721 + priv->cur_rssi = &p54_rssi_default; 722 + 840 723 wiphy_info(dev->wiphy, "hwaddr %pM, MAC:isl38%02x RF:%s\n", 841 724 dev->wiphy->perm_addr, priv->version, 842 725 p54_rf_chips[priv->rxhw]); ··· 849 728 kfree(priv->iq_autocal); 850 729 kfree(priv->output_limit); 851 730 kfree(priv->curve_data); 731 + kfree(priv->rssi_db); 852 732 priv->iq_autocal = NULL; 853 733 priv->output_limit = NULL; 854 734 priv->curve_data = NULL; 735 + priv->rssi_db = NULL; 855 736 856 737 wiphy_err(dev->wiphy, "eeprom parse failed!\n"); 857 738 return err;
+7
drivers/net/wireless/p54/eeprom.h
··· 81 81 u8 data[0]; 82 82 } __packed; 83 83 84 + struct pda_rssi_cal_ext_entry { 85 + __le16 freq; 86 + __le16 mul; 87 + __le16 add; 88 + } __packed; 89 + 84 90 struct pda_rssi_cal_entry { 85 91 __le16 mul; 86 92 __le16 add; ··· 185 179 186 180 /* used by our modificated eeprom image */ 187 181 #define PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM 0xDEAD 182 + #define PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2 0xCAFF 188 183 #define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM 0xBEEF 189 184 #define PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM 0xB05D 190 185
+7 -5
drivers/net/wireless/p54/fwio.c
··· 397 397 union p54_scan_body_union *body; 398 398 struct p54_scan_tail_rate *rate; 399 399 struct pda_rssi_cal_entry *rssi; 400 + struct p54_rssi_db_entry *rssi_data; 400 401 unsigned int i; 401 402 void *entry; 402 - int band = priv->hw->conf.channel->band; 403 403 __le16 freq = cpu_to_le16(priv->hw->conf.channel->center_freq); 404 404 405 405 skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*head) + ··· 503 503 } 504 504 505 505 rssi = (struct pda_rssi_cal_entry *) skb_put(skb, sizeof(*rssi)); 506 - rssi->mul = cpu_to_le16(priv->rssical_db[band].mul); 507 - rssi->add = cpu_to_le16(priv->rssical_db[band].add); 506 + rssi_data = p54_rssi_find(priv, le16_to_cpu(freq)); 507 + rssi->mul = cpu_to_le16(rssi_data->mul); 508 + rssi->add = cpu_to_le16(rssi_data->add); 508 509 if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { 509 510 /* Longbow frontend needs ever more */ 510 511 rssi = (void *) skb_put(skb, sizeof(*rssi)); 511 - rssi->mul = cpu_to_le16(priv->rssical_db[band].longbow_unkn); 512 - rssi->add = cpu_to_le16(priv->rssical_db[band].longbow_unk2); 512 + rssi->mul = cpu_to_le16(rssi_data->longbow_unkn); 513 + rssi->add = cpu_to_le16(rssi_data->longbow_unk2); 513 514 } 514 515 515 516 if (priv->fw_var >= 0x509) { ··· 524 523 hdr->len = cpu_to_le16(skb->len - sizeof(*hdr)); 525 524 526 525 p54_tx(priv, skb); 526 + priv->cur_rssi = rssi_data; 527 527 return 0; 528 528 529 529 err:
+1
drivers/net/wireless/p54/lmac.h
··· 551 551 /* eeprom */ 552 552 int p54_download_eeprom(struct p54_common *priv, void *buf, 553 553 u16 offset, u16 len); 554 + struct p54_rssi_db_entry *p54_rssi_find(struct p54_common *p, const u16 freq); 554 555 555 556 /* utility */ 556 557 u8 *p54_find_ie(struct sk_buff *skb, u8 ie);
+2
drivers/net/wireless/p54/main.c
··· 642 642 kfree(priv->iq_autocal); 643 643 kfree(priv->output_limit); 644 644 kfree(priv->curve_data); 645 + kfree(priv->rssi_db); 645 646 kfree(priv->used_rxkeys); 646 647 priv->iq_autocal = NULL; 647 648 priv->output_limit = NULL; 648 649 priv->curve_data = NULL; 650 + priv->rssi_db = NULL; 649 651 priv->used_rxkeys = NULL; 650 652 ieee80211_free_hw(dev); 651 653 }
+4 -2
drivers/net/wireless/p54/p54.h
··· 116 116 __le16 txop; 117 117 } __packed; 118 118 119 - struct p54_rssi_linear_approximation { 119 + struct p54_rssi_db_entry { 120 + u16 freq; 120 121 s16 mul; 121 122 s16 add; 122 123 s16 longbow_unkn; ··· 198 197 u8 rx_diversity_mask; 199 198 u8 tx_diversity_mask; 200 199 unsigned int output_power; 200 + struct p54_rssi_db_entry *cur_rssi; 201 201 int noise; 202 202 /* calibration, output power limit and rssi<->dBm conversation data */ 203 203 struct pda_iq_autocal_entry *iq_autocal; 204 204 unsigned int iq_autocal_len; 205 205 struct p54_cal_database *curve_data; 206 206 struct p54_cal_database *output_limit; 207 - struct p54_rssi_linear_approximation rssical_db[IEEE80211_NUM_BANDS]; 207 + struct p54_cal_database *rssi_db; 208 208 struct ieee80211_supported_band *band_table[IEEE80211_NUM_BANDS]; 209 209 210 210 /* BBP/MAC state */
+2 -4
drivers/net/wireless/p54/txrx.c
··· 273 273 274 274 static int p54_rssi_to_dbm(struct p54_common *priv, int rssi) 275 275 { 276 - int band = priv->hw->conf.channel->band; 277 - 278 276 if (priv->rxhw != 5) { 279 - return ((rssi * priv->rssical_db[band].mul) / 64 + 280 - priv->rssical_db[band].add) / 4; 277 + return ((rssi * priv->cur_rssi->mul) / 64 + 278 + priv->cur_rssi->add) / 4; 281 279 } else { 282 280 /* 283 281 * TODO: find the correct formula