diff options
Diffstat (limited to 'drivers/net/wireless/orinoco/hw.c')
-rw-r--r-- | drivers/net/wireless/orinoco/hw.c | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c new file mode 100644 index 000000000000..081428d9409e --- /dev/null +++ b/drivers/net/wireless/orinoco/hw.c | |||
@@ -0,0 +1,586 @@ | |||
1 | /* Encapsulate basic setting changes and retrieval on Hermes hardware | ||
2 | * | ||
3 | * See copyright notice in main.c | ||
4 | */ | ||
5 | #include <linux/kernel.h> | ||
6 | #include <linux/if_arp.h> | ||
7 | #include <linux/ieee80211.h> | ||
8 | #include <linux/wireless.h> | ||
9 | |||
10 | #include "hermes.h" | ||
11 | #include "hermes_rid.h" | ||
12 | #include "orinoco.h" | ||
13 | |||
14 | #include "hw.h" | ||
15 | |||
16 | /********************************************************************/ | ||
17 | /* Data tables */ | ||
18 | /********************************************************************/ | ||
19 | |||
20 | /* This tables gives the actual meanings of the bitrate IDs returned | ||
21 | * by the firmware. */ | ||
22 | static const struct { | ||
23 | int bitrate; /* in 100s of kilobits */ | ||
24 | int automatic; | ||
25 | u16 agere_txratectrl; | ||
26 | u16 intersil_txratectrl; | ||
27 | } bitrate_table[] = { | ||
28 | {110, 1, 3, 15}, /* Entry 0 is the default */ | ||
29 | {10, 0, 1, 1}, | ||
30 | {10, 1, 1, 1}, | ||
31 | {20, 0, 2, 2}, | ||
32 | {20, 1, 6, 3}, | ||
33 | {55, 0, 4, 4}, | ||
34 | {55, 1, 7, 7}, | ||
35 | {110, 0, 5, 8}, | ||
36 | }; | ||
37 | #define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table) | ||
38 | |||
39 | int orinoco_get_bitratemode(int bitrate, int automatic) | ||
40 | { | ||
41 | int ratemode = -1; | ||
42 | int i; | ||
43 | |||
44 | if ((bitrate != 10) && (bitrate != 20) && | ||
45 | (bitrate != 55) && (bitrate != 110)) | ||
46 | return ratemode; | ||
47 | |||
48 | for (i = 0; i < BITRATE_TABLE_SIZE; i++) { | ||
49 | if ((bitrate_table[i].bitrate == bitrate) && | ||
50 | (bitrate_table[i].automatic == automatic)) { | ||
51 | ratemode = i; | ||
52 | break; | ||
53 | } | ||
54 | } | ||
55 | return ratemode; | ||
56 | } | ||
57 | |||
58 | void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic) | ||
59 | { | ||
60 | BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE)); | ||
61 | |||
62 | *bitrate = bitrate_table[ratemode].bitrate * 100000; | ||
63 | *automatic = bitrate_table[ratemode].automatic; | ||
64 | } | ||
65 | |||
66 | /* Get tsc from the firmware */ | ||
67 | int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc) | ||
68 | { | ||
69 | hermes_t *hw = &priv->hw; | ||
70 | int err = 0; | ||
71 | u8 tsc_arr[4][IW_ENCODE_SEQ_MAX_SIZE]; | ||
72 | |||
73 | if ((key < 0) || (key > 4)) | ||
74 | return -EINVAL; | ||
75 | |||
76 | err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV, | ||
77 | sizeof(tsc_arr), NULL, &tsc_arr); | ||
78 | if (!err) | ||
79 | memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0])); | ||
80 | |||
81 | return err; | ||
82 | } | ||
83 | |||
84 | int __orinoco_hw_set_bitrate(struct orinoco_private *priv) | ||
85 | { | ||
86 | hermes_t *hw = &priv->hw; | ||
87 | int ratemode = priv->bitratemode; | ||
88 | int err = 0; | ||
89 | |||
90 | if (ratemode >= BITRATE_TABLE_SIZE) { | ||
91 | printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n", | ||
92 | priv->ndev->name, ratemode); | ||
93 | return -EINVAL; | ||
94 | } | ||
95 | |||
96 | switch (priv->firmware_type) { | ||
97 | case FIRMWARE_TYPE_AGERE: | ||
98 | err = hermes_write_wordrec(hw, USER_BAP, | ||
99 | HERMES_RID_CNFTXRATECONTROL, | ||
100 | bitrate_table[ratemode].agere_txratectrl); | ||
101 | break; | ||
102 | case FIRMWARE_TYPE_INTERSIL: | ||
103 | case FIRMWARE_TYPE_SYMBOL: | ||
104 | err = hermes_write_wordrec(hw, USER_BAP, | ||
105 | HERMES_RID_CNFTXRATECONTROL, | ||
106 | bitrate_table[ratemode].intersil_txratectrl); | ||
107 | break; | ||
108 | default: | ||
109 | BUG(); | ||
110 | } | ||
111 | |||
112 | return err; | ||
113 | } | ||
114 | |||
115 | int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate) | ||
116 | { | ||
117 | hermes_t *hw = &priv->hw; | ||
118 | int i; | ||
119 | int err = 0; | ||
120 | u16 val; | ||
121 | |||
122 | err = hermes_read_wordrec(hw, USER_BAP, | ||
123 | HERMES_RID_CURRENTTXRATE, &val); | ||
124 | if (err) | ||
125 | return err; | ||
126 | |||
127 | switch (priv->firmware_type) { | ||
128 | case FIRMWARE_TYPE_AGERE: /* Lucent style rate */ | ||
129 | /* Note : in Lucent firmware, the return value of | ||
130 | * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s, | ||
131 | * and therefore is totally different from the | ||
132 | * encoding of HERMES_RID_CNFTXRATECONTROL. | ||
133 | * Don't forget that 6Mb/s is really 5.5Mb/s */ | ||
134 | if (val == 6) | ||
135 | *bitrate = 5500000; | ||
136 | else | ||
137 | *bitrate = val * 1000000; | ||
138 | break; | ||
139 | case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */ | ||
140 | case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */ | ||
141 | for (i = 0; i < BITRATE_TABLE_SIZE; i++) | ||
142 | if (bitrate_table[i].intersil_txratectrl == val) | ||
143 | break; | ||
144 | |||
145 | if (i >= BITRATE_TABLE_SIZE) | ||
146 | printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n", | ||
147 | priv->ndev->name, val); | ||
148 | |||
149 | *bitrate = bitrate_table[i].bitrate * 100000; | ||
150 | break; | ||
151 | default: | ||
152 | BUG(); | ||
153 | } | ||
154 | |||
155 | return err; | ||
156 | } | ||
157 | |||
158 | /* Set fixed AP address */ | ||
159 | int __orinoco_hw_set_wap(struct orinoco_private *priv) | ||
160 | { | ||
161 | int roaming_flag; | ||
162 | int err = 0; | ||
163 | hermes_t *hw = &priv->hw; | ||
164 | |||
165 | switch (priv->firmware_type) { | ||
166 | case FIRMWARE_TYPE_AGERE: | ||
167 | /* not supported */ | ||
168 | break; | ||
169 | case FIRMWARE_TYPE_INTERSIL: | ||
170 | if (priv->bssid_fixed) | ||
171 | roaming_flag = 2; | ||
172 | else | ||
173 | roaming_flag = 1; | ||
174 | |||
175 | err = hermes_write_wordrec(hw, USER_BAP, | ||
176 | HERMES_RID_CNFROAMINGMODE, | ||
177 | roaming_flag); | ||
178 | break; | ||
179 | case FIRMWARE_TYPE_SYMBOL: | ||
180 | err = HERMES_WRITE_RECORD(hw, USER_BAP, | ||
181 | HERMES_RID_CNFMANDATORYBSSID_SYMBOL, | ||
182 | &priv->desired_bssid); | ||
183 | break; | ||
184 | } | ||
185 | return err; | ||
186 | } | ||
187 | |||
188 | /* Change the WEP keys and/or the current keys. Can be called | ||
189 | * either from __orinoco_hw_setup_enc() or directly from | ||
190 | * orinoco_ioctl_setiwencode(). In the later case the association | ||
191 | * with the AP is not broken (if the firmware can handle it), | ||
192 | * which is needed for 802.1x implementations. */ | ||
193 | int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv) | ||
194 | { | ||
195 | hermes_t *hw = &priv->hw; | ||
196 | int err = 0; | ||
197 | |||
198 | switch (priv->firmware_type) { | ||
199 | case FIRMWARE_TYPE_AGERE: | ||
200 | err = HERMES_WRITE_RECORD(hw, USER_BAP, | ||
201 | HERMES_RID_CNFWEPKEYS_AGERE, | ||
202 | &priv->keys); | ||
203 | if (err) | ||
204 | return err; | ||
205 | err = hermes_write_wordrec(hw, USER_BAP, | ||
206 | HERMES_RID_CNFTXKEY_AGERE, | ||
207 | priv->tx_key); | ||
208 | if (err) | ||
209 | return err; | ||
210 | break; | ||
211 | case FIRMWARE_TYPE_INTERSIL: | ||
212 | case FIRMWARE_TYPE_SYMBOL: | ||
213 | { | ||
214 | int keylen; | ||
215 | int i; | ||
216 | |||
217 | /* Force uniform key length to work around | ||
218 | * firmware bugs */ | ||
219 | keylen = le16_to_cpu(priv->keys[priv->tx_key].len); | ||
220 | |||
221 | if (keylen > LARGE_KEY_SIZE) { | ||
222 | printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n", | ||
223 | priv->ndev->name, priv->tx_key, keylen); | ||
224 | return -E2BIG; | ||
225 | } | ||
226 | |||
227 | /* Write all 4 keys */ | ||
228 | for (i = 0; i < ORINOCO_MAX_KEYS; i++) { | ||
229 | err = hermes_write_ltv(hw, USER_BAP, | ||
230 | HERMES_RID_CNFDEFAULTKEY0 + i, | ||
231 | HERMES_BYTES_TO_RECLEN(keylen), | ||
232 | priv->keys[i].data); | ||
233 | if (err) | ||
234 | return err; | ||
235 | } | ||
236 | |||
237 | /* Write the index of the key used in transmission */ | ||
238 | err = hermes_write_wordrec(hw, USER_BAP, | ||
239 | HERMES_RID_CNFWEPDEFAULTKEYID, | ||
240 | priv->tx_key); | ||
241 | if (err) | ||
242 | return err; | ||
243 | } | ||
244 | break; | ||
245 | } | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | int __orinoco_hw_setup_enc(struct orinoco_private *priv) | ||
251 | { | ||
252 | hermes_t *hw = &priv->hw; | ||
253 | int err = 0; | ||
254 | int master_wep_flag; | ||
255 | int auth_flag; | ||
256 | int enc_flag; | ||
257 | |||
258 | /* Setup WEP keys for WEP and WPA */ | ||
259 | if (priv->encode_alg) | ||
260 | __orinoco_hw_setup_wepkeys(priv); | ||
261 | |||
262 | if (priv->wep_restrict) | ||
263 | auth_flag = HERMES_AUTH_SHARED_KEY; | ||
264 | else | ||
265 | auth_flag = HERMES_AUTH_OPEN; | ||
266 | |||
267 | if (priv->wpa_enabled) | ||
268 | enc_flag = 2; | ||
269 | else if (priv->encode_alg == IW_ENCODE_ALG_WEP) | ||
270 | enc_flag = 1; | ||
271 | else | ||
272 | enc_flag = 0; | ||
273 | |||
274 | switch (priv->firmware_type) { | ||
275 | case FIRMWARE_TYPE_AGERE: /* Agere style WEP */ | ||
276 | if (priv->encode_alg == IW_ENCODE_ALG_WEP) { | ||
277 | /* Enable the shared-key authentication. */ | ||
278 | err = hermes_write_wordrec(hw, USER_BAP, | ||
279 | HERMES_RID_CNFAUTHENTICATION_AGERE, | ||
280 | auth_flag); | ||
281 | } | ||
282 | err = hermes_write_wordrec(hw, USER_BAP, | ||
283 | HERMES_RID_CNFWEPENABLED_AGERE, | ||
284 | enc_flag); | ||
285 | if (err) | ||
286 | return err; | ||
287 | |||
288 | if (priv->has_wpa) { | ||
289 | /* Set WPA key management */ | ||
290 | err = hermes_write_wordrec(hw, USER_BAP, | ||
291 | HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE, | ||
292 | priv->key_mgmt); | ||
293 | if (err) | ||
294 | return err; | ||
295 | } | ||
296 | |||
297 | break; | ||
298 | |||
299 | case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */ | ||
300 | case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */ | ||
301 | if (priv->encode_alg == IW_ENCODE_ALG_WEP) { | ||
302 | if (priv->wep_restrict || | ||
303 | (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)) | ||
304 | master_wep_flag = HERMES_WEP_PRIVACY_INVOKED | | ||
305 | HERMES_WEP_EXCL_UNENCRYPTED; | ||
306 | else | ||
307 | master_wep_flag = HERMES_WEP_PRIVACY_INVOKED; | ||
308 | |||
309 | err = hermes_write_wordrec(hw, USER_BAP, | ||
310 | HERMES_RID_CNFAUTHENTICATION, | ||
311 | auth_flag); | ||
312 | if (err) | ||
313 | return err; | ||
314 | } else | ||
315 | master_wep_flag = 0; | ||
316 | |||
317 | if (priv->iw_mode == IW_MODE_MONITOR) | ||
318 | master_wep_flag |= HERMES_WEP_HOST_DECRYPT; | ||
319 | |||
320 | /* Master WEP setting : on/off */ | ||
321 | err = hermes_write_wordrec(hw, USER_BAP, | ||
322 | HERMES_RID_CNFWEPFLAGS_INTERSIL, | ||
323 | master_wep_flag); | ||
324 | if (err) | ||
325 | return err; | ||
326 | |||
327 | break; | ||
328 | } | ||
329 | |||
330 | return 0; | ||
331 | } | ||
332 | |||
333 | /* key must be 32 bytes, including the tx and rx MIC keys. | ||
334 | * rsc must be 8 bytes | ||
335 | * tsc must be 8 bytes or NULL | ||
336 | */ | ||
337 | int __orinoco_hw_set_tkip_key(hermes_t *hw, int key_idx, int set_tx, | ||
338 | u8 *key, u8 *rsc, u8 *tsc) | ||
339 | { | ||
340 | struct { | ||
341 | __le16 idx; | ||
342 | u8 rsc[IW_ENCODE_SEQ_MAX_SIZE]; | ||
343 | u8 key[TKIP_KEYLEN]; | ||
344 | u8 tx_mic[MIC_KEYLEN]; | ||
345 | u8 rx_mic[MIC_KEYLEN]; | ||
346 | u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; | ||
347 | } __attribute__ ((packed)) buf; | ||
348 | int ret; | ||
349 | int err; | ||
350 | int k; | ||
351 | u16 xmitting; | ||
352 | |||
353 | key_idx &= 0x3; | ||
354 | |||
355 | if (set_tx) | ||
356 | key_idx |= 0x8000; | ||
357 | |||
358 | buf.idx = cpu_to_le16(key_idx); | ||
359 | memcpy(buf.key, key, | ||
360 | sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic)); | ||
361 | |||
362 | if (rsc == NULL) | ||
363 | memset(buf.rsc, 0, sizeof(buf.rsc)); | ||
364 | else | ||
365 | memcpy(buf.rsc, rsc, sizeof(buf.rsc)); | ||
366 | |||
367 | if (tsc == NULL) { | ||
368 | memset(buf.tsc, 0, sizeof(buf.tsc)); | ||
369 | buf.tsc[4] = 0x10; | ||
370 | } else { | ||
371 | memcpy(buf.tsc, tsc, sizeof(buf.tsc)); | ||
372 | } | ||
373 | |||
374 | /* Wait upto 100ms for tx queue to empty */ | ||
375 | k = 100; | ||
376 | do { | ||
377 | k--; | ||
378 | udelay(1000); | ||
379 | ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY, | ||
380 | &xmitting); | ||
381 | if (ret) | ||
382 | break; | ||
383 | } while ((k > 0) && xmitting); | ||
384 | |||
385 | if (k == 0) | ||
386 | ret = -ETIMEDOUT; | ||
387 | |||
388 | err = HERMES_WRITE_RECORD(hw, USER_BAP, | ||
389 | HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE, | ||
390 | &buf); | ||
391 | |||
392 | return ret ? ret : err; | ||
393 | } | ||
394 | |||
395 | int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx) | ||
396 | { | ||
397 | hermes_t *hw = &priv->hw; | ||
398 | int err; | ||
399 | |||
400 | memset(&priv->tkip_key[key_idx], 0, sizeof(priv->tkip_key[key_idx])); | ||
401 | err = hermes_write_wordrec(hw, USER_BAP, | ||
402 | HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE, | ||
403 | key_idx); | ||
404 | if (err) | ||
405 | printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n", | ||
406 | priv->ndev->name, err, key_idx); | ||
407 | return err; | ||
408 | } | ||
409 | |||
410 | int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, | ||
411 | struct dev_addr_list *mc_list, | ||
412 | int mc_count, int promisc) | ||
413 | { | ||
414 | hermes_t *hw = &priv->hw; | ||
415 | int err = 0; | ||
416 | |||
417 | if (promisc != priv->promiscuous) { | ||
418 | err = hermes_write_wordrec(hw, USER_BAP, | ||
419 | HERMES_RID_CNFPROMISCUOUSMODE, | ||
420 | promisc); | ||
421 | if (err) { | ||
422 | printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n", | ||
423 | priv->ndev->name, err); | ||
424 | } else | ||
425 | priv->promiscuous = promisc; | ||
426 | } | ||
427 | |||
428 | /* If we're not in promiscuous mode, then we need to set the | ||
429 | * group address if either we want to multicast, or if we were | ||
430 | * multicasting and want to stop */ | ||
431 | if (!promisc && (mc_count || priv->mc_count)) { | ||
432 | struct dev_mc_list *p = mc_list; | ||
433 | struct hermes_multicast mclist; | ||
434 | int i; | ||
435 | |||
436 | for (i = 0; i < mc_count; i++) { | ||
437 | /* paranoia: is list shorter than mc_count? */ | ||
438 | BUG_ON(!p); | ||
439 | /* paranoia: bad address size in list? */ | ||
440 | BUG_ON(p->dmi_addrlen != ETH_ALEN); | ||
441 | |||
442 | memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN); | ||
443 | p = p->next; | ||
444 | } | ||
445 | |||
446 | if (p) | ||
447 | printk(KERN_WARNING "%s: Multicast list is " | ||
448 | "longer than mc_count\n", priv->ndev->name); | ||
449 | |||
450 | err = hermes_write_ltv(hw, USER_BAP, | ||
451 | HERMES_RID_CNFGROUPADDRESSES, | ||
452 | HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN), | ||
453 | &mclist); | ||
454 | if (err) | ||
455 | printk(KERN_ERR "%s: Error %d setting multicast list.\n", | ||
456 | priv->ndev->name, err); | ||
457 | else | ||
458 | priv->mc_count = mc_count; | ||
459 | } | ||
460 | return err; | ||
461 | } | ||
462 | |||
463 | /* Return : < 0 -> error code ; >= 0 -> length */ | ||
464 | int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, | ||
465 | char buf[IW_ESSID_MAX_SIZE+1]) | ||
466 | { | ||
467 | hermes_t *hw = &priv->hw; | ||
468 | int err = 0; | ||
469 | struct hermes_idstring essidbuf; | ||
470 | char *p = (char *)(&essidbuf.val); | ||
471 | int len; | ||
472 | unsigned long flags; | ||
473 | |||
474 | if (orinoco_lock(priv, &flags) != 0) | ||
475 | return -EBUSY; | ||
476 | |||
477 | if (strlen(priv->desired_essid) > 0) { | ||
478 | /* We read the desired SSID from the hardware rather | ||
479 | than from priv->desired_essid, just in case the | ||
480 | firmware is allowed to change it on us. I'm not | ||
481 | sure about this */ | ||
482 | /* My guess is that the OWNSSID should always be whatever | ||
483 | * we set to the card, whereas CURRENT_SSID is the one that | ||
484 | * may change... - Jean II */ | ||
485 | u16 rid; | ||
486 | |||
487 | *active = 1; | ||
488 | |||
489 | rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID : | ||
490 | HERMES_RID_CNFDESIREDSSID; | ||
491 | |||
492 | err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf), | ||
493 | NULL, &essidbuf); | ||
494 | if (err) | ||
495 | goto fail_unlock; | ||
496 | } else { | ||
497 | *active = 0; | ||
498 | |||
499 | err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID, | ||
500 | sizeof(essidbuf), NULL, &essidbuf); | ||
501 | if (err) | ||
502 | goto fail_unlock; | ||
503 | } | ||
504 | |||
505 | len = le16_to_cpu(essidbuf.len); | ||
506 | BUG_ON(len > IW_ESSID_MAX_SIZE); | ||
507 | |||
508 | memset(buf, 0, IW_ESSID_MAX_SIZE); | ||
509 | memcpy(buf, p, len); | ||
510 | err = len; | ||
511 | |||
512 | fail_unlock: | ||
513 | orinoco_unlock(priv, &flags); | ||
514 | |||
515 | return err; | ||
516 | } | ||
517 | |||
518 | int orinoco_hw_get_freq(struct orinoco_private *priv) | ||
519 | { | ||
520 | hermes_t *hw = &priv->hw; | ||
521 | int err = 0; | ||
522 | u16 channel; | ||
523 | int freq = 0; | ||
524 | unsigned long flags; | ||
525 | |||
526 | if (orinoco_lock(priv, &flags) != 0) | ||
527 | return -EBUSY; | ||
528 | |||
529 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, | ||
530 | &channel); | ||
531 | if (err) | ||
532 | goto out; | ||
533 | |||
534 | /* Intersil firmware 1.3.5 returns 0 when the interface is down */ | ||
535 | if (channel == 0) { | ||
536 | err = -EBUSY; | ||
537 | goto out; | ||
538 | } | ||
539 | |||
540 | if ((channel < 1) || (channel > NUM_CHANNELS)) { | ||
541 | printk(KERN_WARNING "%s: Channel out of range (%d)!\n", | ||
542 | priv->ndev->name, channel); | ||
543 | err = -EBUSY; | ||
544 | goto out; | ||
545 | |||
546 | } | ||
547 | freq = ieee80211_dsss_chan_to_freq(channel); | ||
548 | |||
549 | out: | ||
550 | orinoco_unlock(priv, &flags); | ||
551 | |||
552 | if (err > 0) | ||
553 | err = -EBUSY; | ||
554 | return err ? err : freq; | ||
555 | } | ||
556 | |||
557 | int orinoco_hw_get_bitratelist(struct orinoco_private *priv, | ||
558 | int *numrates, s32 *rates, int max) | ||
559 | { | ||
560 | hermes_t *hw = &priv->hw; | ||
561 | struct hermes_idstring list; | ||
562 | unsigned char *p = (unsigned char *)&list.val; | ||
563 | int err = 0; | ||
564 | int num; | ||
565 | int i; | ||
566 | unsigned long flags; | ||
567 | |||
568 | if (orinoco_lock(priv, &flags) != 0) | ||
569 | return -EBUSY; | ||
570 | |||
571 | err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES, | ||
572 | sizeof(list), NULL, &list); | ||
573 | orinoco_unlock(priv, &flags); | ||
574 | |||
575 | if (err) | ||
576 | return err; | ||
577 | |||
578 | num = le16_to_cpu(list.len); | ||
579 | *numrates = num; | ||
580 | num = min(num, max); | ||
581 | |||
582 | for (i = 0; i < num; i++) | ||
583 | rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */ | ||
584 | |||
585 | return 0; | ||
586 | } | ||