diff options
author | Jouni Malinen <jkmaline@cc.hut.fi> | 2005-05-12 22:54:16 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-05-12 22:54:16 -0400 |
commit | ff1d2767d5a43c85f944e86a45284b721f66196c (patch) | |
tree | 91c1b6dd20bd772bc112c0012830678b46b69604 /drivers/net/wireless/hostap/hostap_download.c | |
parent | 88d7bd8cb9eb8d64bf7997600b0d64f7834047c5 (diff) |
Add HostAP wireless driver.
Includes minor cleanups from Adrian Bunk <bunk@stusta.de>.
Diffstat (limited to 'drivers/net/wireless/hostap/hostap_download.c')
-rw-r--r-- | drivers/net/wireless/hostap/hostap_download.c | 766 |
1 files changed, 766 insertions, 0 deletions
diff --git a/drivers/net/wireless/hostap/hostap_download.c b/drivers/net/wireless/hostap/hostap_download.c new file mode 100644 index 000000000000..ab26b52b3e76 --- /dev/null +++ b/drivers/net/wireless/hostap/hostap_download.c | |||
@@ -0,0 +1,766 @@ | |||
1 | static int prism2_enable_aux_port(struct net_device *dev, int enable) | ||
2 | { | ||
3 | u16 val, reg; | ||
4 | int i, tries; | ||
5 | unsigned long flags; | ||
6 | struct hostap_interface *iface; | ||
7 | local_info_t *local; | ||
8 | |||
9 | iface = netdev_priv(dev); | ||
10 | local = iface->local; | ||
11 | |||
12 | if (local->no_pri) { | ||
13 | if (enable) { | ||
14 | PDEBUG(DEBUG_EXTRA2, "%s: no PRI f/w - assuming Aux " | ||
15 | "port is already enabled\n", dev->name); | ||
16 | } | ||
17 | return 0; | ||
18 | } | ||
19 | |||
20 | spin_lock_irqsave(&local->cmdlock, flags); | ||
21 | |||
22 | /* wait until busy bit is clear */ | ||
23 | tries = HFA384X_CMD_BUSY_TIMEOUT; | ||
24 | while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { | ||
25 | tries--; | ||
26 | udelay(1); | ||
27 | } | ||
28 | if (tries == 0) { | ||
29 | reg = HFA384X_INW(HFA384X_CMD_OFF); | ||
30 | spin_unlock_irqrestore(&local->cmdlock, flags); | ||
31 | printk("%s: prism2_enable_aux_port - timeout - reg=0x%04x\n", | ||
32 | dev->name, reg); | ||
33 | return -ETIMEDOUT; | ||
34 | } | ||
35 | |||
36 | val = HFA384X_INW(HFA384X_CONTROL_OFF); | ||
37 | |||
38 | if (enable) { | ||
39 | HFA384X_OUTW(HFA384X_AUX_MAGIC0, HFA384X_PARAM0_OFF); | ||
40 | HFA384X_OUTW(HFA384X_AUX_MAGIC1, HFA384X_PARAM1_OFF); | ||
41 | HFA384X_OUTW(HFA384X_AUX_MAGIC2, HFA384X_PARAM2_OFF); | ||
42 | |||
43 | if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_DISABLED) | ||
44 | printk("prism2_enable_aux_port: was not disabled!?\n"); | ||
45 | val &= ~HFA384X_AUX_PORT_MASK; | ||
46 | val |= HFA384X_AUX_PORT_ENABLE; | ||
47 | } else { | ||
48 | HFA384X_OUTW(0, HFA384X_PARAM0_OFF); | ||
49 | HFA384X_OUTW(0, HFA384X_PARAM1_OFF); | ||
50 | HFA384X_OUTW(0, HFA384X_PARAM2_OFF); | ||
51 | |||
52 | if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_ENABLED) | ||
53 | printk("prism2_enable_aux_port: was not enabled!?\n"); | ||
54 | val &= ~HFA384X_AUX_PORT_MASK; | ||
55 | val |= HFA384X_AUX_PORT_DISABLE; | ||
56 | } | ||
57 | HFA384X_OUTW(val, HFA384X_CONTROL_OFF); | ||
58 | |||
59 | udelay(5); | ||
60 | |||
61 | i = 10000; | ||
62 | while (i > 0) { | ||
63 | val = HFA384X_INW(HFA384X_CONTROL_OFF); | ||
64 | val &= HFA384X_AUX_PORT_MASK; | ||
65 | |||
66 | if ((enable && val == HFA384X_AUX_PORT_ENABLED) || | ||
67 | (!enable && val == HFA384X_AUX_PORT_DISABLED)) | ||
68 | break; | ||
69 | |||
70 | udelay(10); | ||
71 | i--; | ||
72 | } | ||
73 | |||
74 | spin_unlock_irqrestore(&local->cmdlock, flags); | ||
75 | |||
76 | if (i == 0) { | ||
77 | printk("prism2_enable_aux_port(%d) timed out\n", | ||
78 | enable); | ||
79 | return -ETIMEDOUT; | ||
80 | } | ||
81 | |||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | |||
86 | static int hfa384x_from_aux(struct net_device *dev, unsigned int addr, int len, | ||
87 | void *buf) | ||
88 | { | ||
89 | u16 page, offset; | ||
90 | if (addr & 1 || len & 1) | ||
91 | return -1; | ||
92 | |||
93 | page = addr >> 7; | ||
94 | offset = addr & 0x7f; | ||
95 | |||
96 | HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); | ||
97 | HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); | ||
98 | |||
99 | udelay(5); | ||
100 | |||
101 | #ifdef PRISM2_PCI | ||
102 | { | ||
103 | u16 *pos = (u16 *) buf; | ||
104 | while (len > 0) { | ||
105 | *pos++ = HFA384X_INW_DATA(HFA384X_AUXDATA_OFF); | ||
106 | len -= 2; | ||
107 | } | ||
108 | } | ||
109 | #else /* PRISM2_PCI */ | ||
110 | HFA384X_INSW(HFA384X_AUXDATA_OFF, buf, len / 2); | ||
111 | #endif /* PRISM2_PCI */ | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | |||
117 | static int hfa384x_to_aux(struct net_device *dev, unsigned int addr, int len, | ||
118 | void *buf) | ||
119 | { | ||
120 | u16 page, offset; | ||
121 | if (addr & 1 || len & 1) | ||
122 | return -1; | ||
123 | |||
124 | page = addr >> 7; | ||
125 | offset = addr & 0x7f; | ||
126 | |||
127 | HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); | ||
128 | HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); | ||
129 | |||
130 | udelay(5); | ||
131 | |||
132 | #ifdef PRISM2_PCI | ||
133 | { | ||
134 | u16 *pos = (u16 *) buf; | ||
135 | while (len > 0) { | ||
136 | HFA384X_OUTW_DATA(*pos++, HFA384X_AUXDATA_OFF); | ||
137 | len -= 2; | ||
138 | } | ||
139 | } | ||
140 | #else /* PRISM2_PCI */ | ||
141 | HFA384X_OUTSW(HFA384X_AUXDATA_OFF, buf, len / 2); | ||
142 | #endif /* PRISM2_PCI */ | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | |||
148 | static int prism2_pda_ok(u8 *buf) | ||
149 | { | ||
150 | u16 *pda = (u16 *) buf; | ||
151 | int pos; | ||
152 | u16 len, pdr; | ||
153 | |||
154 | if (buf[0] == 0xff && buf[1] == 0x00 && buf[2] == 0xff && | ||
155 | buf[3] == 0x00) | ||
156 | return 0; | ||
157 | |||
158 | pos = 0; | ||
159 | while (pos + 1 < PRISM2_PDA_SIZE / 2) { | ||
160 | len = le16_to_cpu(pda[pos]); | ||
161 | pdr = le16_to_cpu(pda[pos + 1]); | ||
162 | if (len == 0 || pos + len > PRISM2_PDA_SIZE / 2) | ||
163 | return 0; | ||
164 | |||
165 | if (pdr == 0x0000 && len == 2) { | ||
166 | /* PDA end found */ | ||
167 | return 1; | ||
168 | } | ||
169 | |||
170 | pos += len + 1; | ||
171 | } | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | |||
177 | static int prism2_download_aux_dump(struct net_device *dev, | ||
178 | unsigned int addr, int len, u8 *buf) | ||
179 | { | ||
180 | int res; | ||
181 | |||
182 | prism2_enable_aux_port(dev, 1); | ||
183 | res = hfa384x_from_aux(dev, addr, len, buf); | ||
184 | prism2_enable_aux_port(dev, 0); | ||
185 | if (res) | ||
186 | return -1; | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | |||
192 | static u8 * prism2_read_pda(struct net_device *dev) | ||
193 | { | ||
194 | u8 *buf; | ||
195 | int res, i, found = 0; | ||
196 | #define NUM_PDA_ADDRS 4 | ||
197 | unsigned int pda_addr[NUM_PDA_ADDRS] = { | ||
198 | 0x7f0000 /* others than HFA3841 */, | ||
199 | 0x3f0000 /* HFA3841 */, | ||
200 | 0x390000 /* apparently used in older cards */, | ||
201 | 0x7f0002 /* Intel PRO/Wireless 2011B (PCI) */, | ||
202 | }; | ||
203 | |||
204 | buf = (u8 *) kmalloc(PRISM2_PDA_SIZE, GFP_KERNEL); | ||
205 | if (buf == NULL) | ||
206 | return NULL; | ||
207 | |||
208 | /* Note: wlan card should be in initial state (just after init cmd) | ||
209 | * and no other operations should be performed concurrently. */ | ||
210 | |||
211 | prism2_enable_aux_port(dev, 1); | ||
212 | |||
213 | for (i = 0; i < NUM_PDA_ADDRS; i++) { | ||
214 | PDEBUG(DEBUG_EXTRA2, "%s: trying to read PDA from 0x%08x", | ||
215 | dev->name, pda_addr[i]); | ||
216 | res = hfa384x_from_aux(dev, pda_addr[i], PRISM2_PDA_SIZE, buf); | ||
217 | if (res) | ||
218 | continue; | ||
219 | if (res == 0 && prism2_pda_ok(buf)) { | ||
220 | PDEBUG2(DEBUG_EXTRA2, ": OK\n"); | ||
221 | found = 1; | ||
222 | break; | ||
223 | } else { | ||
224 | PDEBUG2(DEBUG_EXTRA2, ": failed\n"); | ||
225 | } | ||
226 | } | ||
227 | |||
228 | prism2_enable_aux_port(dev, 0); | ||
229 | |||
230 | if (!found) { | ||
231 | printk(KERN_DEBUG "%s: valid PDA not found\n", dev->name); | ||
232 | kfree(buf); | ||
233 | buf = NULL; | ||
234 | } | ||
235 | |||
236 | return buf; | ||
237 | } | ||
238 | |||
239 | |||
240 | static int prism2_download_volatile(local_info_t *local, | ||
241 | struct prism2_download_data *param) | ||
242 | { | ||
243 | struct net_device *dev = local->dev; | ||
244 | int ret = 0, i; | ||
245 | u16 param0, param1; | ||
246 | |||
247 | if (local->hw_downloading) { | ||
248 | printk(KERN_WARNING "%s: Already downloading - aborting new " | ||
249 | "request\n", dev->name); | ||
250 | return -1; | ||
251 | } | ||
252 | |||
253 | local->hw_downloading = 1; | ||
254 | if (local->pri_only) { | ||
255 | hfa384x_disable_interrupts(dev); | ||
256 | } else { | ||
257 | prism2_hw_shutdown(dev, 0); | ||
258 | |||
259 | if (prism2_hw_init(dev, 0)) { | ||
260 | printk(KERN_WARNING "%s: Could not initialize card for" | ||
261 | " download\n", dev->name); | ||
262 | ret = -1; | ||
263 | goto out; | ||
264 | } | ||
265 | } | ||
266 | |||
267 | if (prism2_enable_aux_port(dev, 1)) { | ||
268 | printk(KERN_WARNING "%s: Could not enable AUX port\n", | ||
269 | dev->name); | ||
270 | ret = -1; | ||
271 | goto out; | ||
272 | } | ||
273 | |||
274 | param0 = param->start_addr & 0xffff; | ||
275 | param1 = param->start_addr >> 16; | ||
276 | |||
277 | HFA384X_OUTW(0, HFA384X_PARAM2_OFF); | ||
278 | HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); | ||
279 | if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | | ||
280 | (HFA384X_PROGMODE_ENABLE_VOLATILE << 8), | ||
281 | param0)) { | ||
282 | printk(KERN_WARNING "%s: Download command execution failed\n", | ||
283 | dev->name); | ||
284 | ret = -1; | ||
285 | goto out; | ||
286 | } | ||
287 | |||
288 | for (i = 0; i < param->num_areas; i++) { | ||
289 | PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n", | ||
290 | dev->name, param->data[i].len, param->data[i].addr); | ||
291 | if (hfa384x_to_aux(dev, param->data[i].addr, | ||
292 | param->data[i].len, param->data[i].data)) { | ||
293 | printk(KERN_WARNING "%s: RAM download at 0x%08x " | ||
294 | "(len=%d) failed\n", dev->name, | ||
295 | param->data[i].addr, param->data[i].len); | ||
296 | ret = -1; | ||
297 | goto out; | ||
298 | } | ||
299 | } | ||
300 | |||
301 | HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); | ||
302 | HFA384X_OUTW(0, HFA384X_PARAM2_OFF); | ||
303 | if (hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_DOWNLOAD | | ||
304 | (HFA384X_PROGMODE_DISABLE << 8), param0)) { | ||
305 | printk(KERN_WARNING "%s: Download command execution failed\n", | ||
306 | dev->name); | ||
307 | ret = -1; | ||
308 | goto out; | ||
309 | } | ||
310 | /* ProgMode disable causes the hardware to restart itself from the | ||
311 | * given starting address. Give hw some time and ACK command just in | ||
312 | * case restart did not happen. */ | ||
313 | mdelay(5); | ||
314 | HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); | ||
315 | |||
316 | if (prism2_enable_aux_port(dev, 0)) { | ||
317 | printk(KERN_DEBUG "%s: Disabling AUX port failed\n", | ||
318 | dev->name); | ||
319 | /* continue anyway.. restart should have taken care of this */ | ||
320 | } | ||
321 | |||
322 | mdelay(5); | ||
323 | local->hw_downloading = 0; | ||
324 | if (prism2_hw_config(dev, 2)) { | ||
325 | printk(KERN_WARNING "%s: Card configuration after RAM " | ||
326 | "download failed\n", dev->name); | ||
327 | ret = -1; | ||
328 | goto out; | ||
329 | } | ||
330 | |||
331 | out: | ||
332 | local->hw_downloading = 0; | ||
333 | return ret; | ||
334 | } | ||
335 | |||
336 | |||
337 | static int prism2_enable_genesis(local_info_t *local, int hcr) | ||
338 | { | ||
339 | struct net_device *dev = local->dev; | ||
340 | u8 initseq[4] = { 0x00, 0xe1, 0xa1, 0xff }; | ||
341 | u8 readbuf[4]; | ||
342 | |||
343 | printk(KERN_DEBUG "%s: test Genesis mode with HCR 0x%02x\n", | ||
344 | dev->name, hcr); | ||
345 | local->func->cor_sreset(local); | ||
346 | hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq); | ||
347 | local->func->genesis_reset(local, hcr); | ||
348 | |||
349 | /* Readback test */ | ||
350 | hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf); | ||
351 | hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq); | ||
352 | hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf); | ||
353 | |||
354 | if (memcmp(initseq, readbuf, sizeof(initseq)) == 0) { | ||
355 | printk(KERN_DEBUG "Readback test succeeded, HCR 0x%02x\n", | ||
356 | hcr); | ||
357 | return 0; | ||
358 | } else { | ||
359 | printk(KERN_DEBUG "Readback test failed, HCR 0x%02x " | ||
360 | "write %02x %02x %02x %02x read %02x %02x %02x %02x\n", | ||
361 | hcr, initseq[0], initseq[1], initseq[2], initseq[3], | ||
362 | readbuf[0], readbuf[1], readbuf[2], readbuf[3]); | ||
363 | return 1; | ||
364 | } | ||
365 | } | ||
366 | |||
367 | |||
368 | static int prism2_get_ram_size(local_info_t *local) | ||
369 | { | ||
370 | int ret; | ||
371 | |||
372 | /* Try to enable genesis mode; 0x1F for x8 SRAM or 0x0F for x16 SRAM */ | ||
373 | if (prism2_enable_genesis(local, 0x1f) == 0) | ||
374 | ret = 8; | ||
375 | else if (prism2_enable_genesis(local, 0x0f) == 0) | ||
376 | ret = 16; | ||
377 | else | ||
378 | ret = -1; | ||
379 | |||
380 | /* Disable genesis mode */ | ||
381 | local->func->genesis_reset(local, ret == 16 ? 0x07 : 0x17); | ||
382 | |||
383 | return ret; | ||
384 | } | ||
385 | |||
386 | |||
387 | static int prism2_download_genesis(local_info_t *local, | ||
388 | struct prism2_download_data *param) | ||
389 | { | ||
390 | struct net_device *dev = local->dev; | ||
391 | int ram16 = 0, i; | ||
392 | int ret = 0; | ||
393 | |||
394 | if (local->hw_downloading) { | ||
395 | printk(KERN_WARNING "%s: Already downloading - aborting new " | ||
396 | "request\n", dev->name); | ||
397 | return -EBUSY; | ||
398 | } | ||
399 | |||
400 | if (!local->func->genesis_reset || !local->func->cor_sreset) { | ||
401 | printk(KERN_INFO "%s: Genesis mode downloading not supported " | ||
402 | "with this hwmodel\n", dev->name); | ||
403 | return -EOPNOTSUPP; | ||
404 | } | ||
405 | |||
406 | local->hw_downloading = 1; | ||
407 | |||
408 | if (prism2_enable_aux_port(dev, 1)) { | ||
409 | printk(KERN_DEBUG "%s: failed to enable AUX port\n", | ||
410 | dev->name); | ||
411 | ret = -EIO; | ||
412 | goto out; | ||
413 | } | ||
414 | |||
415 | if (local->sram_type == -1) { | ||
416 | /* 0x1F for x8 SRAM or 0x0F for x16 SRAM */ | ||
417 | if (prism2_enable_genesis(local, 0x1f) == 0) { | ||
418 | ram16 = 0; | ||
419 | PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x8 " | ||
420 | "SRAM\n", dev->name); | ||
421 | } else if (prism2_enable_genesis(local, 0x0f) == 0) { | ||
422 | ram16 = 1; | ||
423 | PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x16 " | ||
424 | "SRAM\n", dev->name); | ||
425 | } else { | ||
426 | printk(KERN_DEBUG "%s: Could not initiate genesis " | ||
427 | "mode\n", dev->name); | ||
428 | ret = -EIO; | ||
429 | goto out; | ||
430 | } | ||
431 | } else { | ||
432 | if (prism2_enable_genesis(local, local->sram_type == 8 ? | ||
433 | 0x1f : 0x0f)) { | ||
434 | printk(KERN_DEBUG "%s: Failed to set Genesis " | ||
435 | "mode (sram_type=%d)\n", dev->name, | ||
436 | local->sram_type); | ||
437 | ret = -EIO; | ||
438 | goto out; | ||
439 | } | ||
440 | ram16 = local->sram_type != 8; | ||
441 | } | ||
442 | |||
443 | for (i = 0; i < param->num_areas; i++) { | ||
444 | PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n", | ||
445 | dev->name, param->data[i].len, param->data[i].addr); | ||
446 | if (hfa384x_to_aux(dev, param->data[i].addr, | ||
447 | param->data[i].len, param->data[i].data)) { | ||
448 | printk(KERN_WARNING "%s: RAM download at 0x%08x " | ||
449 | "(len=%d) failed\n", dev->name, | ||
450 | param->data[i].addr, param->data[i].len); | ||
451 | ret = -EIO; | ||
452 | goto out; | ||
453 | } | ||
454 | } | ||
455 | |||
456 | PDEBUG(DEBUG_EXTRA2, "Disable genesis mode\n"); | ||
457 | local->func->genesis_reset(local, ram16 ? 0x07 : 0x17); | ||
458 | if (prism2_enable_aux_port(dev, 0)) { | ||
459 | printk(KERN_DEBUG "%s: Failed to disable AUX port\n", | ||
460 | dev->name); | ||
461 | } | ||
462 | |||
463 | mdelay(5); | ||
464 | local->hw_downloading = 0; | ||
465 | |||
466 | PDEBUG(DEBUG_EXTRA2, "Trying to initialize card\n"); | ||
467 | /* | ||
468 | * Make sure the INIT command does not generate a command completion | ||
469 | * event by disabling interrupts. | ||
470 | */ | ||
471 | hfa384x_disable_interrupts(dev); | ||
472 | if (prism2_hw_init(dev, 1)) { | ||
473 | printk(KERN_DEBUG "%s: Initialization after genesis mode " | ||
474 | "download failed\n", dev->name); | ||
475 | ret = -EIO; | ||
476 | goto out; | ||
477 | } | ||
478 | |||
479 | PDEBUG(DEBUG_EXTRA2, "Card initialized - running PRI only\n"); | ||
480 | if (prism2_hw_init2(dev, 1)) { | ||
481 | printk(KERN_DEBUG "%s: Initialization(2) after genesis mode " | ||
482 | "download failed\n", dev->name); | ||
483 | ret = -EIO; | ||
484 | goto out; | ||
485 | } | ||
486 | |||
487 | out: | ||
488 | local->hw_downloading = 0; | ||
489 | return ret; | ||
490 | } | ||
491 | |||
492 | |||
493 | #ifdef PRISM2_NON_VOLATILE_DOWNLOAD | ||
494 | /* Note! Non-volatile downloading functionality has not yet been tested | ||
495 | * thoroughly and it may corrupt flash image and effectively kill the card that | ||
496 | * is being updated. You have been warned. */ | ||
497 | |||
498 | static inline int prism2_download_block(struct net_device *dev, | ||
499 | u32 addr, u8 *data, | ||
500 | u32 bufaddr, int rest_len) | ||
501 | { | ||
502 | u16 param0, param1; | ||
503 | int block_len; | ||
504 | |||
505 | block_len = rest_len < 4096 ? rest_len : 4096; | ||
506 | |||
507 | param0 = addr & 0xffff; | ||
508 | param1 = addr >> 16; | ||
509 | |||
510 | HFA384X_OUTW(block_len, HFA384X_PARAM2_OFF); | ||
511 | HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); | ||
512 | |||
513 | if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | | ||
514 | (HFA384X_PROGMODE_ENABLE_NON_VOLATILE << 8), | ||
515 | param0)) { | ||
516 | printk(KERN_WARNING "%s: Flash download command execution " | ||
517 | "failed\n", dev->name); | ||
518 | return -1; | ||
519 | } | ||
520 | |||
521 | if (hfa384x_to_aux(dev, bufaddr, block_len, data)) { | ||
522 | printk(KERN_WARNING "%s: flash download at 0x%08x " | ||
523 | "(len=%d) failed\n", dev->name, addr, block_len); | ||
524 | return -1; | ||
525 | } | ||
526 | |||
527 | HFA384X_OUTW(0, HFA384X_PARAM2_OFF); | ||
528 | HFA384X_OUTW(0, HFA384X_PARAM1_OFF); | ||
529 | if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | | ||
530 | (HFA384X_PROGMODE_PROGRAM_NON_VOLATILE << 8), | ||
531 | 0)) { | ||
532 | printk(KERN_WARNING "%s: Flash write command execution " | ||
533 | "failed\n", dev->name); | ||
534 | return -1; | ||
535 | } | ||
536 | |||
537 | return block_len; | ||
538 | } | ||
539 | |||
540 | |||
541 | static int prism2_download_nonvolatile(local_info_t *local, | ||
542 | struct prism2_download_data *dl) | ||
543 | { | ||
544 | struct net_device *dev = local->dev; | ||
545 | int ret = 0, i; | ||
546 | struct { | ||
547 | u16 page; | ||
548 | u16 offset; | ||
549 | u16 len; | ||
550 | } dlbuffer; | ||
551 | u32 bufaddr; | ||
552 | |||
553 | if (local->hw_downloading) { | ||
554 | printk(KERN_WARNING "%s: Already downloading - aborting new " | ||
555 | "request\n", dev->name); | ||
556 | return -1; | ||
557 | } | ||
558 | |||
559 | ret = local->func->get_rid(dev, HFA384X_RID_DOWNLOADBUFFER, | ||
560 | &dlbuffer, 6, 0); | ||
561 | |||
562 | if (ret < 0) { | ||
563 | printk(KERN_WARNING "%s: Could not read download buffer " | ||
564 | "parameters\n", dev->name); | ||
565 | goto out; | ||
566 | } | ||
567 | |||
568 | dlbuffer.page = le16_to_cpu(dlbuffer.page); | ||
569 | dlbuffer.offset = le16_to_cpu(dlbuffer.offset); | ||
570 | dlbuffer.len = le16_to_cpu(dlbuffer.len); | ||
571 | |||
572 | printk(KERN_DEBUG "Download buffer: %d bytes at 0x%04x:0x%04x\n", | ||
573 | dlbuffer.len, dlbuffer.page, dlbuffer.offset); | ||
574 | |||
575 | bufaddr = (dlbuffer.page << 7) + dlbuffer.offset; | ||
576 | |||
577 | local->hw_downloading = 1; | ||
578 | |||
579 | if (!local->pri_only) { | ||
580 | prism2_hw_shutdown(dev, 0); | ||
581 | |||
582 | if (prism2_hw_init(dev, 0)) { | ||
583 | printk(KERN_WARNING "%s: Could not initialize card for" | ||
584 | " download\n", dev->name); | ||
585 | ret = -1; | ||
586 | goto out; | ||
587 | } | ||
588 | } | ||
589 | |||
590 | hfa384x_disable_interrupts(dev); | ||
591 | |||
592 | if (prism2_enable_aux_port(dev, 1)) { | ||
593 | printk(KERN_WARNING "%s: Could not enable AUX port\n", | ||
594 | dev->name); | ||
595 | ret = -1; | ||
596 | goto out; | ||
597 | } | ||
598 | |||
599 | printk(KERN_DEBUG "%s: starting flash download\n", dev->name); | ||
600 | for (i = 0; i < dl->num_areas; i++) { | ||
601 | int rest_len = dl->data[i].len; | ||
602 | int data_off = 0; | ||
603 | |||
604 | while (rest_len > 0) { | ||
605 | int block_len; | ||
606 | |||
607 | block_len = prism2_download_block( | ||
608 | dev, dl->data[i].addr + data_off, | ||
609 | dl->data[i].data + data_off, bufaddr, | ||
610 | rest_len); | ||
611 | |||
612 | if (block_len < 0) { | ||
613 | ret = -1; | ||
614 | goto out; | ||
615 | } | ||
616 | |||
617 | rest_len -= block_len; | ||
618 | data_off += block_len; | ||
619 | } | ||
620 | } | ||
621 | |||
622 | HFA384X_OUTW(0, HFA384X_PARAM1_OFF); | ||
623 | HFA384X_OUTW(0, HFA384X_PARAM2_OFF); | ||
624 | if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | | ||
625 | (HFA384X_PROGMODE_DISABLE << 8), 0)) { | ||
626 | printk(KERN_WARNING "%s: Download command execution failed\n", | ||
627 | dev->name); | ||
628 | ret = -1; | ||
629 | goto out; | ||
630 | } | ||
631 | |||
632 | if (prism2_enable_aux_port(dev, 0)) { | ||
633 | printk(KERN_DEBUG "%s: Disabling AUX port failed\n", | ||
634 | dev->name); | ||
635 | /* continue anyway.. restart should have taken care of this */ | ||
636 | } | ||
637 | |||
638 | mdelay(5); | ||
639 | |||
640 | local->func->hw_reset(dev); | ||
641 | local->hw_downloading = 0; | ||
642 | if (prism2_hw_config(dev, 2)) { | ||
643 | printk(KERN_WARNING "%s: Card configuration after flash " | ||
644 | "download failed\n", dev->name); | ||
645 | ret = -1; | ||
646 | } else { | ||
647 | printk(KERN_INFO "%s: Card initialized successfully after " | ||
648 | "flash download\n", dev->name); | ||
649 | } | ||
650 | |||
651 | out: | ||
652 | local->hw_downloading = 0; | ||
653 | return ret; | ||
654 | } | ||
655 | #endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ | ||
656 | |||
657 | |||
658 | static void prism2_download_free_data(struct prism2_download_data *dl) | ||
659 | { | ||
660 | int i; | ||
661 | |||
662 | if (dl == NULL) | ||
663 | return; | ||
664 | |||
665 | for (i = 0; i < dl->num_areas; i++) | ||
666 | kfree(dl->data[i].data); | ||
667 | kfree(dl); | ||
668 | } | ||
669 | |||
670 | |||
671 | static int prism2_download(local_info_t *local, | ||
672 | struct prism2_download_param *param) | ||
673 | { | ||
674 | int ret = 0; | ||
675 | int i; | ||
676 | u32 total_len = 0; | ||
677 | struct prism2_download_data *dl = NULL; | ||
678 | |||
679 | printk(KERN_DEBUG "prism2_download: dl_cmd=%d start_addr=0x%08x " | ||
680 | "num_areas=%d\n", | ||
681 | param->dl_cmd, param->start_addr, param->num_areas); | ||
682 | |||
683 | if (param->num_areas > 100) { | ||
684 | ret = -EINVAL; | ||
685 | goto out; | ||
686 | } | ||
687 | |||
688 | dl = kmalloc(sizeof(*dl) + param->num_areas * | ||
689 | sizeof(struct prism2_download_data_area), GFP_KERNEL); | ||
690 | if (dl == NULL) { | ||
691 | ret = -ENOMEM; | ||
692 | goto out; | ||
693 | } | ||
694 | memset(dl, 0, sizeof(*dl) + param->num_areas * | ||
695 | sizeof(struct prism2_download_data_area)); | ||
696 | dl->dl_cmd = param->dl_cmd; | ||
697 | dl->start_addr = param->start_addr; | ||
698 | dl->num_areas = param->num_areas; | ||
699 | for (i = 0; i < param->num_areas; i++) { | ||
700 | PDEBUG(DEBUG_EXTRA2, | ||
701 | " area %d: addr=0x%08x len=%d ptr=0x%p\n", | ||
702 | i, param->data[i].addr, param->data[i].len, | ||
703 | param->data[i].ptr); | ||
704 | |||
705 | dl->data[i].addr = param->data[i].addr; | ||
706 | dl->data[i].len = param->data[i].len; | ||
707 | |||
708 | total_len += param->data[i].len; | ||
709 | if (param->data[i].len > PRISM2_MAX_DOWNLOAD_AREA_LEN || | ||
710 | total_len > PRISM2_MAX_DOWNLOAD_LEN) { | ||
711 | ret = -E2BIG; | ||
712 | goto out; | ||
713 | } | ||
714 | |||
715 | dl->data[i].data = kmalloc(dl->data[i].len, GFP_KERNEL); | ||
716 | if (dl->data[i].data == NULL) { | ||
717 | ret = -ENOMEM; | ||
718 | goto out; | ||
719 | } | ||
720 | |||
721 | if (copy_from_user(dl->data[i].data, param->data[i].ptr, | ||
722 | param->data[i].len)) { | ||
723 | ret = -EFAULT; | ||
724 | goto out; | ||
725 | } | ||
726 | } | ||
727 | |||
728 | switch (param->dl_cmd) { | ||
729 | case PRISM2_DOWNLOAD_VOLATILE: | ||
730 | case PRISM2_DOWNLOAD_VOLATILE_PERSISTENT: | ||
731 | ret = prism2_download_volatile(local, dl); | ||
732 | break; | ||
733 | case PRISM2_DOWNLOAD_VOLATILE_GENESIS: | ||
734 | case PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT: | ||
735 | ret = prism2_download_genesis(local, dl); | ||
736 | break; | ||
737 | case PRISM2_DOWNLOAD_NON_VOLATILE: | ||
738 | #ifdef PRISM2_NON_VOLATILE_DOWNLOAD | ||
739 | ret = prism2_download_nonvolatile(local, dl); | ||
740 | #else /* PRISM2_NON_VOLATILE_DOWNLOAD */ | ||
741 | printk(KERN_INFO "%s: non-volatile downloading not enabled\n", | ||
742 | local->dev->name); | ||
743 | ret = -EOPNOTSUPP; | ||
744 | #endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ | ||
745 | break; | ||
746 | default: | ||
747 | printk(KERN_DEBUG "%s: unsupported download command %d\n", | ||
748 | local->dev->name, param->dl_cmd); | ||
749 | ret = -EINVAL; | ||
750 | break; | ||
751 | }; | ||
752 | |||
753 | out: | ||
754 | if (ret == 0 && dl && | ||
755 | param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT) { | ||
756 | prism2_download_free_data(local->dl_pri); | ||
757 | local->dl_pri = dl; | ||
758 | } else if (ret == 0 && dl && | ||
759 | param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_PERSISTENT) { | ||
760 | prism2_download_free_data(local->dl_sec); | ||
761 | local->dl_sec = dl; | ||
762 | } else | ||
763 | prism2_download_free_data(dl); | ||
764 | |||
765 | return ret; | ||
766 | } | ||