diff options
author | Christian Lamparter <chunkeey@web.de> | 2009-07-10 19:22:26 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-07-24 15:05:12 -0400 |
commit | 1a9b6679adfb8ef1f1f3dbb7ebd2ee72e2ea4b56 (patch) | |
tree | 2cb7507ba929d9214ef84d76dca2fe93285f7a35 | |
parent | 596a07c18b35c9df2fb212856241ae0dfe3162b9 (diff) |
p54: generate channel list dynamically
This patch enhances the eeprom parser to generate customized
channel list for every device.
Signed-off-by: Christian Lamparter <chunkeey@web.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/p54/eeprom.c | 323 | ||||
-rw-r--r-- | drivers/net/wireless/p54/main.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/p54/p54.h | 1 |
3 files changed, 261 insertions, 67 deletions
diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c index a2a044ef101..549ef2d19cd 100644 --- a/drivers/net/wireless/p54/eeprom.c +++ b/drivers/net/wireless/p54/eeprom.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/init.h> | 19 | #include <linux/init.h> |
20 | #include <linux/firmware.h> | 20 | #include <linux/firmware.h> |
21 | #include <linux/etherdevice.h> | 21 | #include <linux/etherdevice.h> |
22 | #include <linux/sort.h> | ||
22 | 23 | ||
23 | #include <net/mac80211.h> | 24 | #include <net/mac80211.h> |
24 | 25 | ||
@@ -41,30 +42,6 @@ static struct ieee80211_rate p54_bgrates[] = { | |||
41 | { .bitrate = 540, .hw_value = 11, }, | 42 | { .bitrate = 540, .hw_value = 11, }, |
42 | }; | 43 | }; |
43 | 44 | ||
44 | static struct ieee80211_channel p54_bgchannels[] = { | ||
45 | { .center_freq = 2412, .hw_value = 1, }, | ||
46 | { .center_freq = 2417, .hw_value = 2, }, | ||
47 | { .center_freq = 2422, .hw_value = 3, }, | ||
48 | { .center_freq = 2427, .hw_value = 4, }, | ||
49 | { .center_freq = 2432, .hw_value = 5, }, | ||
50 | { .center_freq = 2437, .hw_value = 6, }, | ||
51 | { .center_freq = 2442, .hw_value = 7, }, | ||
52 | { .center_freq = 2447, .hw_value = 8, }, | ||
53 | { .center_freq = 2452, .hw_value = 9, }, | ||
54 | { .center_freq = 2457, .hw_value = 10, }, | ||
55 | { .center_freq = 2462, .hw_value = 11, }, | ||
56 | { .center_freq = 2467, .hw_value = 12, }, | ||
57 | { .center_freq = 2472, .hw_value = 13, }, | ||
58 | { .center_freq = 2484, .hw_value = 14, }, | ||
59 | }; | ||
60 | |||
61 | static struct ieee80211_supported_band band_2GHz = { | ||
62 | .channels = p54_bgchannels, | ||
63 | .n_channels = ARRAY_SIZE(p54_bgchannels), | ||
64 | .bitrates = p54_bgrates, | ||
65 | .n_bitrates = ARRAY_SIZE(p54_bgrates), | ||
66 | }; | ||
67 | |||
68 | static struct ieee80211_rate p54_arates[] = { | 45 | static struct ieee80211_rate p54_arates[] = { |
69 | { .bitrate = 60, .hw_value = 4, }, | 46 | { .bitrate = 60, .hw_value = 4, }, |
70 | { .bitrate = 90, .hw_value = 5, }, | 47 | { .bitrate = 90, .hw_value = 5, }, |
@@ -76,51 +53,257 @@ static struct ieee80211_rate p54_arates[] = { | |||
76 | { .bitrate = 540, .hw_value = 11, }, | 53 | { .bitrate = 540, .hw_value = 11, }, |
77 | }; | 54 | }; |
78 | 55 | ||
79 | static struct ieee80211_channel p54_achannels[] = { | 56 | #define CHAN_HAS_CAL BIT(0) |
80 | { .center_freq = 4920 }, | 57 | #define CHAN_HAS_LIMIT BIT(1) |
81 | { .center_freq = 4940 }, | 58 | #define CHAN_HAS_CURVE BIT(2) |
82 | { .center_freq = 4960 }, | 59 | #define CHAN_HAS_ALL (CHAN_HAS_CAL | CHAN_HAS_LIMIT | CHAN_HAS_CURVE) |
83 | { .center_freq = 4980 }, | 60 | |
84 | { .center_freq = 5040 }, | 61 | struct p54_channel_entry { |
85 | { .center_freq = 5060 }, | 62 | u16 freq; |
86 | { .center_freq = 5080 }, | 63 | u16 data; |
87 | { .center_freq = 5170 }, | 64 | int index; |
88 | { .center_freq = 5180 }, | 65 | enum ieee80211_band band; |
89 | { .center_freq = 5190 }, | ||
90 | { .center_freq = 5200 }, | ||
91 | { .center_freq = 5210 }, | ||
92 | { .center_freq = 5220 }, | ||
93 | { .center_freq = 5230 }, | ||
94 | { .center_freq = 5240 }, | ||
95 | { .center_freq = 5260 }, | ||
96 | { .center_freq = 5280 }, | ||
97 | { .center_freq = 5300 }, | ||
98 | { .center_freq = 5320 }, | ||
99 | { .center_freq = 5500 }, | ||
100 | { .center_freq = 5520 }, | ||
101 | { .center_freq = 5540 }, | ||
102 | { .center_freq = 5560 }, | ||
103 | { .center_freq = 5580 }, | ||
104 | { .center_freq = 5600 }, | ||
105 | { .center_freq = 5620 }, | ||
106 | { .center_freq = 5640 }, | ||
107 | { .center_freq = 5660 }, | ||
108 | { .center_freq = 5680 }, | ||
109 | { .center_freq = 5700 }, | ||
110 | { .center_freq = 5745 }, | ||
111 | { .center_freq = 5765 }, | ||
112 | { .center_freq = 5785 }, | ||
113 | { .center_freq = 5805 }, | ||
114 | { .center_freq = 5825 }, | ||
115 | }; | 66 | }; |
116 | 67 | ||
117 | static struct ieee80211_supported_band band_5GHz = { | 68 | struct p54_channel_list { |
118 | .channels = p54_achannels, | 69 | struct p54_channel_entry *channels; |
119 | .n_channels = ARRAY_SIZE(p54_achannels), | 70 | size_t entries; |
120 | .bitrates = p54_arates, | 71 | size_t max_entries; |
121 | .n_bitrates = ARRAY_SIZE(p54_arates), | 72 | size_t band_channel_num[IEEE80211_NUM_BANDS]; |
122 | }; | 73 | }; |
123 | 74 | ||
75 | static int p54_get_band_from_freq(u16 freq) | ||
76 | { | ||
77 | /* FIXME: sync these values with the 802.11 spec */ | ||
78 | |||
79 | if ((freq >= 2412) && (freq <= 2484)) | ||
80 | return IEEE80211_BAND_2GHZ; | ||
81 | |||
82 | if ((freq >= 4920) && (freq <= 5825)) | ||
83 | return IEEE80211_BAND_5GHZ; | ||
84 | |||
85 | return -1; | ||
86 | } | ||
87 | |||
88 | static int p54_compare_channels(const void *_a, | ||
89 | const void *_b) | ||
90 | { | ||
91 | const struct p54_channel_entry *a = _a; | ||
92 | const struct p54_channel_entry *b = _b; | ||
93 | |||
94 | return a->index - b->index; | ||
95 | } | ||
96 | |||
97 | static int p54_fill_band_bitrates(struct ieee80211_hw *dev, | ||
98 | struct ieee80211_supported_band *band_entry, | ||
99 | enum ieee80211_band band) | ||
100 | { | ||
101 | /* TODO: generate rate array dynamically */ | ||
102 | |||
103 | switch (band) { | ||
104 | case IEEE80211_BAND_2GHZ: | ||
105 | band_entry->bitrates = p54_bgrates; | ||
106 | band_entry->n_bitrates = ARRAY_SIZE(p54_bgrates); | ||
107 | break; | ||
108 | case IEEE80211_BAND_5GHZ: | ||
109 | band_entry->bitrates = p54_arates; | ||
110 | band_entry->n_bitrates = ARRAY_SIZE(p54_arates); | ||
111 | break; | ||
112 | default: | ||
113 | return -EINVAL; | ||
114 | } | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static int p54_generate_band(struct ieee80211_hw *dev, | ||
120 | struct p54_channel_list *list, | ||
121 | enum ieee80211_band band) | ||
122 | { | ||
123 | struct p54_common *priv = dev->priv; | ||
124 | struct ieee80211_supported_band *tmp, *old; | ||
125 | unsigned int i, j; | ||
126 | int ret = -ENOMEM; | ||
127 | |||
128 | if ((!list->entries) || (!list->band_channel_num[band])) | ||
129 | return 0; | ||
130 | |||
131 | tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); | ||
132 | if (!tmp) | ||
133 | goto err_out; | ||
134 | |||
135 | tmp->channels = kzalloc(sizeof(struct ieee80211_channel) * | ||
136 | list->band_channel_num[band], GFP_KERNEL); | ||
137 | if (!tmp->channels) | ||
138 | goto err_out; | ||
139 | |||
140 | ret = p54_fill_band_bitrates(dev, tmp, band); | ||
141 | if (ret) | ||
142 | goto err_out; | ||
143 | |||
144 | for (i = 0, j = 0; (j < list->band_channel_num[band]) && | ||
145 | (i < list->entries); i++) { | ||
146 | |||
147 | if (list->channels[i].band != band) | ||
148 | continue; | ||
149 | |||
150 | if (list->channels[i].data != CHAN_HAS_ALL) { | ||
151 | printk(KERN_ERR "%s:%s%s%s is/are missing for " | ||
152 | "channel:%d [%d MHz].\n", | ||
153 | wiphy_name(dev->wiphy), | ||
154 | (list->channels[i].data & CHAN_HAS_CAL ? "" : | ||
155 | " [iqauto calibration data]"), | ||
156 | (list->channels[i].data & CHAN_HAS_LIMIT ? "" : | ||
157 | " [output power limits]"), | ||
158 | (list->channels[i].data & CHAN_HAS_CURVE ? "" : | ||
159 | " [curve data]"), | ||
160 | list->channels[i].index, list->channels[i].freq); | ||
161 | } | ||
162 | |||
163 | tmp->channels[j].band = list->channels[i].band; | ||
164 | tmp->channels[j].center_freq = list->channels[i].freq; | ||
165 | j++; | ||
166 | } | ||
167 | |||
168 | tmp->n_channels = list->band_channel_num[band]; | ||
169 | old = priv->band_table[band]; | ||
170 | priv->band_table[band] = tmp; | ||
171 | if (old) { | ||
172 | kfree(old->channels); | ||
173 | kfree(old); | ||
174 | } | ||
175 | |||
176 | return 0; | ||
177 | |||
178 | err_out: | ||
179 | if (tmp) { | ||
180 | kfree(tmp->channels); | ||
181 | kfree(tmp); | ||
182 | } | ||
183 | |||
184 | return ret; | ||
185 | } | ||
186 | |||
187 | static void p54_update_channel_param(struct p54_channel_list *list, | ||
188 | u16 freq, u16 data) | ||
189 | { | ||
190 | int band, i; | ||
191 | |||
192 | /* | ||
193 | * usually all lists in the eeprom are mostly sorted. | ||
194 | * so it's very likely that the entry we are looking for | ||
195 | * is right at the end of the list | ||
196 | */ | ||
197 | for (i = list->entries; i >= 0; i--) { | ||
198 | if (freq == list->channels[i].freq) { | ||
199 | list->channels[i].data |= data; | ||
200 | break; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | if ((i < 0) && (list->entries < list->max_entries)) { | ||
205 | /* entry does not exist yet. Initialize a new one. */ | ||
206 | band = p54_get_band_from_freq(freq); | ||
207 | |||
208 | /* | ||
209 | * filter out frequencies which don't belong into | ||
210 | * any supported band. | ||
211 | */ | ||
212 | if (band < 0) | ||
213 | return ; | ||
214 | |||
215 | i = list->entries++; | ||
216 | list->band_channel_num[band]++; | ||
217 | |||
218 | list->channels[i].freq = freq; | ||
219 | list->channels[i].data = data; | ||
220 | list->channels[i].band = band; | ||
221 | list->channels[i].index = ieee80211_frequency_to_channel(freq); | ||
222 | /* TODO: parse output_limit and fill max_power */ | ||
223 | } | ||
224 | } | ||
225 | |||
226 | static int p54_generate_channel_lists(struct ieee80211_hw *dev) | ||
227 | { | ||
228 | struct p54_common *priv = dev->priv; | ||
229 | struct p54_channel_list *list; | ||
230 | unsigned int i, j, max_channel_num; | ||
231 | int ret = -ENOMEM; | ||
232 | u16 freq; | ||
233 | |||
234 | if ((priv->iq_autocal_len != priv->curve_data->entries) || | ||
235 | (priv->iq_autocal_len != priv->output_limit->entries)) | ||
236 | printk(KERN_ERR "%s: EEPROM is damaged... you may not be able" | ||
237 | "to use all channels with this device.\n", | ||
238 | wiphy_name(dev->wiphy)); | ||
239 | |||
240 | max_channel_num = max_t(unsigned int, priv->output_limit->entries, | ||
241 | priv->iq_autocal_len); | ||
242 | max_channel_num = max_t(unsigned int, max_channel_num, | ||
243 | priv->curve_data->entries); | ||
244 | |||
245 | list = kzalloc(sizeof(*list), GFP_KERNEL); | ||
246 | if (!list) | ||
247 | goto free; | ||
248 | |||
249 | list->max_entries = max_channel_num; | ||
250 | list->channels = kzalloc(sizeof(struct p54_channel_entry) * | ||
251 | max_channel_num, GFP_KERNEL); | ||
252 | if (!list->channels) | ||
253 | goto free; | ||
254 | |||
255 | for (i = 0; i < max_channel_num; i++) { | ||
256 | if (i < priv->iq_autocal_len) { | ||
257 | freq = le16_to_cpu(priv->iq_autocal[i].freq); | ||
258 | p54_update_channel_param(list, freq, CHAN_HAS_CAL); | ||
259 | } | ||
260 | |||
261 | if (i < priv->output_limit->entries) { | ||
262 | freq = le16_to_cpup((__le16 *) (i * | ||
263 | priv->output_limit->entry_size + | ||
264 | priv->output_limit->offset + | ||
265 | priv->output_limit->data)); | ||
266 | |||
267 | p54_update_channel_param(list, freq, CHAN_HAS_LIMIT); | ||
268 | } | ||
269 | |||
270 | if (i < priv->curve_data->entries) { | ||
271 | freq = le16_to_cpup((__le16 *) (i * | ||
272 | priv->curve_data->entry_size + | ||
273 | priv->curve_data->offset + | ||
274 | priv->curve_data->data)); | ||
275 | |||
276 | p54_update_channel_param(list, freq, CHAN_HAS_CURVE); | ||
277 | } | ||
278 | } | ||
279 | |||
280 | /* sort the list by the channel index */ | ||
281 | sort(list->channels, list->entries, sizeof(struct p54_channel_entry), | ||
282 | p54_compare_channels, NULL); | ||
283 | |||
284 | for (i = 0, j = 0; i < IEEE80211_NUM_BANDS; i++) { | ||
285 | if (list->band_channel_num[i]) { | ||
286 | ret = p54_generate_band(dev, list, i); | ||
287 | if (ret) | ||
288 | goto free; | ||
289 | |||
290 | j++; | ||
291 | } | ||
292 | } | ||
293 | if (j == 0) { | ||
294 | /* no useable band available. */ | ||
295 | ret = -EINVAL; | ||
296 | } | ||
297 | |||
298 | free: | ||
299 | if (list) { | ||
300 | kfree(list->channels); | ||
301 | kfree(list); | ||
302 | } | ||
303 | |||
304 | return ret; | ||
305 | } | ||
306 | |||
124 | static int p54_convert_rev0(struct ieee80211_hw *dev, | 307 | static int p54_convert_rev0(struct ieee80211_hw *dev, |
125 | struct pda_pa_curve_data *curve_data) | 308 | struct pda_pa_curve_data *curve_data) |
126 | { | 309 | { |
@@ -487,13 +670,19 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) | |||
487 | goto err; | 670 | goto err; |
488 | } | 671 | } |
489 | 672 | ||
673 | err = p54_generate_channel_lists(dev); | ||
674 | if (err) | ||
675 | goto err; | ||
676 | |||
490 | priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK; | 677 | priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK; |
491 | if (priv->rxhw == PDR_SYNTH_FRONTEND_XBOW) | 678 | if (priv->rxhw == PDR_SYNTH_FRONTEND_XBOW) |
492 | p54_init_xbow_synth(priv); | 679 | p54_init_xbow_synth(priv); |
493 | if (!(synth & PDR_SYNTH_24_GHZ_DISABLED)) | 680 | if (!(synth & PDR_SYNTH_24_GHZ_DISABLED)) |
494 | dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &band_2GHz; | 681 | dev->wiphy->bands[IEEE80211_BAND_2GHZ] = |
682 | priv->band_table[IEEE80211_BAND_2GHZ]; | ||
495 | if (!(synth & PDR_SYNTH_5_GHZ_DISABLED)) | 683 | if (!(synth & PDR_SYNTH_5_GHZ_DISABLED)) |
496 | dev->wiphy->bands[IEEE80211_BAND_5GHZ] = &band_5GHz; | 684 | dev->wiphy->bands[IEEE80211_BAND_5GHZ] = |
685 | priv->band_table[IEEE80211_BAND_5GHZ]; | ||
497 | if ((synth & PDR_SYNTH_RX_DIV_MASK) == PDR_SYNTH_RX_DIV_SUPPORTED) | 686 | if ((synth & PDR_SYNTH_RX_DIV_MASK) == PDR_SYNTH_RX_DIV_SUPPORTED) |
498 | priv->rx_diversity_mask = 3; | 687 | priv->rx_diversity_mask = 3; |
499 | if ((synth & PDR_SYNTH_TX_DIV_MASK) == PDR_SYNTH_TX_DIV_SUPPORTED) | 688 | if ((synth & PDR_SYNTH_TX_DIV_MASK) == PDR_SYNTH_TX_DIV_SUPPORTED) |
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index c9a054548d9..f19add2c3ca 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c | |||
@@ -598,6 +598,10 @@ EXPORT_SYMBOL_GPL(p54_register_common); | |||
598 | void p54_free_common(struct ieee80211_hw *dev) | 598 | void p54_free_common(struct ieee80211_hw *dev) |
599 | { | 599 | { |
600 | struct p54_common *priv = dev->priv; | 600 | struct p54_common *priv = dev->priv; |
601 | unsigned int i; | ||
602 | |||
603 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) | ||
604 | kfree(priv->band_table[i]); | ||
601 | 605 | ||
602 | kfree(priv->iq_autocal); | 606 | kfree(priv->iq_autocal); |
603 | kfree(priv->output_limit); | 607 | kfree(priv->output_limit); |
diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h index 6772ed505d4..584b1560aff 100644 --- a/drivers/net/wireless/p54/p54.h +++ b/drivers/net/wireless/p54/p54.h | |||
@@ -198,6 +198,7 @@ struct p54_common { | |||
198 | struct p54_cal_database *curve_data; | 198 | struct p54_cal_database *curve_data; |
199 | struct p54_cal_database *output_limit; | 199 | struct p54_cal_database *output_limit; |
200 | struct p54_rssi_linear_approximation rssical_db[IEEE80211_NUM_BANDS]; | 200 | struct p54_rssi_linear_approximation rssical_db[IEEE80211_NUM_BANDS]; |
201 | struct ieee80211_supported_band *band_table[IEEE80211_NUM_BANDS]; | ||
201 | 202 | ||
202 | /* BBP/MAC state */ | 203 | /* BBP/MAC state */ |
203 | u8 mac_addr[ETH_ALEN]; | 204 | u8 mac_addr[ETH_ALEN]; |