]>
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> | |
a3f47b9c | 6 | #include <linux/device.h> |
712a4342 DK |
7 | #include <linux/if_arp.h> |
8 | #include <linux/ieee80211.h> | |
9 | #include <linux/wireless.h> | |
5217c571 | 10 | #include <net/cfg80211.h> |
712a4342 DK |
11 | #include "hermes.h" |
12 | #include "hermes_rid.h" | |
13 | #include "orinoco.h" | |
14 | ||
15 | #include "hw.h" | |
16 | ||
a2d1a42a DK |
17 | #define SYMBOL_MAX_VER_LEN (14) |
18 | ||
42a51b93 DK |
19 | /* Symbol firmware has a bug allocating buffers larger than this */ |
20 | #define TX_NICBUF_SIZE_BUG 1585 | |
21 | ||
712a4342 DK |
22 | /********************************************************************/ |
23 | /* Data tables */ | |
24 | /********************************************************************/ | |
25 | ||
26 | /* This tables gives the actual meanings of the bitrate IDs returned | |
27 | * by the firmware. */ | |
28 | static const struct { | |
29 | int bitrate; /* in 100s of kilobits */ | |
30 | int automatic; | |
31 | u16 agere_txratectrl; | |
32 | u16 intersil_txratectrl; | |
33 | } bitrate_table[] = { | |
34 | {110, 1, 3, 15}, /* Entry 0 is the default */ | |
35 | {10, 0, 1, 1}, | |
36 | {10, 1, 1, 1}, | |
37 | {20, 0, 2, 2}, | |
38 | {20, 1, 6, 3}, | |
39 | {55, 0, 4, 4}, | |
40 | {55, 1, 7, 7}, | |
41 | {110, 0, 5, 8}, | |
42 | }; | |
43 | #define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table) | |
44 | ||
a2d1a42a DK |
45 | /* Firmware version encoding */ |
46 | struct comp_id { | |
47 | u16 id, variant, major, minor; | |
ba2d3587 | 48 | } __packed; |
a2d1a42a DK |
49 | |
50 | static inline fwtype_t determine_firmware_type(struct comp_id *nic_id) | |
51 | { | |
52 | if (nic_id->id < 0x8000) | |
53 | return FIRMWARE_TYPE_AGERE; | |
54 | else if (nic_id->id == 0x8000 && nic_id->major == 0) | |
55 | return FIRMWARE_TYPE_SYMBOL; | |
56 | else | |
57 | return FIRMWARE_TYPE_INTERSIL; | |
58 | } | |
59 | ||
a3f47b9c DK |
60 | /* Set priv->firmware type, determine firmware properties |
61 | * This function can be called before we have registerred with netdev, | |
62 | * so all errors go out with dev_* rather than printk | |
3414fc3f DK |
63 | * |
64 | * If non-NULL stores a firmware description in fw_name. | |
65 | * If non-NULL stores a HW version in hw_ver | |
66 | * | |
67 | * These are output via generic cfg80211 ethtool support. | |
a3f47b9c | 68 | */ |
3414fc3f DK |
69 | int determine_fw_capabilities(struct orinoco_private *priv, |
70 | char *fw_name, size_t fw_name_len, | |
71 | u32 *hw_ver) | |
a2d1a42a | 72 | { |
a3f47b9c | 73 | struct device *dev = priv->dev; |
a2d1a42a DK |
74 | hermes_t *hw = &priv->hw; |
75 | int err; | |
76 | struct comp_id nic_id, sta_id; | |
77 | unsigned int firmver; | |
78 | char tmp[SYMBOL_MAX_VER_LEN+1] __attribute__((aligned(2))); | |
79 | ||
80 | /* Get the hardware version */ | |
81 | err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id); | |
82 | if (err) { | |
a3f47b9c DK |
83 | dev_err(dev, "Cannot read hardware identity: error %d\n", |
84 | err); | |
a2d1a42a DK |
85 | return err; |
86 | } | |
87 | ||
88 | le16_to_cpus(&nic_id.id); | |
89 | le16_to_cpus(&nic_id.variant); | |
90 | le16_to_cpus(&nic_id.major); | |
91 | le16_to_cpus(&nic_id.minor); | |
a3f47b9c DK |
92 | dev_info(dev, "Hardware identity %04x:%04x:%04x:%04x\n", |
93 | nic_id.id, nic_id.variant, nic_id.major, nic_id.minor); | |
a2d1a42a | 94 | |
3414fc3f DK |
95 | if (hw_ver) |
96 | *hw_ver = (((nic_id.id & 0xff) << 24) | | |
97 | ((nic_id.variant & 0xff) << 16) | | |
98 | ((nic_id.major & 0xff) << 8) | | |
99 | (nic_id.minor & 0xff)); | |
100 | ||
a2d1a42a DK |
101 | priv->firmware_type = determine_firmware_type(&nic_id); |
102 | ||
103 | /* Get the firmware version */ | |
104 | err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id); | |
105 | if (err) { | |
a3f47b9c DK |
106 | dev_err(dev, "Cannot read station identity: error %d\n", |
107 | err); | |
a2d1a42a DK |
108 | return err; |
109 | } | |
110 | ||
111 | le16_to_cpus(&sta_id.id); | |
112 | le16_to_cpus(&sta_id.variant); | |
113 | le16_to_cpus(&sta_id.major); | |
114 | le16_to_cpus(&sta_id.minor); | |
a3f47b9c DK |
115 | dev_info(dev, "Station identity %04x:%04x:%04x:%04x\n", |
116 | sta_id.id, sta_id.variant, sta_id.major, sta_id.minor); | |
a2d1a42a DK |
117 | |
118 | switch (sta_id.id) { | |
119 | case 0x15: | |
a3f47b9c | 120 | dev_err(dev, "Primary firmware is active\n"); |
a2d1a42a DK |
121 | return -ENODEV; |
122 | case 0x14b: | |
a3f47b9c | 123 | dev_err(dev, "Tertiary firmware is active\n"); |
a2d1a42a DK |
124 | return -ENODEV; |
125 | case 0x1f: /* Intersil, Agere, Symbol Spectrum24 */ | |
126 | case 0x21: /* Symbol Spectrum24 Trilogy */ | |
127 | break; | |
128 | default: | |
a3f47b9c | 129 | dev_notice(dev, "Unknown station ID, please report\n"); |
a2d1a42a DK |
130 | break; |
131 | } | |
132 | ||
133 | /* Default capabilities */ | |
134 | priv->has_sensitivity = 1; | |
135 | priv->has_mwo = 0; | |
136 | priv->has_preamble = 0; | |
137 | priv->has_port3 = 1; | |
138 | priv->has_ibss = 1; | |
139 | priv->has_wep = 0; | |
140 | priv->has_big_wep = 0; | |
141 | priv->has_alt_txcntl = 0; | |
142 | priv->has_ext_scan = 0; | |
143 | priv->has_wpa = 0; | |
144 | priv->do_fw_download = 0; | |
145 | ||
146 | /* Determine capabilities from the firmware version */ | |
147 | switch (priv->firmware_type) { | |
148 | case FIRMWARE_TYPE_AGERE: | |
149 | /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout, | |
150 | ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */ | |
3414fc3f DK |
151 | if (fw_name) |
152 | snprintf(fw_name, fw_name_len, "Lucent/Agere %d.%02d", | |
153 | sta_id.major, sta_id.minor); | |
a2d1a42a DK |
154 | |
155 | firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor; | |
156 | ||
157 | priv->has_ibss = (firmver >= 0x60006); | |
158 | priv->has_wep = (firmver >= 0x40020); | |
159 | priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell | |
160 | Gold cards from the others? */ | |
161 | priv->has_mwo = (firmver >= 0x60000); | |
162 | priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ | |
163 | priv->ibss_port = 1; | |
164 | priv->has_hostscan = (firmver >= 0x8000a); | |
165 | priv->do_fw_download = 1; | |
166 | priv->broken_monitor = (firmver >= 0x80000); | |
167 | priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */ | |
168 | priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */ | |
169 | priv->has_wpa = (firmver >= 0x9002a); | |
170 | /* Tested with Agere firmware : | |
171 | * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II | |
172 | * Tested CableTron firmware : 4.32 => Anton */ | |
173 | break; | |
174 | case FIRMWARE_TYPE_SYMBOL: | |
175 | /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */ | |
176 | /* Intel MAC : 00:02:B3:* */ | |
177 | /* 3Com MAC : 00:50:DA:* */ | |
178 | memset(tmp, 0, sizeof(tmp)); | |
179 | /* Get the Symbol firmware version */ | |
b42f2074 DK |
180 | err = hw->ops->read_ltv(hw, USER_BAP, |
181 | HERMES_RID_SECONDARYVERSION_SYMBOL, | |
182 | SYMBOL_MAX_VER_LEN, NULL, &tmp); | |
a2d1a42a | 183 | if (err) { |
a3f47b9c DK |
184 | dev_warn(dev, "Error %d reading Symbol firmware info. " |
185 | "Wildly guessing capabilities...\n", err); | |
a2d1a42a DK |
186 | firmver = 0; |
187 | tmp[0] = '\0'; | |
188 | } else { | |
189 | /* The firmware revision is a string, the format is | |
190 | * something like : "V2.20-01". | |
191 | * Quick and dirty parsing... - Jean II | |
192 | */ | |
193 | firmver = ((tmp[1] - '0') << 16) | |
194 | | ((tmp[3] - '0') << 12) | |
195 | | ((tmp[4] - '0') << 8) | |
196 | | ((tmp[6] - '0') << 4) | |
197 | | (tmp[7] - '0'); | |
198 | ||
199 | tmp[SYMBOL_MAX_VER_LEN] = '\0'; | |
200 | } | |
201 | ||
3414fc3f DK |
202 | if (fw_name) |
203 | snprintf(fw_name, fw_name_len, "Symbol %s", tmp); | |
a2d1a42a DK |
204 | |
205 | priv->has_ibss = (firmver >= 0x20000); | |
206 | priv->has_wep = (firmver >= 0x15012); | |
207 | priv->has_big_wep = (firmver >= 0x20000); | |
208 | priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) || | |
209 | (firmver >= 0x29000 && firmver < 0x30000) || | |
210 | firmver >= 0x31000; | |
211 | priv->has_preamble = (firmver >= 0x20000); | |
212 | priv->ibss_port = 4; | |
213 | ||
214 | /* Symbol firmware is found on various cards, but | |
215 | * there has been no attempt to check firmware | |
216 | * download on non-spectrum_cs based cards. | |
217 | * | |
218 | * Given that the Agere firmware download works | |
219 | * differently, we should avoid doing a firmware | |
220 | * download with the Symbol algorithm on non-spectrum | |
221 | * cards. | |
222 | * | |
223 | * For now we can identify a spectrum_cs based card | |
224 | * because it has a firmware reset function. | |
225 | */ | |
226 | priv->do_fw_download = (priv->stop_fw != NULL); | |
227 | ||
228 | priv->broken_disableport = (firmver == 0x25013) || | |
229 | (firmver >= 0x30000 && firmver <= 0x31000); | |
230 | priv->has_hostscan = (firmver >= 0x31001) || | |
231 | (firmver >= 0x29057 && firmver < 0x30000); | |
232 | /* Tested with Intel firmware : 0x20015 => Jean II */ | |
233 | /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */ | |
234 | break; | |
235 | case FIRMWARE_TYPE_INTERSIL: | |
236 | /* D-Link, Linksys, Adtron, ZoomAir, and many others... | |
237 | * Samsung, Compaq 100/200 and Proxim are slightly | |
238 | * different and less well tested */ | |
239 | /* D-Link MAC : 00:40:05:* */ | |
240 | /* Addtron MAC : 00:90:D1:* */ | |
3414fc3f DK |
241 | if (fw_name) |
242 | snprintf(fw_name, fw_name_len, "Intersil %d.%d.%d", | |
243 | sta_id.major, sta_id.minor, sta_id.variant); | |
a2d1a42a DK |
244 | |
245 | firmver = ((unsigned long)sta_id.major << 16) | | |
246 | ((unsigned long)sta_id.minor << 8) | sta_id.variant; | |
247 | ||
248 | priv->has_ibss = (firmver >= 0x000700); /* FIXME */ | |
249 | priv->has_big_wep = priv->has_wep = (firmver >= 0x000800); | |
250 | priv->has_pm = (firmver >= 0x000700); | |
251 | priv->has_hostscan = (firmver >= 0x010301); | |
252 | ||
253 | if (firmver >= 0x000800) | |
254 | priv->ibss_port = 0; | |
255 | else { | |
a3f47b9c DK |
256 | dev_notice(dev, "Intersil firmware earlier than v0.8.x" |
257 | " - several features not supported\n"); | |
a2d1a42a DK |
258 | priv->ibss_port = 1; |
259 | } | |
260 | break; | |
261 | } | |
3414fc3f DK |
262 | if (fw_name) |
263 | dev_info(dev, "Firmware determined as %s\n", fw_name); | |
a2d1a42a | 264 | |
484b4dd5 PR |
265 | #ifndef CONFIG_HERMES_PRISM |
266 | if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL) { | |
267 | dev_err(dev, "Support for Prism chipset is not enabled\n"); | |
268 | return -ENODEV; | |
269 | } | |
270 | #endif | |
271 | ||
a2d1a42a DK |
272 | return 0; |
273 | } | |
274 | ||
e9e3d010 | 275 | /* Read settings from EEPROM into our private structure. |
a3f47b9c DK |
276 | * MAC address gets dropped into callers buffer |
277 | * Can be called before netdev registration. | |
278 | */ | |
e9e3d010 DK |
279 | int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr) |
280 | { | |
a3f47b9c | 281 | struct device *dev = priv->dev; |
e9e3d010 DK |
282 | struct hermes_idstring nickbuf; |
283 | hermes_t *hw = &priv->hw; | |
284 | int len; | |
285 | int err; | |
286 | u16 reclen; | |
287 | ||
288 | /* Get the MAC address */ | |
b42f2074 DK |
289 | err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, |
290 | ETH_ALEN, NULL, dev_addr); | |
e9e3d010 | 291 | if (err) { |
a3f47b9c | 292 | dev_warn(dev, "Failed to read MAC address!\n"); |
e9e3d010 DK |
293 | goto out; |
294 | } | |
295 | ||
a3f47b9c | 296 | dev_dbg(dev, "MAC address %pM\n", dev_addr); |
e9e3d010 DK |
297 | |
298 | /* Get the station name */ | |
b42f2074 DK |
299 | err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, |
300 | sizeof(nickbuf), &reclen, &nickbuf); | |
e9e3d010 | 301 | if (err) { |
a3f47b9c | 302 | dev_err(dev, "failed to read station name\n"); |
e9e3d010 DK |
303 | goto out; |
304 | } | |
305 | if (nickbuf.len) | |
306 | len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len)); | |
307 | else | |
308 | len = min(IW_ESSID_MAX_SIZE, 2 * reclen); | |
309 | memcpy(priv->nick, &nickbuf.val, len); | |
310 | priv->nick[len] = '\0'; | |
311 | ||
a3f47b9c | 312 | dev_dbg(dev, "Station name \"%s\"\n", priv->nick); |
e9e3d010 DK |
313 | |
314 | /* Get allowed channels */ | |
315 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST, | |
316 | &priv->channel_mask); | |
317 | if (err) { | |
a3f47b9c | 318 | dev_err(dev, "Failed to read channel list!\n"); |
e9e3d010 DK |
319 | goto out; |
320 | } | |
321 | ||
322 | /* Get initial AP density */ | |
323 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, | |
324 | &priv->ap_density); | |
325 | if (err || priv->ap_density < 1 || priv->ap_density > 3) | |
326 | priv->has_sensitivity = 0; | |
327 | ||
328 | /* Get initial RTS threshold */ | |
329 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, | |
330 | &priv->rts_thresh); | |
331 | if (err) { | |
a3f47b9c | 332 | dev_err(dev, "Failed to read RTS threshold!\n"); |
e9e3d010 DK |
333 | goto out; |
334 | } | |
335 | ||
336 | /* Get initial fragmentation settings */ | |
337 | if (priv->has_mwo) | |
338 | err = hermes_read_wordrec(hw, USER_BAP, | |
339 | HERMES_RID_CNFMWOROBUST_AGERE, | |
340 | &priv->mwo_robust); | |
341 | else | |
342 | err = hermes_read_wordrec(hw, USER_BAP, | |
343 | HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, | |
344 | &priv->frag_thresh); | |
345 | if (err) { | |
a3f47b9c | 346 | dev_err(dev, "Failed to read fragmentation settings!\n"); |
e9e3d010 DK |
347 | goto out; |
348 | } | |
349 | ||
350 | /* Power management setup */ | |
351 | if (priv->has_pm) { | |
352 | priv->pm_on = 0; | |
353 | priv->pm_mcast = 1; | |
354 | err = hermes_read_wordrec(hw, USER_BAP, | |
355 | HERMES_RID_CNFMAXSLEEPDURATION, | |
356 | &priv->pm_period); | |
357 | if (err) { | |
a3f47b9c DK |
358 | dev_err(dev, "Failed to read power management " |
359 | "period!\n"); | |
e9e3d010 DK |
360 | goto out; |
361 | } | |
362 | err = hermes_read_wordrec(hw, USER_BAP, | |
363 | HERMES_RID_CNFPMHOLDOVERDURATION, | |
364 | &priv->pm_timeout); | |
365 | if (err) { | |
a3f47b9c DK |
366 | dev_err(dev, "Failed to read power management " |
367 | "timeout!\n"); | |
e9e3d010 DK |
368 | goto out; |
369 | } | |
370 | } | |
371 | ||
372 | /* Preamble setup */ | |
373 | if (priv->has_preamble) { | |
374 | err = hermes_read_wordrec(hw, USER_BAP, | |
375 | HERMES_RID_CNFPREAMBLE_SYMBOL, | |
376 | &priv->preamble); | |
c3d41503 DK |
377 | if (err) { |
378 | dev_err(dev, "Failed to read preamble setup\n"); | |
379 | goto out; | |
380 | } | |
381 | } | |
382 | ||
383 | /* Retry settings */ | |
384 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT, | |
385 | &priv->short_retry_limit); | |
386 | if (err) { | |
387 | dev_err(dev, "Failed to read short retry limit\n"); | |
388 | goto out; | |
389 | } | |
390 | ||
391 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT, | |
392 | &priv->long_retry_limit); | |
393 | if (err) { | |
394 | dev_err(dev, "Failed to read long retry limit\n"); | |
395 | goto out; | |
396 | } | |
397 | ||
398 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME, | |
399 | &priv->retry_lifetime); | |
400 | if (err) { | |
401 | dev_err(dev, "Failed to read max retry lifetime\n"); | |
402 | goto out; | |
e9e3d010 DK |
403 | } |
404 | ||
405 | out: | |
406 | return err; | |
407 | } | |
408 | ||
a3f47b9c | 409 | /* Can be called before netdev registration */ |
42a51b93 DK |
410 | int orinoco_hw_allocate_fid(struct orinoco_private *priv) |
411 | { | |
a3f47b9c | 412 | struct device *dev = priv->dev; |
42a51b93 DK |
413 | struct hermes *hw = &priv->hw; |
414 | int err; | |
415 | ||
b42f2074 | 416 | err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid); |
42a51b93 DK |
417 | if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) { |
418 | /* Try workaround for old Symbol firmware bug */ | |
419 | priv->nicbuf_size = TX_NICBUF_SIZE_BUG; | |
b42f2074 | 420 | err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid); |
42a51b93 | 421 | |
a3f47b9c DK |
422 | dev_warn(dev, "Firmware ALLOC bug detected " |
423 | "(old Symbol firmware?). Work around %s\n", | |
424 | err ? "failed!" : "ok."); | |
42a51b93 DK |
425 | } |
426 | ||
427 | return err; | |
428 | } | |
429 | ||
712a4342 DK |
430 | int orinoco_get_bitratemode(int bitrate, int automatic) |
431 | { | |
432 | int ratemode = -1; | |
433 | int i; | |
434 | ||
435 | if ((bitrate != 10) && (bitrate != 20) && | |
436 | (bitrate != 55) && (bitrate != 110)) | |
437 | return ratemode; | |
438 | ||
439 | for (i = 0; i < BITRATE_TABLE_SIZE; i++) { | |
440 | if ((bitrate_table[i].bitrate == bitrate) && | |
441 | (bitrate_table[i].automatic == automatic)) { | |
442 | ratemode = i; | |
443 | break; | |
444 | } | |
445 | } | |
446 | return ratemode; | |
447 | } | |
448 | ||
449 | void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic) | |
450 | { | |
451 | BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE)); | |
452 | ||
453 | *bitrate = bitrate_table[ratemode].bitrate * 100000; | |
454 | *automatic = bitrate_table[ratemode].automatic; | |
455 | } | |
456 | ||
721aa2f7 DK |
457 | int orinoco_hw_program_rids(struct orinoco_private *priv) |
458 | { | |
459 | struct net_device *dev = priv->ndev; | |
5217c571 | 460 | struct wireless_dev *wdev = netdev_priv(dev); |
721aa2f7 DK |
461 | hermes_t *hw = &priv->hw; |
462 | int err; | |
463 | struct hermes_idstring idbuf; | |
464 | ||
465 | /* Set the MAC address */ | |
b42f2074 DK |
466 | err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, |
467 | HERMES_BYTES_TO_RECLEN(ETH_ALEN), | |
468 | dev->dev_addr); | |
721aa2f7 DK |
469 | if (err) { |
470 | printk(KERN_ERR "%s: Error %d setting MAC address\n", | |
471 | dev->name, err); | |
472 | return err; | |
473 | } | |
474 | ||
475 | /* Set up the link mode */ | |
476 | err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, | |
477 | priv->port_type); | |
478 | if (err) { | |
479 | printk(KERN_ERR "%s: Error %d setting port type\n", | |
480 | dev->name, err); | |
481 | return err; | |
482 | } | |
483 | /* Set the channel/frequency */ | |
5217c571 | 484 | if (priv->channel != 0 && priv->iw_mode != NL80211_IFTYPE_STATION) { |
721aa2f7 DK |
485 | err = hermes_write_wordrec(hw, USER_BAP, |
486 | HERMES_RID_CNFOWNCHANNEL, | |
487 | priv->channel); | |
488 | if (err) { | |
489 | printk(KERN_ERR "%s: Error %d setting channel %d\n", | |
490 | dev->name, err, priv->channel); | |
491 | return err; | |
492 | } | |
493 | } | |
494 | ||
495 | if (priv->has_ibss) { | |
496 | u16 createibss; | |
497 | ||
498 | if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) { | |
499 | printk(KERN_WARNING "%s: This firmware requires an " | |
500 | "ESSID in IBSS-Ad-Hoc mode.\n", dev->name); | |
501 | /* With wvlan_cs, in this case, we would crash. | |
502 | * hopefully, this driver will behave better... | |
503 | * Jean II */ | |
504 | createibss = 0; | |
505 | } else { | |
506 | createibss = priv->createibss; | |
507 | } | |
508 | ||
509 | err = hermes_write_wordrec(hw, USER_BAP, | |
510 | HERMES_RID_CNFCREATEIBSS, | |
511 | createibss); | |
512 | if (err) { | |
513 | printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n", | |
514 | dev->name, err); | |
515 | return err; | |
516 | } | |
517 | } | |
518 | ||
519 | /* Set the desired BSSID */ | |
520 | err = __orinoco_hw_set_wap(priv); | |
521 | if (err) { | |
522 | printk(KERN_ERR "%s: Error %d setting AP address\n", | |
523 | dev->name, err); | |
524 | return err; | |
525 | } | |
526 | ||
527 | /* Set the desired ESSID */ | |
528 | idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); | |
529 | memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); | |
530 | /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */ | |
b42f2074 | 531 | err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID, |
721aa2f7 DK |
532 | HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), |
533 | &idbuf); | |
534 | if (err) { | |
535 | printk(KERN_ERR "%s: Error %d setting OWNSSID\n", | |
536 | dev->name, err); | |
537 | return err; | |
538 | } | |
b42f2074 | 539 | err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID, |
721aa2f7 DK |
540 | HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), |
541 | &idbuf); | |
542 | if (err) { | |
543 | printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n", | |
544 | dev->name, err); | |
545 | return err; | |
546 | } | |
547 | ||
548 | /* Set the station name */ | |
549 | idbuf.len = cpu_to_le16(strlen(priv->nick)); | |
550 | memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val)); | |
b42f2074 DK |
551 | err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, |
552 | HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2), | |
553 | &idbuf); | |
721aa2f7 DK |
554 | if (err) { |
555 | printk(KERN_ERR "%s: Error %d setting nickname\n", | |
556 | dev->name, err); | |
557 | return err; | |
558 | } | |
559 | ||
560 | /* Set AP density */ | |
561 | if (priv->has_sensitivity) { | |
562 | err = hermes_write_wordrec(hw, USER_BAP, | |
563 | HERMES_RID_CNFSYSTEMSCALE, | |
564 | priv->ap_density); | |
565 | if (err) { | |
566 | printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. " | |
567 | "Disabling sensitivity control\n", | |
568 | dev->name, err); | |
569 | ||
570 | priv->has_sensitivity = 0; | |
571 | } | |
572 | } | |
573 | ||
574 | /* Set RTS threshold */ | |
575 | err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, | |
576 | priv->rts_thresh); | |
577 | if (err) { | |
578 | printk(KERN_ERR "%s: Error %d setting RTS threshold\n", | |
579 | dev->name, err); | |
580 | return err; | |
581 | } | |
582 | ||
583 | /* Set fragmentation threshold or MWO robustness */ | |
584 | if (priv->has_mwo) | |
585 | err = hermes_write_wordrec(hw, USER_BAP, | |
586 | HERMES_RID_CNFMWOROBUST_AGERE, | |
587 | priv->mwo_robust); | |
588 | else | |
589 | err = hermes_write_wordrec(hw, USER_BAP, | |
590 | HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, | |
591 | priv->frag_thresh); | |
592 | if (err) { | |
593 | printk(KERN_ERR "%s: Error %d setting fragmentation\n", | |
594 | dev->name, err); | |
595 | return err; | |
596 | } | |
597 | ||
598 | /* Set bitrate */ | |
599 | err = __orinoco_hw_set_bitrate(priv); | |
600 | if (err) { | |
601 | printk(KERN_ERR "%s: Error %d setting bitrate\n", | |
602 | dev->name, err); | |
603 | return err; | |
604 | } | |
605 | ||
606 | /* Set power management */ | |
607 | if (priv->has_pm) { | |
608 | err = hermes_write_wordrec(hw, USER_BAP, | |
609 | HERMES_RID_CNFPMENABLED, | |
610 | priv->pm_on); | |
611 | if (err) { | |
612 | printk(KERN_ERR "%s: Error %d setting up PM\n", | |
613 | dev->name, err); | |
614 | return err; | |
615 | } | |
616 | ||
617 | err = hermes_write_wordrec(hw, USER_BAP, | |
618 | HERMES_RID_CNFMULTICASTRECEIVE, | |
619 | priv->pm_mcast); | |
620 | if (err) { | |
621 | printk(KERN_ERR "%s: Error %d setting up PM\n", | |
622 | dev->name, err); | |
623 | return err; | |
624 | } | |
625 | err = hermes_write_wordrec(hw, USER_BAP, | |
626 | HERMES_RID_CNFMAXSLEEPDURATION, | |
627 | priv->pm_period); | |
628 | if (err) { | |
629 | printk(KERN_ERR "%s: Error %d setting up PM\n", | |
630 | dev->name, err); | |
631 | return err; | |
632 | } | |
633 | err = hermes_write_wordrec(hw, USER_BAP, | |
634 | HERMES_RID_CNFPMHOLDOVERDURATION, | |
635 | priv->pm_timeout); | |
636 | if (err) { | |
637 | printk(KERN_ERR "%s: Error %d setting up PM\n", | |
638 | dev->name, err); | |
639 | return err; | |
640 | } | |
641 | } | |
642 | ||
643 | /* Set preamble - only for Symbol so far... */ | |
644 | if (priv->has_preamble) { | |
645 | err = hermes_write_wordrec(hw, USER_BAP, | |
646 | HERMES_RID_CNFPREAMBLE_SYMBOL, | |
647 | priv->preamble); | |
648 | if (err) { | |
649 | printk(KERN_ERR "%s: Error %d setting preamble\n", | |
650 | dev->name, err); | |
651 | return err; | |
652 | } | |
653 | } | |
654 | ||
655 | /* Set up encryption */ | |
656 | if (priv->has_wep || priv->has_wpa) { | |
657 | err = __orinoco_hw_setup_enc(priv); | |
658 | if (err) { | |
659 | printk(KERN_ERR "%s: Error %d activating encryption\n", | |
660 | dev->name, err); | |
661 | return err; | |
662 | } | |
663 | } | |
664 | ||
5217c571 | 665 | if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { |
721aa2f7 DK |
666 | /* Enable monitor mode */ |
667 | dev->type = ARPHRD_IEEE80211; | |
b42f2074 | 668 | err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | |
721aa2f7 DK |
669 | HERMES_TEST_MONITOR, 0, NULL); |
670 | } else { | |
671 | /* Disable monitor mode */ | |
672 | dev->type = ARPHRD_ETHER; | |
b42f2074 | 673 | err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | |
721aa2f7 DK |
674 | HERMES_TEST_STOP, 0, NULL); |
675 | } | |
676 | if (err) | |
677 | return err; | |
678 | ||
679 | /* Reset promiscuity / multicast*/ | |
680 | priv->promiscuous = 0; | |
681 | priv->mc_count = 0; | |
682 | ||
5217c571 DK |
683 | /* Record mode change */ |
684 | wdev->iftype = priv->iw_mode; | |
685 | ||
721aa2f7 DK |
686 | return 0; |
687 | } | |
688 | ||
712a4342 DK |
689 | /* Get tsc from the firmware */ |
690 | int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc) | |
691 | { | |
692 | hermes_t *hw = &priv->hw; | |
693 | int err = 0; | |
5c9f41e2 | 694 | u8 tsc_arr[4][ORINOCO_SEQ_LEN]; |
712a4342 | 695 | |
518ff04f | 696 | if ((key < 0) || (key >= 4)) |
712a4342 DK |
697 | return -EINVAL; |
698 | ||
b42f2074 DK |
699 | err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV, |
700 | sizeof(tsc_arr), NULL, &tsc_arr); | |
712a4342 DK |
701 | if (!err) |
702 | memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0])); | |
703 | ||
704 | return err; | |
705 | } | |
706 | ||
707 | int __orinoco_hw_set_bitrate(struct orinoco_private *priv) | |
708 | { | |
709 | hermes_t *hw = &priv->hw; | |
710 | int ratemode = priv->bitratemode; | |
711 | int err = 0; | |
712 | ||
713 | if (ratemode >= BITRATE_TABLE_SIZE) { | |
714 | printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n", | |
715 | priv->ndev->name, ratemode); | |
716 | return -EINVAL; | |
717 | } | |
718 | ||
719 | switch (priv->firmware_type) { | |
720 | case FIRMWARE_TYPE_AGERE: | |
721 | err = hermes_write_wordrec(hw, USER_BAP, | |
722 | HERMES_RID_CNFTXRATECONTROL, | |
723 | bitrate_table[ratemode].agere_txratectrl); | |
724 | break; | |
725 | case FIRMWARE_TYPE_INTERSIL: | |
726 | case FIRMWARE_TYPE_SYMBOL: | |
727 | err = hermes_write_wordrec(hw, USER_BAP, | |
728 | HERMES_RID_CNFTXRATECONTROL, | |
729 | bitrate_table[ratemode].intersil_txratectrl); | |
730 | break; | |
731 | default: | |
732 | BUG(); | |
733 | } | |
734 | ||
735 | return err; | |
736 | } | |
737 | ||
738 | int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate) | |
739 | { | |
740 | hermes_t *hw = &priv->hw; | |
741 | int i; | |
742 | int err = 0; | |
743 | u16 val; | |
744 | ||
745 | err = hermes_read_wordrec(hw, USER_BAP, | |
746 | HERMES_RID_CURRENTTXRATE, &val); | |
747 | if (err) | |
748 | return err; | |
749 | ||
750 | switch (priv->firmware_type) { | |
751 | case FIRMWARE_TYPE_AGERE: /* Lucent style rate */ | |
752 | /* Note : in Lucent firmware, the return value of | |
753 | * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s, | |
754 | * and therefore is totally different from the | |
755 | * encoding of HERMES_RID_CNFTXRATECONTROL. | |
756 | * Don't forget that 6Mb/s is really 5.5Mb/s */ | |
757 | if (val == 6) | |
758 | *bitrate = 5500000; | |
759 | else | |
760 | *bitrate = val * 1000000; | |
761 | break; | |
762 | case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */ | |
763 | case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */ | |
764 | for (i = 0; i < BITRATE_TABLE_SIZE; i++) | |
9736ebfe DK |
765 | if (bitrate_table[i].intersil_txratectrl == val) { |
766 | *bitrate = bitrate_table[i].bitrate * 100000; | |
712a4342 | 767 | break; |
9736ebfe | 768 | } |
712a4342 | 769 | |
9736ebfe | 770 | if (i >= BITRATE_TABLE_SIZE) { |
712a4342 DK |
771 | printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n", |
772 | priv->ndev->name, val); | |
9736ebfe DK |
773 | err = -EIO; |
774 | } | |
712a4342 | 775 | |
712a4342 DK |
776 | break; |
777 | default: | |
778 | BUG(); | |
779 | } | |
780 | ||
781 | return err; | |
782 | } | |
783 | ||
784 | /* Set fixed AP address */ | |
785 | int __orinoco_hw_set_wap(struct orinoco_private *priv) | |
786 | { | |
787 | int roaming_flag; | |
788 | int err = 0; | |
789 | hermes_t *hw = &priv->hw; | |
790 | ||
791 | switch (priv->firmware_type) { | |
792 | case FIRMWARE_TYPE_AGERE: | |
793 | /* not supported */ | |
794 | break; | |
795 | case FIRMWARE_TYPE_INTERSIL: | |
796 | if (priv->bssid_fixed) | |
797 | roaming_flag = 2; | |
798 | else | |
799 | roaming_flag = 1; | |
800 | ||
801 | err = hermes_write_wordrec(hw, USER_BAP, | |
802 | HERMES_RID_CNFROAMINGMODE, | |
803 | roaming_flag); | |
804 | break; | |
805 | case FIRMWARE_TYPE_SYMBOL: | |
806 | err = HERMES_WRITE_RECORD(hw, USER_BAP, | |
807 | HERMES_RID_CNFMANDATORYBSSID_SYMBOL, | |
808 | &priv->desired_bssid); | |
809 | break; | |
810 | } | |
811 | return err; | |
812 | } | |
813 | ||
814 | /* Change the WEP keys and/or the current keys. Can be called | |
815 | * either from __orinoco_hw_setup_enc() or directly from | |
816 | * orinoco_ioctl_setiwencode(). In the later case the association | |
817 | * with the AP is not broken (if the firmware can handle it), | |
818 | * which is needed for 802.1x implementations. */ | |
819 | int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv) | |
820 | { | |
821 | hermes_t *hw = &priv->hw; | |
822 | int err = 0; | |
4af198fb | 823 | int i; |
712a4342 DK |
824 | |
825 | switch (priv->firmware_type) { | |
826 | case FIRMWARE_TYPE_AGERE: | |
4af198fb DK |
827 | { |
828 | struct orinoco_key keys[ORINOCO_MAX_KEYS]; | |
829 | ||
830 | memset(&keys, 0, sizeof(keys)); | |
831 | for (i = 0; i < ORINOCO_MAX_KEYS; i++) { | |
832 | int len = min(priv->keys[i].key_len, | |
833 | ORINOCO_MAX_KEY_SIZE); | |
834 | memcpy(&keys[i].data, priv->keys[i].key, len); | |
835 | if (len > SMALL_KEY_SIZE) | |
836 | keys[i].len = cpu_to_le16(LARGE_KEY_SIZE); | |
837 | else if (len > 0) | |
838 | keys[i].len = cpu_to_le16(SMALL_KEY_SIZE); | |
839 | else | |
840 | keys[i].len = cpu_to_le16(0); | |
841 | } | |
842 | ||
712a4342 DK |
843 | err = HERMES_WRITE_RECORD(hw, USER_BAP, |
844 | HERMES_RID_CNFWEPKEYS_AGERE, | |
4af198fb | 845 | &keys); |
712a4342 DK |
846 | if (err) |
847 | return err; | |
848 | err = hermes_write_wordrec(hw, USER_BAP, | |
849 | HERMES_RID_CNFTXKEY_AGERE, | |
850 | priv->tx_key); | |
851 | if (err) | |
852 | return err; | |
853 | break; | |
4af198fb | 854 | } |
712a4342 DK |
855 | case FIRMWARE_TYPE_INTERSIL: |
856 | case FIRMWARE_TYPE_SYMBOL: | |
857 | { | |
858 | int keylen; | |
712a4342 DK |
859 | |
860 | /* Force uniform key length to work around | |
861 | * firmware bugs */ | |
4af198fb | 862 | keylen = priv->keys[priv->tx_key].key_len; |
712a4342 DK |
863 | |
864 | if (keylen > LARGE_KEY_SIZE) { | |
865 | printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n", | |
866 | priv->ndev->name, priv->tx_key, keylen); | |
867 | return -E2BIG; | |
4af198fb DK |
868 | } else if (keylen > SMALL_KEY_SIZE) |
869 | keylen = LARGE_KEY_SIZE; | |
870 | else if (keylen > 0) | |
871 | keylen = SMALL_KEY_SIZE; | |
872 | else | |
873 | keylen = 0; | |
712a4342 DK |
874 | |
875 | /* Write all 4 keys */ | |
876 | for (i = 0; i < ORINOCO_MAX_KEYS; i++) { | |
4af198fb DK |
877 | u8 key[LARGE_KEY_SIZE] = { 0 }; |
878 | ||
879 | memcpy(key, priv->keys[i].key, | |
880 | priv->keys[i].key_len); | |
881 | ||
b42f2074 | 882 | err = hw->ops->write_ltv(hw, USER_BAP, |
712a4342 DK |
883 | HERMES_RID_CNFDEFAULTKEY0 + i, |
884 | HERMES_BYTES_TO_RECLEN(keylen), | |
4af198fb | 885 | key); |
712a4342 DK |
886 | if (err) |
887 | return err; | |
888 | } | |
889 | ||
890 | /* Write the index of the key used in transmission */ | |
891 | err = hermes_write_wordrec(hw, USER_BAP, | |
892 | HERMES_RID_CNFWEPDEFAULTKEYID, | |
893 | priv->tx_key); | |
894 | if (err) | |
895 | return err; | |
896 | } | |
897 | break; | |
898 | } | |
899 | ||
900 | return 0; | |
901 | } | |
902 | ||
903 | int __orinoco_hw_setup_enc(struct orinoco_private *priv) | |
904 | { | |
905 | hermes_t *hw = &priv->hw; | |
906 | int err = 0; | |
907 | int master_wep_flag; | |
908 | int auth_flag; | |
909 | int enc_flag; | |
910 | ||
4af198fb DK |
911 | /* Setup WEP keys */ |
912 | if (priv->encode_alg == ORINOCO_ALG_WEP) | |
712a4342 DK |
913 | __orinoco_hw_setup_wepkeys(priv); |
914 | ||
915 | if (priv->wep_restrict) | |
916 | auth_flag = HERMES_AUTH_SHARED_KEY; | |
917 | else | |
918 | auth_flag = HERMES_AUTH_OPEN; | |
919 | ||
920 | if (priv->wpa_enabled) | |
921 | enc_flag = 2; | |
5c9f41e2 | 922 | else if (priv->encode_alg == ORINOCO_ALG_WEP) |
712a4342 DK |
923 | enc_flag = 1; |
924 | else | |
925 | enc_flag = 0; | |
926 | ||
927 | switch (priv->firmware_type) { | |
928 | case FIRMWARE_TYPE_AGERE: /* Agere style WEP */ | |
5c9f41e2 | 929 | if (priv->encode_alg == ORINOCO_ALG_WEP) { |
712a4342 DK |
930 | /* Enable the shared-key authentication. */ |
931 | err = hermes_write_wordrec(hw, USER_BAP, | |
932 | HERMES_RID_CNFAUTHENTICATION_AGERE, | |
933 | auth_flag); | |
934 | } | |
935 | err = hermes_write_wordrec(hw, USER_BAP, | |
936 | HERMES_RID_CNFWEPENABLED_AGERE, | |
937 | enc_flag); | |
938 | if (err) | |
939 | return err; | |
940 | ||
941 | if (priv->has_wpa) { | |
942 | /* Set WPA key management */ | |
943 | err = hermes_write_wordrec(hw, USER_BAP, | |
944 | HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE, | |
945 | priv->key_mgmt); | |
946 | if (err) | |
947 | return err; | |
948 | } | |
949 | ||
950 | break; | |
951 | ||
952 | case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */ | |
953 | case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */ | |
5c9f41e2 | 954 | if (priv->encode_alg == ORINOCO_ALG_WEP) { |
712a4342 DK |
955 | if (priv->wep_restrict || |
956 | (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)) | |
957 | master_wep_flag = HERMES_WEP_PRIVACY_INVOKED | | |
958 | HERMES_WEP_EXCL_UNENCRYPTED; | |
959 | else | |
960 | master_wep_flag = HERMES_WEP_PRIVACY_INVOKED; | |
961 | ||
962 | err = hermes_write_wordrec(hw, USER_BAP, | |
963 | HERMES_RID_CNFAUTHENTICATION, | |
964 | auth_flag); | |
965 | if (err) | |
966 | return err; | |
967 | } else | |
968 | master_wep_flag = 0; | |
969 | ||
5217c571 | 970 | if (priv->iw_mode == NL80211_IFTYPE_MONITOR) |
712a4342 DK |
971 | master_wep_flag |= HERMES_WEP_HOST_DECRYPT; |
972 | ||
973 | /* Master WEP setting : on/off */ | |
974 | err = hermes_write_wordrec(hw, USER_BAP, | |
975 | HERMES_RID_CNFWEPFLAGS_INTERSIL, | |
976 | master_wep_flag); | |
977 | if (err) | |
978 | return err; | |
979 | ||
980 | break; | |
981 | } | |
982 | ||
983 | return 0; | |
984 | } | |
985 | ||
986 | /* key must be 32 bytes, including the tx and rx MIC keys. | |
16e15848 DK |
987 | * rsc must be NULL or up to 8 bytes |
988 | * tsc must be NULL or up to 8 bytes | |
712a4342 | 989 | */ |
98e5f404 | 990 | int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx, |
16e15848 DK |
991 | int set_tx, u8 *key, u8 *rsc, size_t rsc_len, |
992 | u8 *tsc, size_t tsc_len) | |
712a4342 DK |
993 | { |
994 | struct { | |
995 | __le16 idx; | |
5c9f41e2 | 996 | u8 rsc[ORINOCO_SEQ_LEN]; |
712a4342 DK |
997 | u8 key[TKIP_KEYLEN]; |
998 | u8 tx_mic[MIC_KEYLEN]; | |
999 | u8 rx_mic[MIC_KEYLEN]; | |
5c9f41e2 | 1000 | u8 tsc[ORINOCO_SEQ_LEN]; |
ba2d3587 | 1001 | } __packed buf; |
98e5f404 | 1002 | hermes_t *hw = &priv->hw; |
712a4342 DK |
1003 | int ret; |
1004 | int err; | |
1005 | int k; | |
1006 | u16 xmitting; | |
1007 | ||
1008 | key_idx &= 0x3; | |
1009 | ||
1010 | if (set_tx) | |
1011 | key_idx |= 0x8000; | |
1012 | ||
1013 | buf.idx = cpu_to_le16(key_idx); | |
1014 | memcpy(buf.key, key, | |
1015 | sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic)); | |
1016 | ||
16e15848 DK |
1017 | if (rsc_len > sizeof(buf.rsc)) |
1018 | rsc_len = sizeof(buf.rsc); | |
1019 | ||
1020 | if (tsc_len > sizeof(buf.tsc)) | |
1021 | tsc_len = sizeof(buf.tsc); | |
1022 | ||
1023 | memset(buf.rsc, 0, sizeof(buf.rsc)); | |
1024 | memset(buf.tsc, 0, sizeof(buf.tsc)); | |
1025 | ||
1026 | if (rsc != NULL) | |
1027 | memcpy(buf.rsc, rsc, rsc_len); | |
712a4342 | 1028 | |
16e15848 DK |
1029 | if (tsc != NULL) |
1030 | memcpy(buf.tsc, tsc, tsc_len); | |
1031 | else | |
712a4342 | 1032 | buf.tsc[4] = 0x10; |
712a4342 DK |
1033 | |
1034 | /* Wait upto 100ms for tx queue to empty */ | |
91fe9ca7 | 1035 | for (k = 100; k > 0; k--) { |
712a4342 DK |
1036 | udelay(1000); |
1037 | ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY, | |
1038 | &xmitting); | |
91fe9ca7 | 1039 | if (ret || !xmitting) |
712a4342 | 1040 | break; |
91fe9ca7 | 1041 | } |
712a4342 DK |
1042 | |
1043 | if (k == 0) | |
1044 | ret = -ETIMEDOUT; | |
1045 | ||
1046 | err = HERMES_WRITE_RECORD(hw, USER_BAP, | |
1047 | HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE, | |
1048 | &buf); | |
1049 | ||
1050 | return ret ? ret : err; | |
1051 | } | |
1052 | ||
1053 | int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx) | |
1054 | { | |
1055 | hermes_t *hw = &priv->hw; | |
1056 | int err; | |
1057 | ||
712a4342 DK |
1058 | err = hermes_write_wordrec(hw, USER_BAP, |
1059 | HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE, | |
1060 | key_idx); | |
1061 | if (err) | |
1062 | printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n", | |
1063 | priv->ndev->name, err, key_idx); | |
1064 | return err; | |
1065 | } | |
1066 | ||
1067 | int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, | |
655ffee2 | 1068 | struct net_device *dev, |
712a4342 DK |
1069 | int mc_count, int promisc) |
1070 | { | |
1071 | hermes_t *hw = &priv->hw; | |
1072 | int err = 0; | |
1073 | ||
1074 | if (promisc != priv->promiscuous) { | |
1075 | err = hermes_write_wordrec(hw, USER_BAP, | |
1076 | HERMES_RID_CNFPROMISCUOUSMODE, | |
1077 | promisc); | |
1078 | if (err) { | |
1079 | printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n", | |
1080 | priv->ndev->name, err); | |
1081 | } else | |
1082 | priv->promiscuous = promisc; | |
1083 | } | |
1084 | ||
1085 | /* If we're not in promiscuous mode, then we need to set the | |
1086 | * group address if either we want to multicast, or if we were | |
1087 | * multicasting and want to stop */ | |
1088 | if (!promisc && (mc_count || priv->mc_count)) { | |
22bedad3 | 1089 | struct netdev_hw_addr *ha; |
712a4342 | 1090 | struct hermes_multicast mclist; |
655ffee2 | 1091 | int i = 0; |
712a4342 | 1092 | |
22bedad3 | 1093 | netdev_for_each_mc_addr(ha, dev) { |
655ffee2 JP |
1094 | if (i == mc_count) |
1095 | break; | |
22bedad3 | 1096 | memcpy(mclist.addr[i++], ha->addr, ETH_ALEN); |
712a4342 DK |
1097 | } |
1098 | ||
b42f2074 | 1099 | err = hw->ops->write_ltv(hw, USER_BAP, |
712a4342 DK |
1100 | HERMES_RID_CNFGROUPADDRESSES, |
1101 | HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN), | |
1102 | &mclist); | |
1103 | if (err) | |
1104 | printk(KERN_ERR "%s: Error %d setting multicast list.\n", | |
1105 | priv->ndev->name, err); | |
1106 | else | |
1107 | priv->mc_count = mc_count; | |
1108 | } | |
1109 | return err; | |
1110 | } | |
1111 | ||
1112 | /* Return : < 0 -> error code ; >= 0 -> length */ | |
1113 | int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, | |
1114 | char buf[IW_ESSID_MAX_SIZE+1]) | |
1115 | { | |
1116 | hermes_t *hw = &priv->hw; | |
1117 | int err = 0; | |
1118 | struct hermes_idstring essidbuf; | |
1119 | char *p = (char *)(&essidbuf.val); | |
1120 | int len; | |
1121 | unsigned long flags; | |
1122 | ||
1123 | if (orinoco_lock(priv, &flags) != 0) | |
1124 | return -EBUSY; | |
1125 | ||
1126 | if (strlen(priv->desired_essid) > 0) { | |
1127 | /* We read the desired SSID from the hardware rather | |
1128 | than from priv->desired_essid, just in case the | |
1129 | firmware is allowed to change it on us. I'm not | |
1130 | sure about this */ | |
1131 | /* My guess is that the OWNSSID should always be whatever | |
1132 | * we set to the card, whereas CURRENT_SSID is the one that | |
1133 | * may change... - Jean II */ | |
1134 | u16 rid; | |
1135 | ||
1136 | *active = 1; | |
1137 | ||
1138 | rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID : | |
1139 | HERMES_RID_CNFDESIREDSSID; | |
1140 | ||
b42f2074 DK |
1141 | err = hw->ops->read_ltv(hw, USER_BAP, rid, sizeof(essidbuf), |
1142 | NULL, &essidbuf); | |
712a4342 DK |
1143 | if (err) |
1144 | goto fail_unlock; | |
1145 | } else { | |
1146 | *active = 0; | |
1147 | ||
b42f2074 DK |
1148 | err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID, |
1149 | sizeof(essidbuf), NULL, &essidbuf); | |
712a4342 DK |
1150 | if (err) |
1151 | goto fail_unlock; | |
1152 | } | |
1153 | ||
1154 | len = le16_to_cpu(essidbuf.len); | |
1155 | BUG_ON(len > IW_ESSID_MAX_SIZE); | |
1156 | ||
1157 | memset(buf, 0, IW_ESSID_MAX_SIZE); | |
1158 | memcpy(buf, p, len); | |
1159 | err = len; | |
1160 | ||
1161 | fail_unlock: | |
1162 | orinoco_unlock(priv, &flags); | |
1163 | ||
1164 | return err; | |
1165 | } | |
1166 | ||
1167 | int orinoco_hw_get_freq(struct orinoco_private *priv) | |
1168 | { | |
1169 | hermes_t *hw = &priv->hw; | |
1170 | int err = 0; | |
1171 | u16 channel; | |
1172 | int freq = 0; | |
1173 | unsigned long flags; | |
1174 | ||
1175 | if (orinoco_lock(priv, &flags) != 0) | |
1176 | return -EBUSY; | |
1177 | ||
1178 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, | |
1179 | &channel); | |
1180 | if (err) | |
1181 | goto out; | |
1182 | ||
1183 | /* Intersil firmware 1.3.5 returns 0 when the interface is down */ | |
1184 | if (channel == 0) { | |
1185 | err = -EBUSY; | |
1186 | goto out; | |
1187 | } | |
1188 | ||
1189 | if ((channel < 1) || (channel > NUM_CHANNELS)) { | |
1190 | printk(KERN_WARNING "%s: Channel out of range (%d)!\n", | |
1191 | priv->ndev->name, channel); | |
1192 | err = -EBUSY; | |
1193 | goto out; | |
1194 | ||
1195 | } | |
1196 | freq = ieee80211_dsss_chan_to_freq(channel); | |
1197 | ||
1198 | out: | |
1199 | orinoco_unlock(priv, &flags); | |
1200 | ||
1201 | if (err > 0) | |
1202 | err = -EBUSY; | |
1203 | return err ? err : freq; | |
1204 | } | |
1205 | ||
1206 | int orinoco_hw_get_bitratelist(struct orinoco_private *priv, | |
1207 | int *numrates, s32 *rates, int max) | |
1208 | { | |
1209 | hermes_t *hw = &priv->hw; | |
1210 | struct hermes_idstring list; | |
1211 | unsigned char *p = (unsigned char *)&list.val; | |
1212 | int err = 0; | |
1213 | int num; | |
1214 | int i; | |
1215 | unsigned long flags; | |
1216 | ||
1217 | if (orinoco_lock(priv, &flags) != 0) | |
1218 | return -EBUSY; | |
1219 | ||
b42f2074 DK |
1220 | err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES, |
1221 | sizeof(list), NULL, &list); | |
712a4342 DK |
1222 | orinoco_unlock(priv, &flags); |
1223 | ||
1224 | if (err) | |
1225 | return err; | |
1226 | ||
1227 | num = le16_to_cpu(list.len); | |
1228 | *numrates = num; | |
1229 | num = min(num, max); | |
1230 | ||
1231 | for (i = 0; i < num; i++) | |
1232 | rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */ | |
1233 | ||
1234 | return 0; | |
1235 | } | |
c63cdbe8 DK |
1236 | |
1237 | int orinoco_hw_trigger_scan(struct orinoco_private *priv, | |
1238 | const struct cfg80211_ssid *ssid) | |
1239 | { | |
1240 | struct net_device *dev = priv->ndev; | |
1241 | hermes_t *hw = &priv->hw; | |
1242 | unsigned long flags; | |
1243 | int err = 0; | |
1244 | ||
1245 | if (orinoco_lock(priv, &flags) != 0) | |
1246 | return -EBUSY; | |
1247 | ||
1248 | /* Scanning with port 0 disabled would fail */ | |
1249 | if (!netif_running(dev)) { | |
1250 | err = -ENETDOWN; | |
1251 | goto out; | |
1252 | } | |
1253 | ||
1254 | /* In monitor mode, the scan results are always empty. | |
1255 | * Probe responses are passed to the driver as received | |
1256 | * frames and could be processed in software. */ | |
1257 | if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { | |
1258 | err = -EOPNOTSUPP; | |
1259 | goto out; | |
1260 | } | |
1261 | ||
1262 | if (priv->has_hostscan) { | |
1263 | switch (priv->firmware_type) { | |
1264 | case FIRMWARE_TYPE_SYMBOL: | |
1265 | err = hermes_write_wordrec(hw, USER_BAP, | |
1266 | HERMES_RID_CNFHOSTSCAN_SYMBOL, | |
1267 | HERMES_HOSTSCAN_SYMBOL_ONCE | | |
1268 | HERMES_HOSTSCAN_SYMBOL_BCAST); | |
1269 | break; | |
1270 | case FIRMWARE_TYPE_INTERSIL: { | |
1271 | __le16 req[3]; | |
1272 | ||
1273 | req[0] = cpu_to_le16(0x3fff); /* All channels */ | |
1274 | req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */ | |
1275 | req[2] = 0; /* Any ESSID */ | |
1276 | err = HERMES_WRITE_RECORD(hw, USER_BAP, | |
1277 | HERMES_RID_CNFHOSTSCAN, &req); | |
1278 | break; | |
1279 | } | |
1280 | case FIRMWARE_TYPE_AGERE: | |
1281 | if (ssid->ssid_len > 0) { | |
1282 | struct hermes_idstring idbuf; | |
1283 | size_t len = ssid->ssid_len; | |
1284 | ||
1285 | idbuf.len = cpu_to_le16(len); | |
1286 | memcpy(idbuf.val, ssid->ssid, len); | |
1287 | ||
b42f2074 | 1288 | err = hw->ops->write_ltv(hw, USER_BAP, |
c63cdbe8 DK |
1289 | HERMES_RID_CNFSCANSSID_AGERE, |
1290 | HERMES_BYTES_TO_RECLEN(len + 2), | |
1291 | &idbuf); | |
1292 | } else | |
1293 | err = hermes_write_wordrec(hw, USER_BAP, | |
1294 | HERMES_RID_CNFSCANSSID_AGERE, | |
1295 | 0); /* Any ESSID */ | |
1296 | if (err) | |
1297 | break; | |
1298 | ||
1299 | if (priv->has_ext_scan) { | |
1300 | err = hermes_write_wordrec(hw, USER_BAP, | |
1301 | HERMES_RID_CNFSCANCHANNELS2GHZ, | |
1302 | 0x7FFF); | |
1303 | if (err) | |
1304 | goto out; | |
1305 | ||
1306 | err = hermes_inquire(hw, | |
1307 | HERMES_INQ_CHANNELINFO); | |
1308 | } else | |
1309 | err = hermes_inquire(hw, HERMES_INQ_SCAN); | |
1310 | ||
1311 | break; | |
1312 | } | |
1313 | } else | |
1314 | err = hermes_inquire(hw, HERMES_INQ_SCAN); | |
1315 | ||
1316 | out: | |
1317 | orinoco_unlock(priv, &flags); | |
1318 | ||
1319 | return err; | |
1320 | } | |
07542d08 DK |
1321 | |
1322 | /* Disassociate from node with BSSID addr */ | |
1323 | int orinoco_hw_disassociate(struct orinoco_private *priv, | |
1324 | u8 *addr, u16 reason_code) | |
1325 | { | |
1326 | hermes_t *hw = &priv->hw; | |
1327 | int err; | |
1328 | ||
1329 | struct { | |
1330 | u8 addr[ETH_ALEN]; | |
1331 | __le16 reason_code; | |
ba2d3587 | 1332 | } __packed buf; |
07542d08 DK |
1333 | |
1334 | /* Currently only supported by WPA enabled Agere fw */ | |
1335 | if (!priv->has_wpa) | |
1336 | return -EOPNOTSUPP; | |
1337 | ||
1338 | memcpy(buf.addr, addr, ETH_ALEN); | |
1339 | buf.reason_code = cpu_to_le16(reason_code); | |
1340 | err = HERMES_WRITE_RECORD(hw, USER_BAP, | |
1341 | HERMES_RID_CNFDISASSOCIATE, | |
1342 | &buf); | |
1343 | return err; | |
1344 | } | |
2b260351 DK |
1345 | |
1346 | int orinoco_hw_get_current_bssid(struct orinoco_private *priv, | |
1347 | u8 *addr) | |
1348 | { | |
1349 | hermes_t *hw = &priv->hw; | |
1350 | int err; | |
1351 | ||
b42f2074 DK |
1352 | err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, |
1353 | ETH_ALEN, NULL, addr); | |
2b260351 DK |
1354 | |
1355 | return err; | |
1356 | } |