diff options
author | Christian Lamparter <chunkeey@web.de> | 2008-08-23 16:15:25 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-08-29 16:24:08 -0400 |
commit | 154e3af17fdce617605b63f3534dac2c10c90922 (patch) | |
tree | 48331503716ff5a5f1848237017d06c808f80a18 | |
parent | a3ec233c90f0b8a5c00bf182f2c3ea9119b46caa (diff) |
p54: fix rssi auto calibration
Ever wondered why the signal was so bad with p54 compared to madwifi, or intel?
Well, if you have revision 1 rssi calibration curve points in your EEPROM, then wonder no more.
The firmware wants a extra 1 byte padding for every curve point. But someone forgot to put
them into the EEPROM's data structure...
So now, big question: what happens when we blindly "memcpy" these data points?
Signed-off-by: Christian Lamparter <chunkeey@web.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/p54/p54common.c | 140 | ||||
-rw-r--r-- | drivers/net/wireless/p54/p54common.h | 23 |
2 files changed, 111 insertions, 52 deletions
diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index 29be3dc8ee09..4a6b5363ea4b 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c | |||
@@ -155,14 +155,14 @@ void p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw) | |||
155 | } | 155 | } |
156 | EXPORT_SYMBOL_GPL(p54_parse_firmware); | 156 | EXPORT_SYMBOL_GPL(p54_parse_firmware); |
157 | 157 | ||
158 | static int p54_convert_rev0_to_rev1(struct ieee80211_hw *dev, | 158 | static int p54_convert_rev0(struct ieee80211_hw *dev, |
159 | struct pda_pa_curve_data *curve_data) | 159 | struct pda_pa_curve_data *curve_data) |
160 | { | 160 | { |
161 | struct p54_common *priv = dev->priv; | 161 | struct p54_common *priv = dev->priv; |
162 | struct pda_pa_curve_data_sample_rev1 *rev1; | 162 | struct p54_pa_curve_data_sample *dst; |
163 | struct pda_pa_curve_data_sample_rev0 *rev0; | 163 | struct pda_pa_curve_data_sample_rev0 *src; |
164 | size_t cd_len = sizeof(*curve_data) + | 164 | size_t cd_len = sizeof(*curve_data) + |
165 | (curve_data->points_per_channel*sizeof(*rev1) + 2) * | 165 | (curve_data->points_per_channel*sizeof(*dst) + 2) * |
166 | curve_data->channels; | 166 | curve_data->channels; |
167 | unsigned int i, j; | 167 | unsigned int i, j; |
168 | void *source, *target; | 168 | void *source, *target; |
@@ -180,27 +180,63 @@ static int p54_convert_rev0_to_rev1(struct ieee80211_hw *dev, | |||
180 | *((__le16 *)target) = *freq; | 180 | *((__le16 *)target) = *freq; |
181 | target += sizeof(__le16); | 181 | target += sizeof(__le16); |
182 | for (j = 0; j < curve_data->points_per_channel; j++) { | 182 | for (j = 0; j < curve_data->points_per_channel; j++) { |
183 | rev1 = target; | 183 | dst = target; |
184 | rev0 = source; | 184 | src = source; |
185 | 185 | ||
186 | rev1->rf_power = rev0->rf_power; | 186 | dst->rf_power = src->rf_power; |
187 | rev1->pa_detector = rev0->pa_detector; | 187 | dst->pa_detector = src->pa_detector; |
188 | rev1->data_64qam = rev0->pcv; | 188 | dst->data_64qam = src->pcv; |
189 | /* "invent" the points for the other modulations */ | 189 | /* "invent" the points for the other modulations */ |
190 | #define SUB(x,y) (u8)((x) - (y)) > (x) ? 0 : (x) - (y) | 190 | #define SUB(x,y) (u8)((x) - (y)) > (x) ? 0 : (x) - (y) |
191 | rev1->data_16qam = SUB(rev0->pcv, 12); | 191 | dst->data_16qam = SUB(src->pcv, 12); |
192 | rev1->data_qpsk = SUB(rev1->data_16qam, 12); | 192 | dst->data_qpsk = SUB(dst->data_16qam, 12); |
193 | rev1->data_bpsk = SUB(rev1->data_qpsk, 12); | 193 | dst->data_bpsk = SUB(dst->data_qpsk, 12); |
194 | rev1->data_barker= SUB(rev1->data_bpsk, 14); | 194 | dst->data_barker = SUB(dst->data_bpsk, 14); |
195 | #undef SUB | 195 | #undef SUB |
196 | target += sizeof(*rev1); | 196 | target += sizeof(*dst); |
197 | source += sizeof(*rev0); | 197 | source += sizeof(*src); |
198 | } | 198 | } |
199 | } | 199 | } |
200 | 200 | ||
201 | return 0; | 201 | return 0; |
202 | } | 202 | } |
203 | 203 | ||
204 | static int p54_convert_rev1(struct ieee80211_hw *dev, | ||
205 | struct pda_pa_curve_data *curve_data) | ||
206 | { | ||
207 | struct p54_common *priv = dev->priv; | ||
208 | struct p54_pa_curve_data_sample *dst; | ||
209 | struct pda_pa_curve_data_sample_rev1 *src; | ||
210 | size_t cd_len = sizeof(*curve_data) + | ||
211 | (curve_data->points_per_channel*sizeof(*dst) + 2) * | ||
212 | curve_data->channels; | ||
213 | unsigned int i, j; | ||
214 | void *source, *target; | ||
215 | |||
216 | priv->curve_data = kmalloc(cd_len, GFP_KERNEL); | ||
217 | if (!priv->curve_data) | ||
218 | return -ENOMEM; | ||
219 | |||
220 | memcpy(priv->curve_data, curve_data, sizeof(*curve_data)); | ||
221 | source = curve_data->data; | ||
222 | target = priv->curve_data->data; | ||
223 | for (i = 0; i < curve_data->channels; i++) { | ||
224 | __le16 *freq = source; | ||
225 | source += sizeof(__le16); | ||
226 | *((__le16 *)target) = *freq; | ||
227 | target += sizeof(__le16); | ||
228 | for (j = 0; j < curve_data->points_per_channel; j++) { | ||
229 | memcpy(target, source, sizeof(*src)); | ||
230 | |||
231 | target += sizeof(*dst); | ||
232 | source += sizeof(*src); | ||
233 | } | ||
234 | source++; | ||
235 | } | ||
236 | |||
237 | return 0; | ||
238 | } | ||
239 | |||
204 | int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) | 240 | int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) |
205 | { | 241 | { |
206 | struct p54_common *priv = dev->priv; | 242 | struct p54_common *priv = dev->priv; |
@@ -250,27 +286,32 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) | |||
250 | entry->data[1]*sizeof(*priv->output_limit)); | 286 | entry->data[1]*sizeof(*priv->output_limit)); |
251 | priv->output_limit_len = entry->data[1]; | 287 | priv->output_limit_len = entry->data[1]; |
252 | break; | 288 | break; |
253 | case PDR_PRISM_PA_CAL_CURVE_DATA: | 289 | case PDR_PRISM_PA_CAL_CURVE_DATA: { |
254 | if (data_len < sizeof(struct pda_pa_curve_data)) { | 290 | struct pda_pa_curve_data *curve_data = |
291 | (struct pda_pa_curve_data *)entry->data; | ||
292 | if (data_len < sizeof(*curve_data)) { | ||
255 | err = -EINVAL; | 293 | err = -EINVAL; |
256 | goto err; | 294 | goto err; |
257 | } | 295 | } |
258 | 296 | ||
259 | if (((struct pda_pa_curve_data *)entry->data)->cal_method_rev) { | 297 | switch (curve_data->cal_method_rev) { |
260 | priv->curve_data = kmalloc(data_len, GFP_KERNEL); | 298 | case 0: |
261 | if (!priv->curve_data) { | 299 | err = p54_convert_rev0(dev, curve_data); |
262 | err = -ENOMEM; | 300 | break; |
263 | goto err; | 301 | case 1: |
264 | } | 302 | err = p54_convert_rev1(dev, curve_data); |
265 | 303 | break; | |
266 | memcpy(priv->curve_data, entry->data, data_len); | 304 | default: |
267 | } else { | 305 | printk(KERN_ERR "p54: unknown curve data " |
268 | err = p54_convert_rev0_to_rev1(dev, (struct pda_pa_curve_data *)entry->data); | 306 | "revision %d\n", |
269 | if (err) | 307 | curve_data->cal_method_rev); |
270 | goto err; | 308 | err = -ENODEV; |
309 | break; | ||
271 | } | 310 | } |
311 | if (err) | ||
312 | goto err; | ||
272 | 313 | ||
273 | break; | 314 | } |
274 | case PDR_PRISM_ZIF_TX_IQ_CALIBRATION: | 315 | case PDR_PRISM_ZIF_TX_IQ_CALIBRATION: |
275 | priv->iq_autocal = kmalloc(data_len, GFP_KERNEL); | 316 | priv->iq_autocal = kmalloc(data_len, GFP_KERNEL); |
276 | if (!priv->iq_autocal) { | 317 | if (!priv->iq_autocal) { |
@@ -672,12 +713,9 @@ static int p54_set_freq(struct ieee80211_hw *dev, __le16 freq) | |||
672 | struct p54_control_hdr *hdr; | 713 | struct p54_control_hdr *hdr; |
673 | struct p54_tx_control_channel *chan; | 714 | struct p54_tx_control_channel *chan; |
674 | unsigned int i; | 715 | unsigned int i; |
675 | size_t payload_len = sizeof(*chan) + sizeof(u32)*2 + | ||
676 | sizeof(*chan->curve_data) * | ||
677 | priv->curve_data->points_per_channel; | ||
678 | void *entry; | 716 | void *entry; |
679 | 717 | ||
680 | hdr = kzalloc(sizeof(*hdr) + payload_len + | 718 | hdr = kzalloc(sizeof(*hdr) + sizeof(*chan) + |
681 | priv->tx_hdr_len, GFP_KERNEL); | 719 | priv->tx_hdr_len, GFP_KERNEL); |
682 | if (!hdr) | 720 | if (!hdr) |
683 | return -ENOMEM; | 721 | return -ENOMEM; |
@@ -689,10 +727,10 @@ static int p54_set_freq(struct ieee80211_hw *dev, __le16 freq) | |||
689 | hdr->magic1 = cpu_to_le16(0x8001); | 727 | hdr->magic1 = cpu_to_le16(0x8001); |
690 | hdr->len = cpu_to_le16(sizeof(*chan)); | 728 | hdr->len = cpu_to_le16(sizeof(*chan)); |
691 | hdr->type = cpu_to_le16(P54_CONTROL_TYPE_CHANNEL_CHANGE); | 729 | hdr->type = cpu_to_le16(P54_CONTROL_TYPE_CHANNEL_CHANGE); |
692 | p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + payload_len); | 730 | p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + sizeof(*chan)); |
693 | 731 | ||
694 | chan->magic1 = cpu_to_le16(0x1); | 732 | chan->flags = cpu_to_le16(0x1); |
695 | chan->magic2 = cpu_to_le16(0x0); | 733 | chan->dwell = cpu_to_le16(0x0); |
696 | 734 | ||
697 | for (i = 0; i < priv->iq_autocal_len; i++) { | 735 | for (i = 0; i < priv->iq_autocal_len; i++) { |
698 | if (priv->iq_autocal[i].freq != freq) | 736 | if (priv->iq_autocal[i].freq != freq) |
@@ -710,35 +748,41 @@ static int p54_set_freq(struct ieee80211_hw *dev, __le16 freq) | |||
710 | continue; | 748 | continue; |
711 | 749 | ||
712 | chan->val_barker = 0x38; | 750 | chan->val_barker = 0x38; |
713 | chan->val_bpsk = priv->output_limit[i].val_bpsk; | 751 | chan->val_bpsk = chan->dup_bpsk = |
714 | chan->val_qpsk = priv->output_limit[i].val_qpsk; | 752 | priv->output_limit[i].val_bpsk; |
715 | chan->val_16qam = priv->output_limit[i].val_16qam; | 753 | chan->val_qpsk = chan->dup_qpsk = |
716 | chan->val_64qam = priv->output_limit[i].val_64qam; | 754 | priv->output_limit[i].val_qpsk; |
755 | chan->val_16qam = chan->dup_16qam = | ||
756 | priv->output_limit[i].val_16qam; | ||
757 | chan->val_64qam = chan->dup_64qam = | ||
758 | priv->output_limit[i].val_64qam; | ||
717 | break; | 759 | break; |
718 | } | 760 | } |
719 | if (i == priv->output_limit_len) | 761 | if (i == priv->output_limit_len) |
720 | goto err; | 762 | goto err; |
721 | 763 | ||
722 | chan->pa_points_per_curve = priv->curve_data->points_per_channel; | ||
723 | |||
724 | entry = priv->curve_data->data; | 764 | entry = priv->curve_data->data; |
725 | for (i = 0; i < priv->curve_data->channels; i++) { | 765 | for (i = 0; i < priv->curve_data->channels; i++) { |
726 | if (*((__le16 *)entry) != freq) { | 766 | if (*((__le16 *)entry) != freq) { |
727 | entry += sizeof(__le16); | 767 | entry += sizeof(__le16); |
728 | entry += sizeof(struct pda_pa_curve_data_sample_rev1) * | 768 | entry += sizeof(struct p54_pa_curve_data_sample) * |
729 | chan->pa_points_per_curve; | 769 | priv->curve_data->points_per_channel; |
730 | continue; | 770 | continue; |
731 | } | 771 | } |
732 | 772 | ||
733 | entry += sizeof(__le16); | 773 | entry += sizeof(__le16); |
774 | chan->pa_points_per_curve = | ||
775 | min(priv->curve_data->points_per_channel, (u8) 8); | ||
776 | |||
734 | memcpy(chan->curve_data, entry, sizeof(*chan->curve_data) * | 777 | memcpy(chan->curve_data, entry, sizeof(*chan->curve_data) * |
735 | chan->pa_points_per_curve); | 778 | chan->pa_points_per_curve); |
736 | break; | 779 | break; |
737 | } | 780 | } |
738 | 781 | ||
739 | memcpy(hdr->data + payload_len - 4, &chan->val_bpsk, 4); | 782 | chan->rssical_mul = cpu_to_le16(130); |
783 | chan->rssical_add = cpu_to_le16(0xfe70); /* -400 */ | ||
740 | 784 | ||
741 | priv->tx(dev, hdr, sizeof(*hdr) + payload_len, 1); | 785 | priv->tx(dev, hdr, sizeof(*hdr) + sizeof(*chan), 1); |
742 | return 0; | 786 | return 0; |
743 | 787 | ||
744 | err: | 788 | err: |
diff --git a/drivers/net/wireless/p54/p54common.h b/drivers/net/wireless/p54/p54common.h index 9bc2a1cf4b57..a79c1a146917 100644 --- a/drivers/net/wireless/p54/p54common.h +++ b/drivers/net/wireless/p54/p54common.h | |||
@@ -89,6 +89,16 @@ struct pda_pa_curve_data_sample_rev1 { | |||
89 | u8 data_qpsk; | 89 | u8 data_qpsk; |
90 | u8 data_16qam; | 90 | u8 data_16qam; |
91 | u8 data_64qam; | 91 | u8 data_64qam; |
92 | } __attribute__ ((packed)); | ||
93 | |||
94 | struct p54_pa_curve_data_sample { | ||
95 | u8 rf_power; | ||
96 | u8 pa_detector; | ||
97 | u8 data_barker; | ||
98 | u8 data_bpsk; | ||
99 | u8 data_qpsk; | ||
100 | u8 data_16qam; | ||
101 | u8 data_64qam; | ||
92 | u8 padding; | 102 | u8 padding; |
93 | } __attribute__ ((packed)); | 103 | } __attribute__ ((packed)); |
94 | 104 | ||
@@ -212,8 +222,8 @@ struct p54_tx_control_filter { | |||
212 | } __attribute__ ((packed)); | 222 | } __attribute__ ((packed)); |
213 | 223 | ||
214 | struct p54_tx_control_channel { | 224 | struct p54_tx_control_channel { |
215 | __le16 magic1; | 225 | __le16 flags; |
216 | __le16 magic2; | 226 | __le16 dwell; |
217 | u8 padding1[20]; | 227 | u8 padding1[20]; |
218 | struct pda_iq_autocal_entry iq_autocal; | 228 | struct pda_iq_autocal_entry iq_autocal; |
219 | u8 pa_points_per_curve; | 229 | u8 pa_points_per_curve; |
@@ -222,8 +232,13 @@ struct p54_tx_control_channel { | |||
222 | u8 val_qpsk; | 232 | u8 val_qpsk; |
223 | u8 val_16qam; | 233 | u8 val_16qam; |
224 | u8 val_64qam; | 234 | u8 val_64qam; |
225 | struct pda_pa_curve_data_sample_rev1 curve_data[0]; | 235 | struct pda_pa_curve_data_sample_rev1 curve_data[8]; |
226 | /* additional padding/data after curve_data */ | 236 | u8 dup_bpsk; |
237 | u8 dup_qpsk; | ||
238 | u8 dup_16qam; | ||
239 | u8 dup_64qam; | ||
240 | __le16 rssical_mul; | ||
241 | __le16 rssical_add; | ||
227 | } __attribute__ ((packed)); | 242 | } __attribute__ ((packed)); |
228 | 243 | ||
229 | struct p54_tx_control_led { | 244 | struct p54_tx_control_led { |