]>
Commit | Line | Data |
---|---|---|
712a4342 DK |
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 | ||
a2d1a42a DK |
16 | #define SYMBOL_MAX_VER_LEN (14) |
17 | ||
712a4342 DK |
18 | /********************************************************************/ |
19 | /* Data tables */ | |
20 | /********************************************************************/ | |
21 | ||
22 | /* This tables gives the actual meanings of the bitrate IDs returned | |
23 | * by the firmware. */ | |
24 | static const struct { | |
25 | int bitrate; /* in 100s of kilobits */ | |
26 | int automatic; | |
27 | u16 agere_txratectrl; | |
28 | u16 intersil_txratectrl; | |
29 | } bitrate_table[] = { | |
30 | {110, 1, 3, 15}, /* Entry 0 is the default */ | |
31 | {10, 0, 1, 1}, | |
32 | {10, 1, 1, 1}, | |
33 | {20, 0, 2, 2}, | |
34 | {20, 1, 6, 3}, | |
35 | {55, 0, 4, 4}, | |
36 | {55, 1, 7, 7}, | |
37 | {110, 0, 5, 8}, | |
38 | }; | |
39 | #define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table) | |
40 | ||
a2d1a42a DK |
41 | /* Firmware version encoding */ |
42 | struct comp_id { | |
43 | u16 id, variant, major, minor; | |
44 | } __attribute__ ((packed)); | |
45 | ||
46 | static inline fwtype_t determine_firmware_type(struct comp_id *nic_id) | |
47 | { | |
48 | if (nic_id->id < 0x8000) | |
49 | return FIRMWARE_TYPE_AGERE; | |
50 | else if (nic_id->id == 0x8000 && nic_id->major == 0) | |
51 | return FIRMWARE_TYPE_SYMBOL; | |
52 | else | |
53 | return FIRMWARE_TYPE_INTERSIL; | |
54 | } | |
55 | ||
56 | /* Set priv->firmware type, determine firmware properties */ | |
57 | int determine_fw_capabilities(struct orinoco_private *priv) | |
58 | { | |
59 | struct net_device *dev = priv->ndev; | |
60 | hermes_t *hw = &priv->hw; | |
61 | int err; | |
62 | struct comp_id nic_id, sta_id; | |
63 | unsigned int firmver; | |
64 | char tmp[SYMBOL_MAX_VER_LEN+1] __attribute__((aligned(2))); | |
65 | ||
66 | /* Get the hardware version */ | |
67 | err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id); | |
68 | if (err) { | |
69 | printk(KERN_ERR "%s: Cannot read hardware identity: error %d\n", | |
70 | dev->name, err); | |
71 | return err; | |
72 | } | |
73 | ||
74 | le16_to_cpus(&nic_id.id); | |
75 | le16_to_cpus(&nic_id.variant); | |
76 | le16_to_cpus(&nic_id.major); | |
77 | le16_to_cpus(&nic_id.minor); | |
78 | printk(KERN_DEBUG "%s: Hardware identity %04x:%04x:%04x:%04x\n", | |
79 | dev->name, nic_id.id, nic_id.variant, | |
80 | nic_id.major, nic_id.minor); | |
81 | ||
82 | priv->firmware_type = determine_firmware_type(&nic_id); | |
83 | ||
84 | /* Get the firmware version */ | |
85 | err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id); | |
86 | if (err) { | |
87 | printk(KERN_ERR "%s: Cannot read station identity: error %d\n", | |
88 | dev->name, err); | |
89 | return err; | |
90 | } | |
91 | ||
92 | le16_to_cpus(&sta_id.id); | |
93 | le16_to_cpus(&sta_id.variant); | |
94 | le16_to_cpus(&sta_id.major); | |
95 | le16_to_cpus(&sta_id.minor); | |
96 | printk(KERN_DEBUG "%s: Station identity %04x:%04x:%04x:%04x\n", | |
97 | dev->name, sta_id.id, sta_id.variant, | |
98 | sta_id.major, sta_id.minor); | |
99 | ||
100 | switch (sta_id.id) { | |
101 | case 0x15: | |
102 | printk(KERN_ERR "%s: Primary firmware is active\n", | |
103 | dev->name); | |
104 | return -ENODEV; | |
105 | case 0x14b: | |
106 | printk(KERN_ERR "%s: Tertiary firmware is active\n", | |
107 | dev->name); | |
108 | return -ENODEV; | |
109 | case 0x1f: /* Intersil, Agere, Symbol Spectrum24 */ | |
110 | case 0x21: /* Symbol Spectrum24 Trilogy */ | |
111 | break; | |
112 | default: | |
113 | printk(KERN_NOTICE "%s: Unknown station ID, please report\n", | |
114 | dev->name); | |
115 | break; | |
116 | } | |
117 | ||
118 | /* Default capabilities */ | |
119 | priv->has_sensitivity = 1; | |
120 | priv->has_mwo = 0; | |
121 | priv->has_preamble = 0; | |
122 | priv->has_port3 = 1; | |
123 | priv->has_ibss = 1; | |
124 | priv->has_wep = 0; | |
125 | priv->has_big_wep = 0; | |
126 | priv->has_alt_txcntl = 0; | |
127 | priv->has_ext_scan = 0; | |
128 | priv->has_wpa = 0; | |
129 | priv->do_fw_download = 0; | |
130 | ||
131 | /* Determine capabilities from the firmware version */ | |
132 | switch (priv->firmware_type) { | |
133 | case FIRMWARE_TYPE_AGERE: | |
134 | /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout, | |
135 | ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */ | |
136 | snprintf(priv->fw_name, sizeof(priv->fw_name) - 1, | |
137 | "Lucent/Agere %d.%02d", sta_id.major, sta_id.minor); | |
138 | ||
139 | firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor; | |
140 | ||
141 | priv->has_ibss = (firmver >= 0x60006); | |
142 | priv->has_wep = (firmver >= 0x40020); | |
143 | priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell | |
144 | Gold cards from the others? */ | |
145 | priv->has_mwo = (firmver >= 0x60000); | |
146 | priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ | |
147 | priv->ibss_port = 1; | |
148 | priv->has_hostscan = (firmver >= 0x8000a); | |
149 | priv->do_fw_download = 1; | |
150 | priv->broken_monitor = (firmver >= 0x80000); | |
151 | priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */ | |
152 | priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */ | |
153 | priv->has_wpa = (firmver >= 0x9002a); | |
154 | /* Tested with Agere firmware : | |
155 | * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II | |
156 | * Tested CableTron firmware : 4.32 => Anton */ | |
157 | break; | |
158 | case FIRMWARE_TYPE_SYMBOL: | |
159 | /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */ | |
160 | /* Intel MAC : 00:02:B3:* */ | |
161 | /* 3Com MAC : 00:50:DA:* */ | |
162 | memset(tmp, 0, sizeof(tmp)); | |
163 | /* Get the Symbol firmware version */ | |
164 | err = hermes_read_ltv(hw, USER_BAP, | |
165 | HERMES_RID_SECONDARYVERSION_SYMBOL, | |
166 | SYMBOL_MAX_VER_LEN, NULL, &tmp); | |
167 | if (err) { | |
168 | printk(KERN_WARNING | |
169 | "%s: Error %d reading Symbol firmware info. " | |
170 | "Wildly guessing capabilities...\n", | |
171 | dev->name, err); | |
172 | firmver = 0; | |
173 | tmp[0] = '\0'; | |
174 | } else { | |
175 | /* The firmware revision is a string, the format is | |
176 | * something like : "V2.20-01". | |
177 | * Quick and dirty parsing... - Jean II | |
178 | */ | |
179 | firmver = ((tmp[1] - '0') << 16) | |
180 | | ((tmp[3] - '0') << 12) | |
181 | | ((tmp[4] - '0') << 8) | |
182 | | ((tmp[6] - '0') << 4) | |
183 | | (tmp[7] - '0'); | |
184 | ||
185 | tmp[SYMBOL_MAX_VER_LEN] = '\0'; | |
186 | } | |
187 | ||
188 | snprintf(priv->fw_name, sizeof(priv->fw_name) - 1, | |
189 | "Symbol %s", tmp); | |
190 | ||
191 | priv->has_ibss = (firmver >= 0x20000); | |
192 | priv->has_wep = (firmver >= 0x15012); | |
193 | priv->has_big_wep = (firmver >= 0x20000); | |
194 | priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) || | |
195 | (firmver >= 0x29000 && firmver < 0x30000) || | |
196 | firmver >= 0x31000; | |
197 | priv->has_preamble = (firmver >= 0x20000); | |
198 | priv->ibss_port = 4; | |
199 | ||
200 | /* Symbol firmware is found on various cards, but | |
201 | * there has been no attempt to check firmware | |
202 | * download on non-spectrum_cs based cards. | |
203 | * | |
204 | * Given that the Agere firmware download works | |
205 | * differently, we should avoid doing a firmware | |
206 | * download with the Symbol algorithm on non-spectrum | |
207 | * cards. | |
208 | * | |
209 | * For now we can identify a spectrum_cs based card | |
210 | * because it has a firmware reset function. | |
211 | */ | |
212 | priv->do_fw_download = (priv->stop_fw != NULL); | |
213 | ||
214 | priv->broken_disableport = (firmver == 0x25013) || | |
215 | (firmver >= 0x30000 && firmver <= 0x31000); | |
216 | priv->has_hostscan = (firmver >= 0x31001) || | |
217 | (firmver >= 0x29057 && firmver < 0x30000); | |
218 | /* Tested with Intel firmware : 0x20015 => Jean II */ | |
219 | /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */ | |
220 | break; | |
221 | case FIRMWARE_TYPE_INTERSIL: | |
222 | /* D-Link, Linksys, Adtron, ZoomAir, and many others... | |
223 | * Samsung, Compaq 100/200 and Proxim are slightly | |
224 | * different and less well tested */ | |
225 | /* D-Link MAC : 00:40:05:* */ | |
226 | /* Addtron MAC : 00:90:D1:* */ | |
227 | snprintf(priv->fw_name, sizeof(priv->fw_name) - 1, | |
228 | "Intersil %d.%d.%d", sta_id.major, sta_id.minor, | |
229 | sta_id.variant); | |
230 | ||
231 | firmver = ((unsigned long)sta_id.major << 16) | | |
232 | ((unsigned long)sta_id.minor << 8) | sta_id.variant; | |
233 | ||
234 | priv->has_ibss = (firmver >= 0x000700); /* FIXME */ | |
235 | priv->has_big_wep = priv->has_wep = (firmver >= 0x000800); | |
236 | priv->has_pm = (firmver >= 0x000700); | |
237 | priv->has_hostscan = (firmver >= 0x010301); | |
238 | ||
239 | if (firmver >= 0x000800) | |
240 | priv->ibss_port = 0; | |
241 | else { | |
242 | printk(KERN_NOTICE "%s: Intersil firmware earlier " | |
243 | "than v0.8.x - several features not supported\n", | |
244 | dev->name); | |
245 | priv->ibss_port = 1; | |
246 | } | |
247 | break; | |
248 | } | |
249 | printk(KERN_DEBUG "%s: Firmware determined as %s\n", dev->name, | |
250 | priv->fw_name); | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
e9e3d010 DK |
255 | /* Read settings from EEPROM into our private structure. |
256 | * MAC address gets dropped into callers buffer */ | |
257 | int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr) | |
258 | { | |
259 | struct net_device *dev = priv->ndev; | |
260 | struct hermes_idstring nickbuf; | |
261 | hermes_t *hw = &priv->hw; | |
262 | int len; | |
263 | int err; | |
264 | u16 reclen; | |
265 | ||
266 | /* Get the MAC address */ | |
267 | err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, | |
268 | ETH_ALEN, NULL, dev_addr); | |
269 | if (err) { | |
270 | printk(KERN_WARNING "%s: failed to read MAC address!\n", | |
271 | dev->name); | |
272 | goto out; | |
273 | } | |
274 | ||
275 | printk(KERN_DEBUG "%s: MAC address %pM\n", | |
276 | dev->name, dev_addr); | |
277 | ||
278 | /* Get the station name */ | |
279 | err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, | |
280 | sizeof(nickbuf), &reclen, &nickbuf); | |
281 | if (err) { | |
282 | printk(KERN_ERR "%s: failed to read station name\n", | |
283 | dev->name); | |
284 | goto out; | |
285 | } | |
286 | if (nickbuf.len) | |
287 | len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len)); | |
288 | else | |
289 | len = min(IW_ESSID_MAX_SIZE, 2 * reclen); | |
290 | memcpy(priv->nick, &nickbuf.val, len); | |
291 | priv->nick[len] = '\0'; | |
292 | ||
293 | printk(KERN_DEBUG "%s: Station name \"%s\"\n", dev->name, priv->nick); | |
294 | ||
295 | /* Get allowed channels */ | |
296 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST, | |
297 | &priv->channel_mask); | |
298 | if (err) { | |
299 | printk(KERN_ERR "%s: failed to read channel list!\n", | |
300 | dev->name); | |
301 | goto out; | |
302 | } | |
303 | ||
304 | /* Get initial AP density */ | |
305 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, | |
306 | &priv->ap_density); | |
307 | if (err || priv->ap_density < 1 || priv->ap_density > 3) | |
308 | priv->has_sensitivity = 0; | |
309 | ||
310 | /* Get initial RTS threshold */ | |
311 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, | |
312 | &priv->rts_thresh); | |
313 | if (err) { | |
314 | printk(KERN_ERR "%s: failed to read RTS threshold!\n", | |
315 | dev->name); | |
316 | goto out; | |
317 | } | |
318 | ||
319 | /* Get initial fragmentation settings */ | |
320 | if (priv->has_mwo) | |
321 | err = hermes_read_wordrec(hw, USER_BAP, | |
322 | HERMES_RID_CNFMWOROBUST_AGERE, | |
323 | &priv->mwo_robust); | |
324 | else | |
325 | err = hermes_read_wordrec(hw, USER_BAP, | |
326 | HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, | |
327 | &priv->frag_thresh); | |
328 | if (err) { | |
329 | printk(KERN_ERR "%s: failed to read fragmentation settings!\n", | |
330 | dev->name); | |
331 | goto out; | |
332 | } | |
333 | ||
334 | /* Power management setup */ | |
335 | if (priv->has_pm) { | |
336 | priv->pm_on = 0; | |
337 | priv->pm_mcast = 1; | |
338 | err = hermes_read_wordrec(hw, USER_BAP, | |
339 | HERMES_RID_CNFMAXSLEEPDURATION, | |
340 | &priv->pm_period); | |
341 | if (err) { | |
342 | printk(KERN_ERR "%s: failed to read power management " | |
343 | "period!\n", dev->name); | |
344 | goto out; | |
345 | } | |
346 | err = hermes_read_wordrec(hw, USER_BAP, | |
347 | HERMES_RID_CNFPMHOLDOVERDURATION, | |
348 | &priv->pm_timeout); | |
349 | if (err) { | |
350 | printk(KERN_ERR "%s: failed to read power management " | |
351 | "timeout!\n", dev->name); | |
352 | goto out; | |
353 | } | |
354 | } | |
355 | ||
356 | /* Preamble setup */ | |
357 | if (priv->has_preamble) { | |
358 | err = hermes_read_wordrec(hw, USER_BAP, | |
359 | HERMES_RID_CNFPREAMBLE_SYMBOL, | |
360 | &priv->preamble); | |
361 | } | |
362 | ||
363 | out: | |
364 | return err; | |
365 | } | |
366 | ||
712a4342 DK |
367 | int orinoco_get_bitratemode(int bitrate, int automatic) |
368 | { | |
369 | int ratemode = -1; | |
370 | int i; | |
371 | ||
372 | if ((bitrate != 10) && (bitrate != 20) && | |
373 | (bitrate != 55) && (bitrate != 110)) | |
374 | return ratemode; | |
375 | ||
376 | for (i = 0; i < BITRATE_TABLE_SIZE; i++) { | |
377 | if ((bitrate_table[i].bitrate == bitrate) && | |
378 | (bitrate_table[i].automatic == automatic)) { | |
379 | ratemode = i; | |
380 | break; | |
381 | } | |
382 | } | |
383 | return ratemode; | |
384 | } | |
385 | ||
386 | void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic) | |
387 | { | |
388 | BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE)); | |
389 | ||
390 | *bitrate = bitrate_table[ratemode].bitrate * 100000; | |
391 | *automatic = bitrate_table[ratemode].automatic; | |
392 | } | |
393 | ||
394 | /* Get tsc from the firmware */ | |
395 | int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc) | |
396 | { | |
397 | hermes_t *hw = &priv->hw; | |
398 | int err = 0; | |
399 | u8 tsc_arr[4][IW_ENCODE_SEQ_MAX_SIZE]; | |
400 | ||
401 | if ((key < 0) || (key > 4)) | |
402 | return -EINVAL; | |
403 | ||
404 | err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV, | |
405 | sizeof(tsc_arr), NULL, &tsc_arr); | |
406 | if (!err) | |
407 | memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0])); | |
408 | ||
409 | return err; | |
410 | } | |
411 | ||
412 | int __orinoco_hw_set_bitrate(struct orinoco_private *priv) | |
413 | { | |
414 | hermes_t *hw = &priv->hw; | |
415 | int ratemode = priv->bitratemode; | |
416 | int err = 0; | |
417 | ||
418 | if (ratemode >= BITRATE_TABLE_SIZE) { | |
419 | printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n", | |
420 | priv->ndev->name, ratemode); | |
421 | return -EINVAL; | |
422 | } | |
423 | ||
424 | switch (priv->firmware_type) { | |
425 | case FIRMWARE_TYPE_AGERE: | |
426 | err = hermes_write_wordrec(hw, USER_BAP, | |
427 | HERMES_RID_CNFTXRATECONTROL, | |
428 | bitrate_table[ratemode].agere_txratectrl); | |
429 | break; | |
430 | case FIRMWARE_TYPE_INTERSIL: | |
431 | case FIRMWARE_TYPE_SYMBOL: | |
432 | err = hermes_write_wordrec(hw, USER_BAP, | |
433 | HERMES_RID_CNFTXRATECONTROL, | |
434 | bitrate_table[ratemode].intersil_txratectrl); | |
435 | break; | |
436 | default: | |
437 | BUG(); | |
438 | } | |
439 | ||
440 | return err; | |
441 | } | |
442 | ||
443 | int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate) | |
444 | { | |
445 | hermes_t *hw = &priv->hw; | |
446 | int i; | |
447 | int err = 0; | |
448 | u16 val; | |
449 | ||
450 | err = hermes_read_wordrec(hw, USER_BAP, | |
451 | HERMES_RID_CURRENTTXRATE, &val); | |
452 | if (err) | |
453 | return err; | |
454 | ||
455 | switch (priv->firmware_type) { | |
456 | case FIRMWARE_TYPE_AGERE: /* Lucent style rate */ | |
457 | /* Note : in Lucent firmware, the return value of | |
458 | * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s, | |
459 | * and therefore is totally different from the | |
460 | * encoding of HERMES_RID_CNFTXRATECONTROL. | |
461 | * Don't forget that 6Mb/s is really 5.5Mb/s */ | |
462 | if (val == 6) | |
463 | *bitrate = 5500000; | |
464 | else | |
465 | *bitrate = val * 1000000; | |
466 | break; | |
467 | case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */ | |
468 | case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */ | |
469 | for (i = 0; i < BITRATE_TABLE_SIZE; i++) | |
470 | if (bitrate_table[i].intersil_txratectrl == val) | |
471 | break; | |
472 | ||
473 | if (i >= BITRATE_TABLE_SIZE) | |
474 | printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n", | |
475 | priv->ndev->name, val); | |
476 | ||
477 | *bitrate = bitrate_table[i].bitrate * 100000; | |
478 | break; | |
479 | default: | |
480 | BUG(); | |
481 | } | |
482 | ||
483 | return err; | |
484 | } | |
485 | ||
486 | /* Set fixed AP address */ | |
487 | int __orinoco_hw_set_wap(struct orinoco_private *priv) | |
488 | { | |
489 | int roaming_flag; | |
490 | int err = 0; | |
491 | hermes_t *hw = &priv->hw; | |
492 | ||
493 | switch (priv->firmware_type) { | |
494 | case FIRMWARE_TYPE_AGERE: | |
495 | /* not supported */ | |
496 | break; | |
497 | case FIRMWARE_TYPE_INTERSIL: | |
498 | if (priv->bssid_fixed) | |
499 | roaming_flag = 2; | |
500 | else | |
501 | roaming_flag = 1; | |
502 | ||
503 | err = hermes_write_wordrec(hw, USER_BAP, | |
504 | HERMES_RID_CNFROAMINGMODE, | |
505 | roaming_flag); | |
506 | break; | |
507 | case FIRMWARE_TYPE_SYMBOL: | |
508 | err = HERMES_WRITE_RECORD(hw, USER_BAP, | |
509 | HERMES_RID_CNFMANDATORYBSSID_SYMBOL, | |
510 | &priv->desired_bssid); | |
511 | break; | |
512 | } | |
513 | return err; | |
514 | } | |
515 | ||
516 | /* Change the WEP keys and/or the current keys. Can be called | |
517 | * either from __orinoco_hw_setup_enc() or directly from | |
518 | * orinoco_ioctl_setiwencode(). In the later case the association | |
519 | * with the AP is not broken (if the firmware can handle it), | |
520 | * which is needed for 802.1x implementations. */ | |
521 | int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv) | |
522 | { | |
523 | hermes_t *hw = &priv->hw; | |
524 | int err = 0; | |
525 | ||
526 | switch (priv->firmware_type) { | |
527 | case FIRMWARE_TYPE_AGERE: | |
528 | err = HERMES_WRITE_RECORD(hw, USER_BAP, | |
529 | HERMES_RID_CNFWEPKEYS_AGERE, | |
530 | &priv->keys); | |
531 | if (err) | |
532 | return err; | |
533 | err = hermes_write_wordrec(hw, USER_BAP, | |
534 | HERMES_RID_CNFTXKEY_AGERE, | |
535 | priv->tx_key); | |
536 | if (err) | |
537 | return err; | |
538 | break; | |
539 | case FIRMWARE_TYPE_INTERSIL: | |
540 | case FIRMWARE_TYPE_SYMBOL: | |
541 | { | |
542 | int keylen; | |
543 | int i; | |
544 | ||
545 | /* Force uniform key length to work around | |
546 | * firmware bugs */ | |
547 | keylen = le16_to_cpu(priv->keys[priv->tx_key].len); | |
548 | ||
549 | if (keylen > LARGE_KEY_SIZE) { | |
550 | printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n", | |
551 | priv->ndev->name, priv->tx_key, keylen); | |
552 | return -E2BIG; | |
553 | } | |
554 | ||
555 | /* Write all 4 keys */ | |
556 | for (i = 0; i < ORINOCO_MAX_KEYS; i++) { | |
557 | err = hermes_write_ltv(hw, USER_BAP, | |
558 | HERMES_RID_CNFDEFAULTKEY0 + i, | |
559 | HERMES_BYTES_TO_RECLEN(keylen), | |
560 | priv->keys[i].data); | |
561 | if (err) | |
562 | return err; | |
563 | } | |
564 | ||
565 | /* Write the index of the key used in transmission */ | |
566 | err = hermes_write_wordrec(hw, USER_BAP, | |
567 | HERMES_RID_CNFWEPDEFAULTKEYID, | |
568 | priv->tx_key); | |
569 | if (err) | |
570 | return err; | |
571 | } | |
572 | break; | |
573 | } | |
574 | ||
575 | return 0; | |
576 | } | |
577 | ||
578 | int __orinoco_hw_setup_enc(struct orinoco_private *priv) | |
579 | { | |
580 | hermes_t *hw = &priv->hw; | |
581 | int err = 0; | |
582 | int master_wep_flag; | |
583 | int auth_flag; | |
584 | int enc_flag; | |
585 | ||
586 | /* Setup WEP keys for WEP and WPA */ | |
587 | if (priv->encode_alg) | |
588 | __orinoco_hw_setup_wepkeys(priv); | |
589 | ||
590 | if (priv->wep_restrict) | |
591 | auth_flag = HERMES_AUTH_SHARED_KEY; | |
592 | else | |
593 | auth_flag = HERMES_AUTH_OPEN; | |
594 | ||
595 | if (priv->wpa_enabled) | |
596 | enc_flag = 2; | |
597 | else if (priv->encode_alg == IW_ENCODE_ALG_WEP) | |
598 | enc_flag = 1; | |
599 | else | |
600 | enc_flag = 0; | |
601 | ||
602 | switch (priv->firmware_type) { | |
603 | case FIRMWARE_TYPE_AGERE: /* Agere style WEP */ | |
604 | if (priv->encode_alg == IW_ENCODE_ALG_WEP) { | |
605 | /* Enable the shared-key authentication. */ | |
606 | err = hermes_write_wordrec(hw, USER_BAP, | |
607 | HERMES_RID_CNFAUTHENTICATION_AGERE, | |
608 | auth_flag); | |
609 | } | |
610 | err = hermes_write_wordrec(hw, USER_BAP, | |
611 | HERMES_RID_CNFWEPENABLED_AGERE, | |
612 | enc_flag); | |
613 | if (err) | |
614 | return err; | |
615 | ||
616 | if (priv->has_wpa) { | |
617 | /* Set WPA key management */ | |
618 | err = hermes_write_wordrec(hw, USER_BAP, | |
619 | HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE, | |
620 | priv->key_mgmt); | |
621 | if (err) | |
622 | return err; | |
623 | } | |
624 | ||
625 | break; | |
626 | ||
627 | case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */ | |
628 | case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */ | |
629 | if (priv->encode_alg == IW_ENCODE_ALG_WEP) { | |
630 | if (priv->wep_restrict || | |
631 | (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)) | |
632 | master_wep_flag = HERMES_WEP_PRIVACY_INVOKED | | |
633 | HERMES_WEP_EXCL_UNENCRYPTED; | |
634 | else | |
635 | master_wep_flag = HERMES_WEP_PRIVACY_INVOKED; | |
636 | ||
637 | err = hermes_write_wordrec(hw, USER_BAP, | |
638 | HERMES_RID_CNFAUTHENTICATION, | |
639 | auth_flag); | |
640 | if (err) | |
641 | return err; | |
642 | } else | |
643 | master_wep_flag = 0; | |
644 | ||
645 | if (priv->iw_mode == IW_MODE_MONITOR) | |
646 | master_wep_flag |= HERMES_WEP_HOST_DECRYPT; | |
647 | ||
648 | /* Master WEP setting : on/off */ | |
649 | err = hermes_write_wordrec(hw, USER_BAP, | |
650 | HERMES_RID_CNFWEPFLAGS_INTERSIL, | |
651 | master_wep_flag); | |
652 | if (err) | |
653 | return err; | |
654 | ||
655 | break; | |
656 | } | |
657 | ||
658 | return 0; | |
659 | } | |
660 | ||
661 | /* key must be 32 bytes, including the tx and rx MIC keys. | |
662 | * rsc must be 8 bytes | |
663 | * tsc must be 8 bytes or NULL | |
664 | */ | |
665 | int __orinoco_hw_set_tkip_key(hermes_t *hw, int key_idx, int set_tx, | |
666 | u8 *key, u8 *rsc, u8 *tsc) | |
667 | { | |
668 | struct { | |
669 | __le16 idx; | |
670 | u8 rsc[IW_ENCODE_SEQ_MAX_SIZE]; | |
671 | u8 key[TKIP_KEYLEN]; | |
672 | u8 tx_mic[MIC_KEYLEN]; | |
673 | u8 rx_mic[MIC_KEYLEN]; | |
674 | u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; | |
675 | } __attribute__ ((packed)) buf; | |
676 | int ret; | |
677 | int err; | |
678 | int k; | |
679 | u16 xmitting; | |
680 | ||
681 | key_idx &= 0x3; | |
682 | ||
683 | if (set_tx) | |
684 | key_idx |= 0x8000; | |
685 | ||
686 | buf.idx = cpu_to_le16(key_idx); | |
687 | memcpy(buf.key, key, | |
688 | sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic)); | |
689 | ||
690 | if (rsc == NULL) | |
691 | memset(buf.rsc, 0, sizeof(buf.rsc)); | |
692 | else | |
693 | memcpy(buf.rsc, rsc, sizeof(buf.rsc)); | |
694 | ||
695 | if (tsc == NULL) { | |
696 | memset(buf.tsc, 0, sizeof(buf.tsc)); | |
697 | buf.tsc[4] = 0x10; | |
698 | } else { | |
699 | memcpy(buf.tsc, tsc, sizeof(buf.tsc)); | |
700 | } | |
701 | ||
702 | /* Wait upto 100ms for tx queue to empty */ | |
91fe9ca7 | 703 | for (k = 100; k > 0; k--) { |
712a4342 DK |
704 | udelay(1000); |
705 | ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY, | |
706 | &xmitting); | |
91fe9ca7 | 707 | if (ret || !xmitting) |
712a4342 | 708 | break; |
91fe9ca7 | 709 | } |
712a4342 DK |
710 | |
711 | if (k == 0) | |
712 | ret = -ETIMEDOUT; | |
713 | ||
714 | err = HERMES_WRITE_RECORD(hw, USER_BAP, | |
715 | HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE, | |
716 | &buf); | |
717 | ||
718 | return ret ? ret : err; | |
719 | } | |
720 | ||
721 | int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx) | |
722 | { | |
723 | hermes_t *hw = &priv->hw; | |
724 | int err; | |
725 | ||
726 | memset(&priv->tkip_key[key_idx], 0, sizeof(priv->tkip_key[key_idx])); | |
727 | err = hermes_write_wordrec(hw, USER_BAP, | |
728 | HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE, | |
729 | key_idx); | |
730 | if (err) | |
731 | printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n", | |
732 | priv->ndev->name, err, key_idx); | |
733 | return err; | |
734 | } | |
735 | ||
736 | int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, | |
737 | struct dev_addr_list *mc_list, | |
738 | int mc_count, int promisc) | |
739 | { | |
740 | hermes_t *hw = &priv->hw; | |
741 | int err = 0; | |
742 | ||
743 | if (promisc != priv->promiscuous) { | |
744 | err = hermes_write_wordrec(hw, USER_BAP, | |
745 | HERMES_RID_CNFPROMISCUOUSMODE, | |
746 | promisc); | |
747 | if (err) { | |
748 | printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n", | |
749 | priv->ndev->name, err); | |
750 | } else | |
751 | priv->promiscuous = promisc; | |
752 | } | |
753 | ||
754 | /* If we're not in promiscuous mode, then we need to set the | |
755 | * group address if either we want to multicast, or if we were | |
756 | * multicasting and want to stop */ | |
757 | if (!promisc && (mc_count || priv->mc_count)) { | |
758 | struct dev_mc_list *p = mc_list; | |
759 | struct hermes_multicast mclist; | |
760 | int i; | |
761 | ||
762 | for (i = 0; i < mc_count; i++) { | |
763 | /* paranoia: is list shorter than mc_count? */ | |
764 | BUG_ON(!p); | |
765 | /* paranoia: bad address size in list? */ | |
766 | BUG_ON(p->dmi_addrlen != ETH_ALEN); | |
767 | ||
768 | memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN); | |
769 | p = p->next; | |
770 | } | |
771 | ||
772 | if (p) | |
773 | printk(KERN_WARNING "%s: Multicast list is " | |
774 | "longer than mc_count\n", priv->ndev->name); | |
775 | ||
776 | err = hermes_write_ltv(hw, USER_BAP, | |
777 | HERMES_RID_CNFGROUPADDRESSES, | |
778 | HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN), | |
779 | &mclist); | |
780 | if (err) | |
781 | printk(KERN_ERR "%s: Error %d setting multicast list.\n", | |
782 | priv->ndev->name, err); | |
783 | else | |
784 | priv->mc_count = mc_count; | |
785 | } | |
786 | return err; | |
787 | } | |
788 | ||
789 | /* Return : < 0 -> error code ; >= 0 -> length */ | |
790 | int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, | |
791 | char buf[IW_ESSID_MAX_SIZE+1]) | |
792 | { | |
793 | hermes_t *hw = &priv->hw; | |
794 | int err = 0; | |
795 | struct hermes_idstring essidbuf; | |
796 | char *p = (char *)(&essidbuf.val); | |
797 | int len; | |
798 | unsigned long flags; | |
799 | ||
800 | if (orinoco_lock(priv, &flags) != 0) | |
801 | return -EBUSY; | |
802 | ||
803 | if (strlen(priv->desired_essid) > 0) { | |
804 | /* We read the desired SSID from the hardware rather | |
805 | than from priv->desired_essid, just in case the | |
806 | firmware is allowed to change it on us. I'm not | |
807 | sure about this */ | |
808 | /* My guess is that the OWNSSID should always be whatever | |
809 | * we set to the card, whereas CURRENT_SSID is the one that | |
810 | * may change... - Jean II */ | |
811 | u16 rid; | |
812 | ||
813 | *active = 1; | |
814 | ||
815 | rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID : | |
816 | HERMES_RID_CNFDESIREDSSID; | |
817 | ||
818 | err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf), | |
819 | NULL, &essidbuf); | |
820 | if (err) | |
821 | goto fail_unlock; | |
822 | } else { | |
823 | *active = 0; | |
824 | ||
825 | err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID, | |
826 | sizeof(essidbuf), NULL, &essidbuf); | |
827 | if (err) | |
828 | goto fail_unlock; | |
829 | } | |
830 | ||
831 | len = le16_to_cpu(essidbuf.len); | |
832 | BUG_ON(len > IW_ESSID_MAX_SIZE); | |
833 | ||
834 | memset(buf, 0, IW_ESSID_MAX_SIZE); | |
835 | memcpy(buf, p, len); | |
836 | err = len; | |
837 | ||
838 | fail_unlock: | |
839 | orinoco_unlock(priv, &flags); | |
840 | ||
841 | return err; | |
842 | } | |
843 | ||
844 | int orinoco_hw_get_freq(struct orinoco_private *priv) | |
845 | { | |
846 | hermes_t *hw = &priv->hw; | |
847 | int err = 0; | |
848 | u16 channel; | |
849 | int freq = 0; | |
850 | unsigned long flags; | |
851 | ||
852 | if (orinoco_lock(priv, &flags) != 0) | |
853 | return -EBUSY; | |
854 | ||
855 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, | |
856 | &channel); | |
857 | if (err) | |
858 | goto out; | |
859 | ||
860 | /* Intersil firmware 1.3.5 returns 0 when the interface is down */ | |
861 | if (channel == 0) { | |
862 | err = -EBUSY; | |
863 | goto out; | |
864 | } | |
865 | ||
866 | if ((channel < 1) || (channel > NUM_CHANNELS)) { | |
867 | printk(KERN_WARNING "%s: Channel out of range (%d)!\n", | |
868 | priv->ndev->name, channel); | |
869 | err = -EBUSY; | |
870 | goto out; | |
871 | ||
872 | } | |
873 | freq = ieee80211_dsss_chan_to_freq(channel); | |
874 | ||
875 | out: | |
876 | orinoco_unlock(priv, &flags); | |
877 | ||
878 | if (err > 0) | |
879 | err = -EBUSY; | |
880 | return err ? err : freq; | |
881 | } | |
882 | ||
883 | int orinoco_hw_get_bitratelist(struct orinoco_private *priv, | |
884 | int *numrates, s32 *rates, int max) | |
885 | { | |
886 | hermes_t *hw = &priv->hw; | |
887 | struct hermes_idstring list; | |
888 | unsigned char *p = (unsigned char *)&list.val; | |
889 | int err = 0; | |
890 | int num; | |
891 | int i; | |
892 | unsigned long flags; | |
893 | ||
894 | if (orinoco_lock(priv, &flags) != 0) | |
895 | return -EBUSY; | |
896 | ||
897 | err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES, | |
898 | sizeof(list), NULL, &list); | |
899 | orinoco_unlock(priv, &flags); | |
900 | ||
901 | if (err) | |
902 | return err; | |
903 | ||
904 | num = le16_to_cpu(list.len); | |
905 | *numrates = num; | |
906 | num = min(num, max); | |
907 | ||
908 | for (i = 0; i < num; i++) | |
909 | rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */ | |
910 | ||
911 | return 0; | |
912 | } |