162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * EEPROM parser code for mac80211 Prism54 drivers 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> 662306a36Sopenharmony_ci * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de> 762306a36Sopenharmony_ci * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Based on: 1062306a36Sopenharmony_ci * - the islsm (softmac prism54) driver, which is: 1162306a36Sopenharmony_ci * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. 1262306a36Sopenharmony_ci * - stlc45xx driver 1362306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/firmware.h> 1762306a36Sopenharmony_ci#include <linux/etherdevice.h> 1862306a36Sopenharmony_ci#include <linux/sort.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <net/mac80211.h> 2262306a36Sopenharmony_ci#include <linux/crc-ccitt.h> 2362306a36Sopenharmony_ci#include <linux/export.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "p54.h" 2662306a36Sopenharmony_ci#include "eeprom.h" 2762306a36Sopenharmony_ci#include "lmac.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic struct ieee80211_rate p54_bgrates[] = { 3062306a36Sopenharmony_ci { .bitrate = 10, .hw_value = 0, }, 3162306a36Sopenharmony_ci { .bitrate = 20, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 3262306a36Sopenharmony_ci { .bitrate = 55, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 3362306a36Sopenharmony_ci { .bitrate = 110, .hw_value = 3, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 3462306a36Sopenharmony_ci { .bitrate = 60, .hw_value = 4, }, 3562306a36Sopenharmony_ci { .bitrate = 90, .hw_value = 5, }, 3662306a36Sopenharmony_ci { .bitrate = 120, .hw_value = 6, }, 3762306a36Sopenharmony_ci { .bitrate = 180, .hw_value = 7, }, 3862306a36Sopenharmony_ci { .bitrate = 240, .hw_value = 8, }, 3962306a36Sopenharmony_ci { .bitrate = 360, .hw_value = 9, }, 4062306a36Sopenharmony_ci { .bitrate = 480, .hw_value = 10, }, 4162306a36Sopenharmony_ci { .bitrate = 540, .hw_value = 11, }, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic struct ieee80211_rate p54_arates[] = { 4562306a36Sopenharmony_ci { .bitrate = 60, .hw_value = 4, }, 4662306a36Sopenharmony_ci { .bitrate = 90, .hw_value = 5, }, 4762306a36Sopenharmony_ci { .bitrate = 120, .hw_value = 6, }, 4862306a36Sopenharmony_ci { .bitrate = 180, .hw_value = 7, }, 4962306a36Sopenharmony_ci { .bitrate = 240, .hw_value = 8, }, 5062306a36Sopenharmony_ci { .bitrate = 360, .hw_value = 9, }, 5162306a36Sopenharmony_ci { .bitrate = 480, .hw_value = 10, }, 5262306a36Sopenharmony_ci { .bitrate = 540, .hw_value = 11, }, 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic struct p54_rssi_db_entry p54_rssi_default = { 5662306a36Sopenharmony_ci /* 5762306a36Sopenharmony_ci * The defaults are taken from usb-logs of the 5862306a36Sopenharmony_ci * vendor driver. So, they should be safe to 5962306a36Sopenharmony_ci * use in case we can't get a match from the 6062306a36Sopenharmony_ci * rssi <-> dBm conversion database. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci .mul = 130, 6362306a36Sopenharmony_ci .add = -398, 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define CHAN_HAS_CAL BIT(0) 6762306a36Sopenharmony_ci#define CHAN_HAS_LIMIT BIT(1) 6862306a36Sopenharmony_ci#define CHAN_HAS_CURVE BIT(2) 6962306a36Sopenharmony_ci#define CHAN_HAS_ALL (CHAN_HAS_CAL | CHAN_HAS_LIMIT | CHAN_HAS_CURVE) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistruct p54_channel_entry { 7262306a36Sopenharmony_ci u16 freq; 7362306a36Sopenharmony_ci u16 data; 7462306a36Sopenharmony_ci int index; 7562306a36Sopenharmony_ci int max_power; 7662306a36Sopenharmony_ci enum nl80211_band band; 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistruct p54_channel_list { 8062306a36Sopenharmony_ci struct p54_channel_entry *channels; 8162306a36Sopenharmony_ci size_t entries; 8262306a36Sopenharmony_ci size_t max_entries; 8362306a36Sopenharmony_ci size_t band_channel_num[NUM_NL80211_BANDS]; 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int p54_get_band_from_freq(u16 freq) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci /* FIXME: sync these values with the 802.11 spec */ 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if ((freq >= 2412) && (freq <= 2484)) 9162306a36Sopenharmony_ci return NL80211_BAND_2GHZ; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if ((freq >= 4920) && (freq <= 5825)) 9462306a36Sopenharmony_ci return NL80211_BAND_5GHZ; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return -1; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int same_band(u16 freq, u16 freq2) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci return p54_get_band_from_freq(freq) == p54_get_band_from_freq(freq2); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int p54_compare_channels(const void *_a, 10562306a36Sopenharmony_ci const void *_b) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci const struct p54_channel_entry *a = _a; 10862306a36Sopenharmony_ci const struct p54_channel_entry *b = _b; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return a->freq - b->freq; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int p54_compare_rssichan(const void *_a, 11462306a36Sopenharmony_ci const void *_b) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci const struct p54_rssi_db_entry *a = _a; 11762306a36Sopenharmony_ci const struct p54_rssi_db_entry *b = _b; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return a->freq - b->freq; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int p54_fill_band_bitrates(struct ieee80211_hw *dev, 12362306a36Sopenharmony_ci struct ieee80211_supported_band *band_entry, 12462306a36Sopenharmony_ci enum nl80211_band band) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci /* TODO: generate rate array dynamically */ 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci switch (band) { 12962306a36Sopenharmony_ci case NL80211_BAND_2GHZ: 13062306a36Sopenharmony_ci band_entry->bitrates = p54_bgrates; 13162306a36Sopenharmony_ci band_entry->n_bitrates = ARRAY_SIZE(p54_bgrates); 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci case NL80211_BAND_5GHZ: 13462306a36Sopenharmony_ci band_entry->bitrates = p54_arates; 13562306a36Sopenharmony_ci band_entry->n_bitrates = ARRAY_SIZE(p54_arates); 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci default: 13862306a36Sopenharmony_ci return -EINVAL; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int p54_generate_band(struct ieee80211_hw *dev, 14562306a36Sopenharmony_ci struct p54_channel_list *list, 14662306a36Sopenharmony_ci unsigned int *chan_num, 14762306a36Sopenharmony_ci enum nl80211_band band) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct p54_common *priv = dev->priv; 15062306a36Sopenharmony_ci struct ieee80211_supported_band *tmp, *old; 15162306a36Sopenharmony_ci unsigned int i, j; 15262306a36Sopenharmony_ci int ret = -ENOMEM; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if ((!list->entries) || (!list->band_channel_num[band])) 15562306a36Sopenharmony_ci return -EINVAL; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); 15862306a36Sopenharmony_ci if (!tmp) 15962306a36Sopenharmony_ci goto err_out; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci tmp->channels = kcalloc(list->band_channel_num[band], 16262306a36Sopenharmony_ci sizeof(struct ieee80211_channel), 16362306a36Sopenharmony_ci GFP_KERNEL); 16462306a36Sopenharmony_ci if (!tmp->channels) 16562306a36Sopenharmony_ci goto err_out; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci ret = p54_fill_band_bitrates(dev, tmp, band); 16862306a36Sopenharmony_ci if (ret) 16962306a36Sopenharmony_ci goto err_out; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci for (i = 0, j = 0; (j < list->band_channel_num[band]) && 17262306a36Sopenharmony_ci (i < list->entries); i++) { 17362306a36Sopenharmony_ci struct p54_channel_entry *chan = &list->channels[i]; 17462306a36Sopenharmony_ci struct ieee80211_channel *dest = &tmp->channels[j]; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (chan->band != band) 17762306a36Sopenharmony_ci continue; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (chan->data != CHAN_HAS_ALL) { 18062306a36Sopenharmony_ci wiphy_err(dev->wiphy, "%s%s%s is/are missing for " 18162306a36Sopenharmony_ci "channel:%d [%d MHz].\n", 18262306a36Sopenharmony_ci (chan->data & CHAN_HAS_CAL ? "" : 18362306a36Sopenharmony_ci " [iqauto calibration data]"), 18462306a36Sopenharmony_ci (chan->data & CHAN_HAS_LIMIT ? "" : 18562306a36Sopenharmony_ci " [output power limits]"), 18662306a36Sopenharmony_ci (chan->data & CHAN_HAS_CURVE ? "" : 18762306a36Sopenharmony_ci " [curve data]"), 18862306a36Sopenharmony_ci chan->index, chan->freq); 18962306a36Sopenharmony_ci continue; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci dest->band = chan->band; 19362306a36Sopenharmony_ci dest->center_freq = chan->freq; 19462306a36Sopenharmony_ci dest->max_power = chan->max_power; 19562306a36Sopenharmony_ci priv->survey[*chan_num].channel = &tmp->channels[j]; 19662306a36Sopenharmony_ci priv->survey[*chan_num].filled = SURVEY_INFO_NOISE_DBM | 19762306a36Sopenharmony_ci SURVEY_INFO_TIME | 19862306a36Sopenharmony_ci SURVEY_INFO_TIME_BUSY | 19962306a36Sopenharmony_ci SURVEY_INFO_TIME_TX; 20062306a36Sopenharmony_ci dest->hw_value = (*chan_num); 20162306a36Sopenharmony_ci j++; 20262306a36Sopenharmony_ci (*chan_num)++; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (j == 0) { 20662306a36Sopenharmony_ci wiphy_err(dev->wiphy, "Disabling totally damaged %d GHz band\n", 20762306a36Sopenharmony_ci (band == NL80211_BAND_2GHZ) ? 2 : 5); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci ret = -ENODATA; 21062306a36Sopenharmony_ci goto err_out; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci tmp->n_channels = j; 21462306a36Sopenharmony_ci old = priv->band_table[band]; 21562306a36Sopenharmony_ci priv->band_table[band] = tmp; 21662306a36Sopenharmony_ci if (old) { 21762306a36Sopenharmony_ci kfree(old->channels); 21862306a36Sopenharmony_ci kfree(old); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cierr_out: 22462306a36Sopenharmony_ci if (tmp) { 22562306a36Sopenharmony_ci kfree(tmp->channels); 22662306a36Sopenharmony_ci kfree(tmp); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return ret; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic struct p54_channel_entry *p54_update_channel_param(struct p54_channel_list *list, 23362306a36Sopenharmony_ci u16 freq, u16 data) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci int i; 23662306a36Sopenharmony_ci struct p54_channel_entry *entry = NULL; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* 23962306a36Sopenharmony_ci * usually all lists in the eeprom are mostly sorted. 24062306a36Sopenharmony_ci * so it's very likely that the entry we are looking for 24162306a36Sopenharmony_ci * is right at the end of the list 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci for (i = list->entries; i >= 0; i--) { 24462306a36Sopenharmony_ci if (freq == list->channels[i].freq) { 24562306a36Sopenharmony_ci entry = &list->channels[i]; 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if ((i < 0) && (list->entries < list->max_entries)) { 25162306a36Sopenharmony_ci /* entry does not exist yet. Initialize a new one. */ 25262306a36Sopenharmony_ci int band = p54_get_band_from_freq(freq); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* 25562306a36Sopenharmony_ci * filter out frequencies which don't belong into 25662306a36Sopenharmony_ci * any supported band. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci if (band >= 0) { 25962306a36Sopenharmony_ci i = list->entries++; 26062306a36Sopenharmony_ci list->band_channel_num[band]++; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci entry = &list->channels[i]; 26362306a36Sopenharmony_ci entry->freq = freq; 26462306a36Sopenharmony_ci entry->band = band; 26562306a36Sopenharmony_ci entry->index = ieee80211_frequency_to_channel(freq); 26662306a36Sopenharmony_ci entry->max_power = 0; 26762306a36Sopenharmony_ci entry->data = 0; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (entry) 27262306a36Sopenharmony_ci entry->data |= data; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return entry; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic int p54_get_maxpower(struct p54_common *priv, void *data) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci switch (priv->rxhw & PDR_SYNTH_FRONTEND_MASK) { 28062306a36Sopenharmony_ci case PDR_SYNTH_FRONTEND_LONGBOW: { 28162306a36Sopenharmony_ci struct pda_channel_output_limit_longbow *pda = data; 28262306a36Sopenharmony_ci int j; 28362306a36Sopenharmony_ci u16 rawpower = 0; 28462306a36Sopenharmony_ci pda = data; 28562306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(pda->point); j++) { 28662306a36Sopenharmony_ci struct pda_channel_output_limit_point_longbow *point = 28762306a36Sopenharmony_ci &pda->point[j]; 28862306a36Sopenharmony_ci rawpower = max_t(u16, 28962306a36Sopenharmony_ci rawpower, le16_to_cpu(point->val_qpsk)); 29062306a36Sopenharmony_ci rawpower = max_t(u16, 29162306a36Sopenharmony_ci rawpower, le16_to_cpu(point->val_bpsk)); 29262306a36Sopenharmony_ci rawpower = max_t(u16, 29362306a36Sopenharmony_ci rawpower, le16_to_cpu(point->val_16qam)); 29462306a36Sopenharmony_ci rawpower = max_t(u16, 29562306a36Sopenharmony_ci rawpower, le16_to_cpu(point->val_64qam)); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci /* longbow seems to use 1/16 dBm units */ 29862306a36Sopenharmony_ci return rawpower / 16; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci case PDR_SYNTH_FRONTEND_DUETTE3: 30262306a36Sopenharmony_ci case PDR_SYNTH_FRONTEND_DUETTE2: 30362306a36Sopenharmony_ci case PDR_SYNTH_FRONTEND_FRISBEE: 30462306a36Sopenharmony_ci case PDR_SYNTH_FRONTEND_XBOW: { 30562306a36Sopenharmony_ci struct pda_channel_output_limit *pda = data; 30662306a36Sopenharmony_ci u8 rawpower = 0; 30762306a36Sopenharmony_ci rawpower = max(rawpower, pda->val_qpsk); 30862306a36Sopenharmony_ci rawpower = max(rawpower, pda->val_bpsk); 30962306a36Sopenharmony_ci rawpower = max(rawpower, pda->val_16qam); 31062306a36Sopenharmony_ci rawpower = max(rawpower, pda->val_64qam); 31162306a36Sopenharmony_ci /* raw values are in 1/4 dBm units */ 31262306a36Sopenharmony_ci return rawpower / 4; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci default: 31662306a36Sopenharmony_ci return 20; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int p54_generate_channel_lists(struct ieee80211_hw *dev) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct p54_common *priv = dev->priv; 32362306a36Sopenharmony_ci struct p54_channel_list *list; 32462306a36Sopenharmony_ci unsigned int i, j, k, max_channel_num; 32562306a36Sopenharmony_ci int ret = 0; 32662306a36Sopenharmony_ci u16 freq; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if ((priv->iq_autocal_len != priv->curve_data->entries) || 32962306a36Sopenharmony_ci (priv->iq_autocal_len != priv->output_limit->entries)) 33062306a36Sopenharmony_ci wiphy_err(dev->wiphy, 33162306a36Sopenharmony_ci "Unsupported or damaged EEPROM detected. " 33262306a36Sopenharmony_ci "You may not be able to use all channels.\n"); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci max_channel_num = max_t(unsigned int, priv->output_limit->entries, 33562306a36Sopenharmony_ci priv->iq_autocal_len); 33662306a36Sopenharmony_ci max_channel_num = max_t(unsigned int, max_channel_num, 33762306a36Sopenharmony_ci priv->curve_data->entries); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci list = kzalloc(sizeof(*list), GFP_KERNEL); 34062306a36Sopenharmony_ci if (!list) { 34162306a36Sopenharmony_ci ret = -ENOMEM; 34262306a36Sopenharmony_ci goto free; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci priv->chan_num = max_channel_num; 34562306a36Sopenharmony_ci priv->survey = kcalloc(max_channel_num, sizeof(struct survey_info), 34662306a36Sopenharmony_ci GFP_KERNEL); 34762306a36Sopenharmony_ci if (!priv->survey) { 34862306a36Sopenharmony_ci ret = -ENOMEM; 34962306a36Sopenharmony_ci goto free; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci list->max_entries = max_channel_num; 35362306a36Sopenharmony_ci list->channels = kcalloc(max_channel_num, 35462306a36Sopenharmony_ci sizeof(struct p54_channel_entry), 35562306a36Sopenharmony_ci GFP_KERNEL); 35662306a36Sopenharmony_ci if (!list->channels) { 35762306a36Sopenharmony_ci ret = -ENOMEM; 35862306a36Sopenharmony_ci goto free; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci for (i = 0; i < max_channel_num; i++) { 36262306a36Sopenharmony_ci if (i < priv->iq_autocal_len) { 36362306a36Sopenharmony_ci freq = le16_to_cpu(priv->iq_autocal[i].freq); 36462306a36Sopenharmony_ci p54_update_channel_param(list, freq, CHAN_HAS_CAL); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (i < priv->output_limit->entries) { 36862306a36Sopenharmony_ci struct p54_channel_entry *tmp; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci void *data = (void *) ((unsigned long) i * 37162306a36Sopenharmony_ci priv->output_limit->entry_size + 37262306a36Sopenharmony_ci priv->output_limit->offset + 37362306a36Sopenharmony_ci priv->output_limit->data); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci freq = le16_to_cpup((__le16 *) data); 37662306a36Sopenharmony_ci tmp = p54_update_channel_param(list, freq, 37762306a36Sopenharmony_ci CHAN_HAS_LIMIT); 37862306a36Sopenharmony_ci if (tmp) { 37962306a36Sopenharmony_ci tmp->max_power = p54_get_maxpower(priv, data); 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (i < priv->curve_data->entries) { 38462306a36Sopenharmony_ci freq = le16_to_cpup((__le16 *) (i * 38562306a36Sopenharmony_ci priv->curve_data->entry_size + 38662306a36Sopenharmony_ci priv->curve_data->offset + 38762306a36Sopenharmony_ci priv->curve_data->data)); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci p54_update_channel_param(list, freq, CHAN_HAS_CURVE); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* sort the channel list by frequency */ 39462306a36Sopenharmony_ci sort(list->channels, list->entries, sizeof(struct p54_channel_entry), 39562306a36Sopenharmony_ci p54_compare_channels, NULL); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci k = 0; 39862306a36Sopenharmony_ci for (i = 0, j = 0; i < NUM_NL80211_BANDS; i++) { 39962306a36Sopenharmony_ci if (p54_generate_band(dev, list, &k, i) == 0) 40062306a36Sopenharmony_ci j++; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci if (j == 0) { 40362306a36Sopenharmony_ci /* no useable band available. */ 40462306a36Sopenharmony_ci ret = -EINVAL; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cifree: 40862306a36Sopenharmony_ci if (list) { 40962306a36Sopenharmony_ci kfree(list->channels); 41062306a36Sopenharmony_ci kfree(list); 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci if (ret) { 41362306a36Sopenharmony_ci kfree(priv->survey); 41462306a36Sopenharmony_ci priv->survey = NULL; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return ret; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic int p54_convert_rev0(struct ieee80211_hw *dev, 42162306a36Sopenharmony_ci struct pda_pa_curve_data *curve_data) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct p54_common *priv = dev->priv; 42462306a36Sopenharmony_ci struct p54_pa_curve_data_sample *dst; 42562306a36Sopenharmony_ci struct pda_pa_curve_data_sample_rev0 *src; 42662306a36Sopenharmony_ci size_t cd_len = sizeof(*curve_data) + 42762306a36Sopenharmony_ci (curve_data->points_per_channel*sizeof(*dst) + 2) * 42862306a36Sopenharmony_ci curve_data->channels; 42962306a36Sopenharmony_ci unsigned int i, j; 43062306a36Sopenharmony_ci void *source, *target; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci priv->curve_data = kmalloc(sizeof(*priv->curve_data) + cd_len, 43362306a36Sopenharmony_ci GFP_KERNEL); 43462306a36Sopenharmony_ci if (!priv->curve_data) 43562306a36Sopenharmony_ci return -ENOMEM; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci priv->curve_data->entries = curve_data->channels; 43862306a36Sopenharmony_ci priv->curve_data->entry_size = sizeof(__le16) + 43962306a36Sopenharmony_ci sizeof(*dst) * curve_data->points_per_channel; 44062306a36Sopenharmony_ci priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data); 44162306a36Sopenharmony_ci priv->curve_data->len = cd_len; 44262306a36Sopenharmony_ci memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data)); 44362306a36Sopenharmony_ci source = curve_data->data; 44462306a36Sopenharmony_ci target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data; 44562306a36Sopenharmony_ci for (i = 0; i < curve_data->channels; i++) { 44662306a36Sopenharmony_ci __le16 *freq = source; 44762306a36Sopenharmony_ci source += sizeof(__le16); 44862306a36Sopenharmony_ci *((__le16 *)target) = *freq; 44962306a36Sopenharmony_ci target += sizeof(__le16); 45062306a36Sopenharmony_ci for (j = 0; j < curve_data->points_per_channel; j++) { 45162306a36Sopenharmony_ci dst = target; 45262306a36Sopenharmony_ci src = source; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci dst->rf_power = src->rf_power; 45562306a36Sopenharmony_ci dst->pa_detector = src->pa_detector; 45662306a36Sopenharmony_ci dst->data_64qam = src->pcv; 45762306a36Sopenharmony_ci /* "invent" the points for the other modulations */ 45862306a36Sopenharmony_ci#define SUB(x, y) (u8)(((x) - (y)) > (x) ? 0 : (x) - (y)) 45962306a36Sopenharmony_ci dst->data_16qam = SUB(src->pcv, 12); 46062306a36Sopenharmony_ci dst->data_qpsk = SUB(dst->data_16qam, 12); 46162306a36Sopenharmony_ci dst->data_bpsk = SUB(dst->data_qpsk, 12); 46262306a36Sopenharmony_ci dst->data_barker = SUB(dst->data_bpsk, 14); 46362306a36Sopenharmony_ci#undef SUB 46462306a36Sopenharmony_ci target += sizeof(*dst); 46562306a36Sopenharmony_ci source += sizeof(*src); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return 0; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic int p54_convert_rev1(struct ieee80211_hw *dev, 47362306a36Sopenharmony_ci struct pda_pa_curve_data *curve_data) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct p54_common *priv = dev->priv; 47662306a36Sopenharmony_ci struct p54_pa_curve_data_sample *dst; 47762306a36Sopenharmony_ci struct pda_pa_curve_data_sample_rev1 *src; 47862306a36Sopenharmony_ci size_t cd_len = sizeof(*curve_data) + 47962306a36Sopenharmony_ci (curve_data->points_per_channel*sizeof(*dst) + 2) * 48062306a36Sopenharmony_ci curve_data->channels; 48162306a36Sopenharmony_ci unsigned int i, j; 48262306a36Sopenharmony_ci void *source, *target; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci priv->curve_data = kzalloc(cd_len + sizeof(*priv->curve_data), 48562306a36Sopenharmony_ci GFP_KERNEL); 48662306a36Sopenharmony_ci if (!priv->curve_data) 48762306a36Sopenharmony_ci return -ENOMEM; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci priv->curve_data->entries = curve_data->channels; 49062306a36Sopenharmony_ci priv->curve_data->entry_size = sizeof(__le16) + 49162306a36Sopenharmony_ci sizeof(*dst) * curve_data->points_per_channel; 49262306a36Sopenharmony_ci priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data); 49362306a36Sopenharmony_ci priv->curve_data->len = cd_len; 49462306a36Sopenharmony_ci memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data)); 49562306a36Sopenharmony_ci source = curve_data->data; 49662306a36Sopenharmony_ci target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data; 49762306a36Sopenharmony_ci for (i = 0; i < curve_data->channels; i++) { 49862306a36Sopenharmony_ci __le16 *freq = source; 49962306a36Sopenharmony_ci source += sizeof(__le16); 50062306a36Sopenharmony_ci *((__le16 *)target) = *freq; 50162306a36Sopenharmony_ci target += sizeof(__le16); 50262306a36Sopenharmony_ci for (j = 0; j < curve_data->points_per_channel; j++) { 50362306a36Sopenharmony_ci memcpy(target, source, sizeof(*src)); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci target += sizeof(*dst); 50662306a36Sopenharmony_ci source += sizeof(*src); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci source++; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return 0; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic const char *p54_rf_chips[] = { "INVALID-0", "Duette3", "Duette2", 51562306a36Sopenharmony_ci "Frisbee", "Xbow", "Longbow", "INVALID-6", "INVALID-7" }; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int p54_parse_rssical(struct ieee80211_hw *dev, 51862306a36Sopenharmony_ci u8 *data, int len, u16 type) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct p54_common *priv = dev->priv; 52162306a36Sopenharmony_ci struct p54_rssi_db_entry *entry; 52262306a36Sopenharmony_ci size_t db_len, entries; 52362306a36Sopenharmony_ci int offset = 0, i; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (type != PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) { 52662306a36Sopenharmony_ci entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2; 52762306a36Sopenharmony_ci if (len != sizeof(struct pda_rssi_cal_entry) * entries) { 52862306a36Sopenharmony_ci wiphy_err(dev->wiphy, "rssical size mismatch.\n"); 52962306a36Sopenharmony_ci goto err_data; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci } else { 53262306a36Sopenharmony_ci /* 53362306a36Sopenharmony_ci * Some devices (Dell 1450 USB, Xbow 5GHz card, etc...) 53462306a36Sopenharmony_ci * have an empty two byte header. 53562306a36Sopenharmony_ci */ 53662306a36Sopenharmony_ci if (*((__le16 *)&data[offset]) == cpu_to_le16(0)) 53762306a36Sopenharmony_ci offset += 2; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci entries = (len - offset) / 54062306a36Sopenharmony_ci sizeof(struct pda_rssi_cal_ext_entry); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (len < offset || 54362306a36Sopenharmony_ci (len - offset) % sizeof(struct pda_rssi_cal_ext_entry) || 54462306a36Sopenharmony_ci entries == 0) { 54562306a36Sopenharmony_ci wiphy_err(dev->wiphy, "invalid rssi database.\n"); 54662306a36Sopenharmony_ci goto err_data; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci db_len = sizeof(*entry) * entries; 55162306a36Sopenharmony_ci priv->rssi_db = kzalloc(db_len + sizeof(*priv->rssi_db), GFP_KERNEL); 55262306a36Sopenharmony_ci if (!priv->rssi_db) 55362306a36Sopenharmony_ci return -ENOMEM; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci priv->rssi_db->offset = 0; 55662306a36Sopenharmony_ci priv->rssi_db->entries = entries; 55762306a36Sopenharmony_ci priv->rssi_db->entry_size = sizeof(*entry); 55862306a36Sopenharmony_ci priv->rssi_db->len = db_len; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci entry = (void *)((unsigned long)priv->rssi_db->data + priv->rssi_db->offset); 56162306a36Sopenharmony_ci if (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) { 56262306a36Sopenharmony_ci struct pda_rssi_cal_ext_entry *cal = (void *) &data[offset]; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci for (i = 0; i < entries; i++) { 56562306a36Sopenharmony_ci entry[i].freq = le16_to_cpu(cal[i].freq); 56662306a36Sopenharmony_ci entry[i].mul = (s16) le16_to_cpu(cal[i].mul); 56762306a36Sopenharmony_ci entry[i].add = (s16) le16_to_cpu(cal[i].add); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci } else { 57062306a36Sopenharmony_ci struct pda_rssi_cal_entry *cal = (void *) &data[offset]; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci for (i = 0; i < entries; i++) { 57362306a36Sopenharmony_ci u16 freq = 0; 57462306a36Sopenharmony_ci switch (i) { 57562306a36Sopenharmony_ci case NL80211_BAND_2GHZ: 57662306a36Sopenharmony_ci freq = 2437; 57762306a36Sopenharmony_ci break; 57862306a36Sopenharmony_ci case NL80211_BAND_5GHZ: 57962306a36Sopenharmony_ci freq = 5240; 58062306a36Sopenharmony_ci break; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci entry[i].freq = freq; 58462306a36Sopenharmony_ci entry[i].mul = (s16) le16_to_cpu(cal[i].mul); 58562306a36Sopenharmony_ci entry[i].add = (s16) le16_to_cpu(cal[i].add); 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* sort the list by channel frequency */ 59062306a36Sopenharmony_ci sort(entry, entries, sizeof(*entry), p54_compare_rssichan, NULL); 59162306a36Sopenharmony_ci return 0; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cierr_data: 59462306a36Sopenharmony_ci wiphy_err(dev->wiphy, 59562306a36Sopenharmony_ci "rssi calibration data packing type:(%x) len:%d.\n", 59662306a36Sopenharmony_ci type, len); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE, data, len); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci wiphy_err(dev->wiphy, "please report this issue.\n"); 60162306a36Sopenharmony_ci return -EINVAL; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistruct p54_rssi_db_entry *p54_rssi_find(struct p54_common *priv, const u16 freq) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci struct p54_rssi_db_entry *entry; 60762306a36Sopenharmony_ci int i, found = -1; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (!priv->rssi_db) 61062306a36Sopenharmony_ci return &p54_rssi_default; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci entry = (void *)(priv->rssi_db->data + priv->rssi_db->offset); 61362306a36Sopenharmony_ci for (i = 0; i < priv->rssi_db->entries; i++) { 61462306a36Sopenharmony_ci if (!same_band(freq, entry[i].freq)) 61562306a36Sopenharmony_ci continue; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (found == -1) { 61862306a36Sopenharmony_ci found = i; 61962306a36Sopenharmony_ci continue; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* nearest match */ 62362306a36Sopenharmony_ci if (abs(freq - entry[i].freq) < 62462306a36Sopenharmony_ci abs(freq - entry[found].freq)) { 62562306a36Sopenharmony_ci found = i; 62662306a36Sopenharmony_ci continue; 62762306a36Sopenharmony_ci } else { 62862306a36Sopenharmony_ci break; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci return found < 0 ? &p54_rssi_default : &entry[found]; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic void p54_parse_default_country(struct ieee80211_hw *dev, 63662306a36Sopenharmony_ci void *data, int len) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct pda_country *country; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (len != sizeof(*country)) { 64162306a36Sopenharmony_ci wiphy_err(dev->wiphy, 64262306a36Sopenharmony_ci "found possible invalid default country eeprom entry. (entry size: %d)\n", 64362306a36Sopenharmony_ci len); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci print_hex_dump_bytes("country:", DUMP_PREFIX_NONE, 64662306a36Sopenharmony_ci data, len); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci wiphy_err(dev->wiphy, "please report this issue.\n"); 64962306a36Sopenharmony_ci return; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci country = (struct pda_country *) data; 65362306a36Sopenharmony_ci if (country->flags == PDR_COUNTRY_CERT_CODE_PSEUDO) 65462306a36Sopenharmony_ci regulatory_hint(dev->wiphy, country->alpha2); 65562306a36Sopenharmony_ci else { 65662306a36Sopenharmony_ci /* TODO: 65762306a36Sopenharmony_ci * write a shared/common function that converts 65862306a36Sopenharmony_ci * "Regulatory domain codes" (802.11-2007 14.8.2.2) 65962306a36Sopenharmony_ci * into ISO/IEC 3166-1 alpha2 for regulatory_hint. 66062306a36Sopenharmony_ci */ 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic int p54_convert_output_limits(struct ieee80211_hw *dev, 66562306a36Sopenharmony_ci u8 *data, size_t len) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct p54_common *priv = dev->priv; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (len < 2) 67062306a36Sopenharmony_ci return -EINVAL; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (data[0] != 0) { 67362306a36Sopenharmony_ci wiphy_err(dev->wiphy, "unknown output power db revision:%x\n", 67462306a36Sopenharmony_ci data[0]); 67562306a36Sopenharmony_ci return -EINVAL; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (2 + data[1] * sizeof(struct pda_channel_output_limit) > len) 67962306a36Sopenharmony_ci return -EINVAL; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci priv->output_limit = kmalloc(data[1] * 68262306a36Sopenharmony_ci sizeof(struct pda_channel_output_limit) + 68362306a36Sopenharmony_ci sizeof(*priv->output_limit), GFP_KERNEL); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (!priv->output_limit) 68662306a36Sopenharmony_ci return -ENOMEM; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci priv->output_limit->offset = 0; 68962306a36Sopenharmony_ci priv->output_limit->entries = data[1]; 69062306a36Sopenharmony_ci priv->output_limit->entry_size = 69162306a36Sopenharmony_ci sizeof(struct pda_channel_output_limit); 69262306a36Sopenharmony_ci priv->output_limit->len = priv->output_limit->entry_size * 69362306a36Sopenharmony_ci priv->output_limit->entries + 69462306a36Sopenharmony_ci priv->output_limit->offset; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci memcpy(priv->output_limit->data, &data[2], 69762306a36Sopenharmony_ci data[1] * sizeof(struct pda_channel_output_limit)); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci return 0; 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistatic struct p54_cal_database *p54_convert_db(struct pda_custom_wrapper *src, 70362306a36Sopenharmony_ci size_t total_len) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci struct p54_cal_database *dst; 70662306a36Sopenharmony_ci size_t payload_len, entries, entry_size, offset; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci payload_len = le16_to_cpu(src->len); 70962306a36Sopenharmony_ci entries = le16_to_cpu(src->entries); 71062306a36Sopenharmony_ci entry_size = le16_to_cpu(src->entry_size); 71162306a36Sopenharmony_ci offset = le16_to_cpu(src->offset); 71262306a36Sopenharmony_ci if (((entries * entry_size + offset) != payload_len) || 71362306a36Sopenharmony_ci (payload_len + sizeof(*src) != total_len)) 71462306a36Sopenharmony_ci return NULL; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci dst = kmalloc(sizeof(*dst) + payload_len, GFP_KERNEL); 71762306a36Sopenharmony_ci if (!dst) 71862306a36Sopenharmony_ci return NULL; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci dst->entries = entries; 72162306a36Sopenharmony_ci dst->entry_size = entry_size; 72262306a36Sopenharmony_ci dst->offset = offset; 72362306a36Sopenharmony_ci dst->len = payload_len; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci memcpy(dst->data, src->data, payload_len); 72662306a36Sopenharmony_ci return dst; 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ciint p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci struct p54_common *priv = dev->priv; 73262306a36Sopenharmony_ci struct eeprom_pda_wrap *wrap; 73362306a36Sopenharmony_ci struct pda_entry *entry; 73462306a36Sopenharmony_ci unsigned int data_len, entry_len; 73562306a36Sopenharmony_ci void *tmp; 73662306a36Sopenharmony_ci int err; 73762306a36Sopenharmony_ci u8 *end = (u8 *)eeprom + len; 73862306a36Sopenharmony_ci u16 synth = 0; 73962306a36Sopenharmony_ci u16 crc16 = ~0; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci wrap = (struct eeprom_pda_wrap *) eeprom; 74262306a36Sopenharmony_ci entry = (void *)wrap->data + le16_to_cpu(wrap->len); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* verify that at least the entry length/code fits */ 74562306a36Sopenharmony_ci while ((u8 *)entry <= end - sizeof(*entry)) { 74662306a36Sopenharmony_ci entry_len = le16_to_cpu(entry->len); 74762306a36Sopenharmony_ci data_len = ((entry_len - 1) << 1); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* abort if entry exceeds whole structure */ 75062306a36Sopenharmony_ci if ((u8 *)entry + sizeof(*entry) + data_len > end) 75162306a36Sopenharmony_ci break; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci switch (le16_to_cpu(entry->code)) { 75462306a36Sopenharmony_ci case PDR_MAC_ADDRESS: 75562306a36Sopenharmony_ci if (data_len != ETH_ALEN) 75662306a36Sopenharmony_ci break; 75762306a36Sopenharmony_ci SET_IEEE80211_PERM_ADDR(dev, entry->data); 75862306a36Sopenharmony_ci break; 75962306a36Sopenharmony_ci case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS: 76062306a36Sopenharmony_ci if (priv->output_limit) 76162306a36Sopenharmony_ci break; 76262306a36Sopenharmony_ci err = p54_convert_output_limits(dev, entry->data, 76362306a36Sopenharmony_ci data_len); 76462306a36Sopenharmony_ci if (err) 76562306a36Sopenharmony_ci goto err; 76662306a36Sopenharmony_ci break; 76762306a36Sopenharmony_ci case PDR_PRISM_PA_CAL_CURVE_DATA: { 76862306a36Sopenharmony_ci struct pda_pa_curve_data *curve_data = 76962306a36Sopenharmony_ci (struct pda_pa_curve_data *)entry->data; 77062306a36Sopenharmony_ci if (data_len < sizeof(*curve_data)) { 77162306a36Sopenharmony_ci err = -EINVAL; 77262306a36Sopenharmony_ci goto err; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci switch (curve_data->cal_method_rev) { 77662306a36Sopenharmony_ci case 0: 77762306a36Sopenharmony_ci err = p54_convert_rev0(dev, curve_data); 77862306a36Sopenharmony_ci break; 77962306a36Sopenharmony_ci case 1: 78062306a36Sopenharmony_ci err = p54_convert_rev1(dev, curve_data); 78162306a36Sopenharmony_ci break; 78262306a36Sopenharmony_ci default: 78362306a36Sopenharmony_ci wiphy_err(dev->wiphy, 78462306a36Sopenharmony_ci "unknown curve data revision %d\n", 78562306a36Sopenharmony_ci curve_data->cal_method_rev); 78662306a36Sopenharmony_ci err = -ENODEV; 78762306a36Sopenharmony_ci break; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci if (err) 79062306a36Sopenharmony_ci goto err; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci break; 79362306a36Sopenharmony_ci case PDR_PRISM_ZIF_TX_IQ_CALIBRATION: 79462306a36Sopenharmony_ci priv->iq_autocal = kmemdup(entry->data, data_len, 79562306a36Sopenharmony_ci GFP_KERNEL); 79662306a36Sopenharmony_ci if (!priv->iq_autocal) { 79762306a36Sopenharmony_ci err = -ENOMEM; 79862306a36Sopenharmony_ci goto err; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci priv->iq_autocal_len = data_len / sizeof(struct pda_iq_autocal_entry); 80262306a36Sopenharmony_ci break; 80362306a36Sopenharmony_ci case PDR_DEFAULT_COUNTRY: 80462306a36Sopenharmony_ci p54_parse_default_country(dev, entry->data, data_len); 80562306a36Sopenharmony_ci break; 80662306a36Sopenharmony_ci case PDR_INTERFACE_LIST: 80762306a36Sopenharmony_ci tmp = entry->data; 80862306a36Sopenharmony_ci while ((u8 *)tmp < entry->data + data_len) { 80962306a36Sopenharmony_ci struct exp_if *exp_if = tmp; 81062306a36Sopenharmony_ci if (exp_if->if_id == cpu_to_le16(IF_ID_ISL39000)) 81162306a36Sopenharmony_ci synth = le16_to_cpu(exp_if->variant); 81262306a36Sopenharmony_ci tmp += sizeof(*exp_if); 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci break; 81562306a36Sopenharmony_ci case PDR_HARDWARE_PLATFORM_COMPONENT_ID: 81662306a36Sopenharmony_ci if (data_len < 2) 81762306a36Sopenharmony_ci break; 81862306a36Sopenharmony_ci priv->version = *(u8 *)(entry->data + 1); 81962306a36Sopenharmony_ci break; 82062306a36Sopenharmony_ci case PDR_RSSI_LINEAR_APPROXIMATION: 82162306a36Sopenharmony_ci case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND: 82262306a36Sopenharmony_ci case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED: 82362306a36Sopenharmony_ci err = p54_parse_rssical(dev, entry->data, data_len, 82462306a36Sopenharmony_ci le16_to_cpu(entry->code)); 82562306a36Sopenharmony_ci if (err) 82662306a36Sopenharmony_ci goto err; 82762306a36Sopenharmony_ci break; 82862306a36Sopenharmony_ci case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2: { 82962306a36Sopenharmony_ci struct pda_custom_wrapper *pda = (void *) entry->data; 83062306a36Sopenharmony_ci __le16 *src; 83162306a36Sopenharmony_ci u16 *dst; 83262306a36Sopenharmony_ci int i; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (priv->rssi_db || data_len < sizeof(*pda)) 83562306a36Sopenharmony_ci break; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci priv->rssi_db = p54_convert_db(pda, data_len); 83862306a36Sopenharmony_ci if (!priv->rssi_db) 83962306a36Sopenharmony_ci break; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci src = (void *) priv->rssi_db->data; 84262306a36Sopenharmony_ci dst = (void *) priv->rssi_db->data; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci for (i = 0; i < priv->rssi_db->entries; i++) 84562306a36Sopenharmony_ci *(dst++) = (s16) le16_to_cpu(*(src++)); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci break; 84962306a36Sopenharmony_ci case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: { 85062306a36Sopenharmony_ci struct pda_custom_wrapper *pda = (void *) entry->data; 85162306a36Sopenharmony_ci if (priv->output_limit || data_len < sizeof(*pda)) 85262306a36Sopenharmony_ci break; 85362306a36Sopenharmony_ci priv->output_limit = p54_convert_db(pda, data_len); 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci break; 85662306a36Sopenharmony_ci case PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM: { 85762306a36Sopenharmony_ci struct pda_custom_wrapper *pda = (void *) entry->data; 85862306a36Sopenharmony_ci if (priv->curve_data || data_len < sizeof(*pda)) 85962306a36Sopenharmony_ci break; 86062306a36Sopenharmony_ci priv->curve_data = p54_convert_db(pda, data_len); 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci case PDR_END: 86462306a36Sopenharmony_ci crc16 = ~crc_ccitt(crc16, (u8 *) entry, sizeof(*entry)); 86562306a36Sopenharmony_ci if (crc16 != le16_to_cpup((__le16 *)entry->data)) { 86662306a36Sopenharmony_ci wiphy_err(dev->wiphy, "eeprom failed checksum " 86762306a36Sopenharmony_ci "test!\n"); 86862306a36Sopenharmony_ci err = -ENOMSG; 86962306a36Sopenharmony_ci goto err; 87062306a36Sopenharmony_ci } else { 87162306a36Sopenharmony_ci goto good_eeprom; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci break; 87462306a36Sopenharmony_ci default: 87562306a36Sopenharmony_ci break; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci crc16 = crc_ccitt(crc16, (u8 *)entry, (entry_len + 1) * 2); 87962306a36Sopenharmony_ci entry = (void *)entry + (entry_len + 1) * 2; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci wiphy_err(dev->wiphy, "unexpected end of eeprom data.\n"); 88362306a36Sopenharmony_ci err = -ENODATA; 88462306a36Sopenharmony_ci goto err; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cigood_eeprom: 88762306a36Sopenharmony_ci if (!synth || !priv->iq_autocal || !priv->output_limit || 88862306a36Sopenharmony_ci !priv->curve_data) { 88962306a36Sopenharmony_ci wiphy_err(dev->wiphy, 89062306a36Sopenharmony_ci "not all required entries found in eeprom!\n"); 89162306a36Sopenharmony_ci err = -EINVAL; 89262306a36Sopenharmony_ci goto err; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci err = p54_generate_channel_lists(dev); 89862306a36Sopenharmony_ci if (err) 89962306a36Sopenharmony_ci goto err; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (priv->rxhw == PDR_SYNTH_FRONTEND_XBOW) 90262306a36Sopenharmony_ci p54_init_xbow_synth(priv); 90362306a36Sopenharmony_ci if (!(synth & PDR_SYNTH_24_GHZ_DISABLED)) 90462306a36Sopenharmony_ci dev->wiphy->bands[NL80211_BAND_2GHZ] = 90562306a36Sopenharmony_ci priv->band_table[NL80211_BAND_2GHZ]; 90662306a36Sopenharmony_ci if (!(synth & PDR_SYNTH_5_GHZ_DISABLED)) 90762306a36Sopenharmony_ci dev->wiphy->bands[NL80211_BAND_5GHZ] = 90862306a36Sopenharmony_ci priv->band_table[NL80211_BAND_5GHZ]; 90962306a36Sopenharmony_ci if ((synth & PDR_SYNTH_RX_DIV_MASK) == PDR_SYNTH_RX_DIV_SUPPORTED) 91062306a36Sopenharmony_ci priv->rx_diversity_mask = 3; 91162306a36Sopenharmony_ci if ((synth & PDR_SYNTH_TX_DIV_MASK) == PDR_SYNTH_TX_DIV_SUPPORTED) 91262306a36Sopenharmony_ci priv->tx_diversity_mask = 3; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (!is_valid_ether_addr(dev->wiphy->perm_addr)) { 91562306a36Sopenharmony_ci u8 perm_addr[ETH_ALEN]; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci wiphy_warn(dev->wiphy, 91862306a36Sopenharmony_ci "Invalid hwaddr! Using randomly generated MAC addr\n"); 91962306a36Sopenharmony_ci eth_random_addr(perm_addr); 92062306a36Sopenharmony_ci SET_IEEE80211_PERM_ADDR(dev, perm_addr); 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci priv->cur_rssi = &p54_rssi_default; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci wiphy_info(dev->wiphy, "hwaddr %pM, MAC:isl38%02x RF:%s\n", 92662306a36Sopenharmony_ci dev->wiphy->perm_addr, priv->version, 92762306a36Sopenharmony_ci p54_rf_chips[priv->rxhw]); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return 0; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cierr: 93262306a36Sopenharmony_ci kfree(priv->iq_autocal); 93362306a36Sopenharmony_ci kfree(priv->output_limit); 93462306a36Sopenharmony_ci kfree(priv->curve_data); 93562306a36Sopenharmony_ci kfree(priv->rssi_db); 93662306a36Sopenharmony_ci kfree(priv->survey); 93762306a36Sopenharmony_ci priv->iq_autocal = NULL; 93862306a36Sopenharmony_ci priv->output_limit = NULL; 93962306a36Sopenharmony_ci priv->curve_data = NULL; 94062306a36Sopenharmony_ci priv->rssi_db = NULL; 94162306a36Sopenharmony_ci priv->survey = NULL; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci wiphy_err(dev->wiphy, "eeprom parse failed!\n"); 94462306a36Sopenharmony_ci return err; 94562306a36Sopenharmony_ci} 94662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(p54_parse_eeprom); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ciint p54_read_eeprom(struct ieee80211_hw *dev) 94962306a36Sopenharmony_ci{ 95062306a36Sopenharmony_ci struct p54_common *priv = dev->priv; 95162306a36Sopenharmony_ci size_t eeprom_size = 0x2020, offset = 0, blocksize, maxblocksize; 95262306a36Sopenharmony_ci int ret = -ENOMEM; 95362306a36Sopenharmony_ci void *eeprom; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci maxblocksize = EEPROM_READBACK_LEN; 95662306a36Sopenharmony_ci if (priv->fw_var >= 0x509) 95762306a36Sopenharmony_ci maxblocksize -= 0xc; 95862306a36Sopenharmony_ci else 95962306a36Sopenharmony_ci maxblocksize -= 0x4; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci eeprom = kzalloc(eeprom_size, GFP_KERNEL); 96262306a36Sopenharmony_ci if (unlikely(!eeprom)) 96362306a36Sopenharmony_ci goto free; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci while (eeprom_size) { 96662306a36Sopenharmony_ci blocksize = min(eeprom_size, maxblocksize); 96762306a36Sopenharmony_ci ret = p54_download_eeprom(priv, eeprom + offset, 96862306a36Sopenharmony_ci offset, blocksize); 96962306a36Sopenharmony_ci if (unlikely(ret)) 97062306a36Sopenharmony_ci goto free; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci offset += blocksize; 97362306a36Sopenharmony_ci eeprom_size -= blocksize; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci ret = p54_parse_eeprom(dev, eeprom, offset); 97762306a36Sopenharmony_cifree: 97862306a36Sopenharmony_ci kfree(eeprom); 97962306a36Sopenharmony_ci return ret; 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(p54_read_eeprom); 982