1 | /* $NetBSD: if_wi_pcmcia.c,v 1.92 2016/07/07 06:55:42 msaitoh Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2001, 2004 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Ichiro FUKUHARA (ichiro@ichiro.org), and by Charles M. Hannum. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /* |
33 | * PCMCIA attachment for Lucent & Intersil WaveLAN PCMCIA card |
34 | */ |
35 | |
36 | #include <sys/cdefs.h> |
37 | __KERNEL_RCSID(0, "$NetBSD: if_wi_pcmcia.c,v 1.92 2016/07/07 06:55:42 msaitoh Exp $" ); |
38 | |
39 | #include <sys/param.h> |
40 | #include <sys/systm.h> |
41 | #include <sys/callout.h> |
42 | #include <sys/device.h> |
43 | #include <sys/proc.h> |
44 | #include <sys/socket.h> |
45 | |
46 | #include <net/if.h> |
47 | #include <net/if_ether.h> |
48 | #include <net/if_media.h> |
49 | |
50 | #include <net80211/ieee80211_netbsd.h> |
51 | #include <net80211/ieee80211_var.h> |
52 | #include <net80211/ieee80211_radiotap.h> |
53 | #include <net80211/ieee80211_rssadapt.h> |
54 | |
55 | #include <sys/cpu.h> |
56 | #include <sys/bus.h> |
57 | #include <sys/intr.h> |
58 | |
59 | #include <dev/ic/wi_ieee.h> |
60 | #include <dev/ic/wireg.h> |
61 | #include <dev/ic/wivar.h> |
62 | |
63 | #include <dev/pcmcia/pcmciareg.h> |
64 | #include <dev/pcmcia/pcmciavar.h> |
65 | #include <dev/pcmcia/pcmciadevs.h> |
66 | |
67 | #ifdef _MODULE |
68 | #define WI_PCMCIA_SPECTRUM24T_FW 1 |
69 | #else |
70 | #include <opt_if_wi_pcmcia.h> |
71 | #endif |
72 | |
73 | #if WI_PCMCIA_SPECTRUM24T_FW |
74 | #include <dev/microcode/wi/spectrum24t_cf.h> |
75 | #endif |
76 | |
77 | static int wi_pcmcia_match(device_t, cfdata_t, void *); |
78 | static int wi_pcmcia_validate_config(struct pcmcia_config_entry *); |
79 | static void wi_pcmcia_attach(device_t, device_t, void *); |
80 | static int wi_pcmcia_detach(device_t, int); |
81 | static int wi_pcmcia_enable(device_t, int); |
82 | |
83 | #if WI_PCMCIA_SPECTRUM24T_FW |
84 | /* support to download firmware for symbol CF card */ |
85 | static int wi_pcmcia_load_firm(struct wi_softc *, const void *, int, |
86 | const void *, int); |
87 | static int wi_pcmcia_write_firm(struct wi_softc *, const void *, int, |
88 | const void *, int); |
89 | static int wi_pcmcia_set_hcr(struct wi_softc *, int); |
90 | #endif |
91 | |
92 | struct wi_pcmcia_softc { |
93 | struct wi_softc sc_wi; |
94 | |
95 | int sc_symbol_cf; /* Spectrum24t CF card */ |
96 | |
97 | struct pcmcia_function *sc_pf; /* PCMCIA function */ |
98 | int sc_state; |
99 | #define WI_PCMCIA_ATTACHED 3 |
100 | }; |
101 | |
102 | CFATTACH_DECL_NEW(wi_pcmcia, sizeof(struct wi_pcmcia_softc), |
103 | wi_pcmcia_match, wi_pcmcia_attach, wi_pcmcia_detach, wi_activate); |
104 | |
105 | static const struct pcmcia_product wi_pcmcia_products[] = { |
106 | { PCMCIA_VENDOR_INVALID, PCMCIA_PRODUCT_INVALID, |
107 | PCMCIA_CIS_SMC_2632W }, |
108 | |
109 | { PCMCIA_VENDOR_INVALID, PCMCIA_PRODUCT_INVALID, |
110 | PCMCIA_CIS_NANOSPEED_PRISM2 }, |
111 | |
112 | { PCMCIA_VENDOR_INVALID, PCMCIA_PRODUCT_INVALID, |
113 | PCMCIA_CIS_NEC_CMZ_RT_WP }, |
114 | |
115 | { PCMCIA_VENDOR_INVALID, PCMCIA_PRODUCT_INVALID, |
116 | PCMCIA_CIS_NTT_ME_WLAN }, |
117 | |
118 | { PCMCIA_VENDOR_LUCENT, PCMCIA_PRODUCT_LUCENT_HERMES, |
119 | PCMCIA_CIS_INVALID }, |
120 | |
121 | { PCMCIA_VENDOR_LUCENT, PCMCIA_PRODUCT_LUCENT_HERMES2, |
122 | PCMCIA_CIS_INVALID }, |
123 | |
124 | { PCMCIA_VENDOR_3COM, PCMCIA_PRODUCT_3COM_3CRWE737A, |
125 | PCMCIA_CIS_3COM_3CRWE737A }, |
126 | |
127 | { PCMCIA_VENDOR_3COM, PCMCIA_PRODUCT_3COM_3CRWE777A, |
128 | PCMCIA_CIS_3COM_3CRWE777A }, |
129 | |
130 | { PCMCIA_VENDOR_ALVARION, |
131 | PCMCIA_PRODUCT_ALVARION_BREEZENET, |
132 | PCMCIA_CIS_ALVARION_BREEZENET }, |
133 | |
134 | { PCMCIA_VENDOR_COREGA, PCMCIA_PRODUCT_COREGA_WIRELESS_LAN_PCC_11, |
135 | PCMCIA_CIS_COREGA_WIRELESS_LAN_PCC_11 }, |
136 | |
137 | { PCMCIA_VENDOR_COREGA, PCMCIA_PRODUCT_COREGA_WIRELESS_LAN_PCCA_11, |
138 | PCMCIA_CIS_COREGA_WIRELESS_LAN_PCCA_11 }, |
139 | |
140 | { PCMCIA_VENDOR_COREGA, PCMCIA_PRODUCT_COREGA_WIRELESS_LAN_PCCB_11, |
141 | PCMCIA_CIS_COREGA_WIRELESS_LAN_PCCB_11 }, |
142 | |
143 | { PCMCIA_VENDOR_COREGA, PCMCIA_PRODUCT_COREGA_WIRELESS_LAN_PCCL_11, |
144 | PCMCIA_CIS_COREGA_WIRELESS_LAN_PCCL_11 }, |
145 | |
146 | { PCMCIA_VENDOR_COREGA, PCMCIA_PRODUCT_COREGA_WIRELESS_LAN_WLCFL_11, |
147 | PCMCIA_CIS_COREGA_WIRELESS_LAN_WLCFL_11 }, |
148 | |
149 | { PCMCIA_VENDOR_INTEL, PCMCIA_PRODUCT_INTEL_PRO_WLAN_2011, |
150 | PCMCIA_CIS_INTEL_PRO_WLAN_2011 }, |
151 | |
152 | { PCMCIA_VENDOR_INTERSIL2, PCMCIA_PRODUCT_INTERSIL2_PRISM2, |
153 | PCMCIA_CIS_INTERSIL2_PRISM2 }, |
154 | |
155 | { PCMCIA_VENDOR_INTERSIL2, PCMCIA_PRODUCT_GEMTEK_WLAN, |
156 | PCMCIA_CIS_GEMTEK_WLAN }, |
157 | |
158 | { PCMCIA_VENDOR_SAMSUNG, PCMCIA_PRODUCT_SAMSUNG_SWL_2000N, |
159 | PCMCIA_CIS_SAMSUNG_SWL_2000N }, |
160 | |
161 | { PCMCIA_VENDOR_ELSA, PCMCIA_PRODUCT_ELSA_XI300_IEEE, |
162 | PCMCIA_CIS_ELSA_XI300_IEEE }, |
163 | |
164 | { PCMCIA_VENDOR_ELSA, PCMCIA_PRODUCT_ELSA_XI325_IEEE, |
165 | PCMCIA_CIS_ELSA_XI325_IEEE }, |
166 | |
167 | { PCMCIA_VENDOR_ELSA, PCMCIA_PRODUCT_ELSA_XI800_IEEE, |
168 | PCMCIA_CIS_ELSA_XI800_IEEE }, |
169 | |
170 | { PCMCIA_VENDOR_ELSA, PCMCIA_PRODUCT_ELSA_SMC2531WB, |
171 | PCMCIA_CIS_ELSA_SMC2531WB }, |
172 | |
173 | { PCMCIA_VENDOR_COMPAQ, PCMCIA_PRODUCT_COMPAQ_NC5004, |
174 | PCMCIA_CIS_COMPAQ_NC5004 }, |
175 | |
176 | { PCMCIA_VENDOR_CONTEC, PCMCIA_PRODUCT_CONTEC_FX_DS110_PCC, |
177 | PCMCIA_CIS_CONTEC_FX_DS110_PCC }, |
178 | |
179 | { PCMCIA_VENDOR_TDK, PCMCIA_PRODUCT_TDK_LAK_CD011WL, |
180 | PCMCIA_CIS_TDK_LAK_CD011WL }, |
181 | |
182 | { PCMCIA_VENDOR_IODATA2, PCMCIA_PRODUCT_IODATA2_WNB11PCM, |
183 | PCMCIA_CIS_IODATA2_WNB11PCM }, |
184 | |
185 | { PCMCIA_VENDOR_IODATA2, PCMCIA_PRODUCT_IODATA2_WCF12, |
186 | PCMCIA_CIS_IODATA2_WCF12 }, |
187 | |
188 | { PCMCIA_VENDOR_BUFFALO, PCMCIA_PRODUCT_BUFFALO_WLI_PCM_S11, |
189 | PCMCIA_CIS_BUFFALO_WLI_PCM_S11 }, |
190 | |
191 | { PCMCIA_VENDOR_BUFFALO, PCMCIA_PRODUCT_BUFFALO_WLI_CF_S11G, |
192 | PCMCIA_CIS_BUFFALO_WLI_CF_S11G }, |
193 | |
194 | { PCMCIA_VENDOR_EMTAC, PCMCIA_PRODUCT_EMTAC_WLAN, |
195 | PCMCIA_CIS_EMTAC_WLAN }, |
196 | |
197 | { PCMCIA_VENDOR_INTERSIL, PCMCIA_PRODUCT_INTERSIL_ISL37100P, |
198 | PCMCIA_CIS_INTERSIL_ISL37100P }, |
199 | |
200 | { PCMCIA_VENDOR_INTERSIL, PCMCIA_PRODUCT_INTERSIL_ISL37110P, |
201 | PCMCIA_CIS_INTERSIL_ISL37110P }, |
202 | |
203 | { PCMCIA_VENDOR_INTERSIL, PCMCIA_PRODUCT_INTERSIL_ISL37300P, |
204 | PCMCIA_CIS_INTERSIL_ISL37300P }, |
205 | |
206 | { PCMCIA_VENDOR_SIMPLETECH, PCMCIA_PRODUCT_SIMPLETECH_SPECTRUM24, |
207 | PCMCIA_CIS_SIMPLETECH_SPECTRUM24 }, |
208 | |
209 | { PCMCIA_VENDOR_ERICSSON, PCMCIA_PRODUCT_ERICSSON_WIRELESSLAN, |
210 | PCMCIA_CIS_ERICSSON_WIRELESSLAN }, |
211 | |
212 | { PCMCIA_VENDOR_SYMBOL, PCMCIA_PRODUCT_SYMBOL_LA4100, |
213 | PCMCIA_CIS_SYMBOL_LA4100 }, |
214 | |
215 | { PCMCIA_VENDOR_LINKSYS2, PCMCIA_PRODUCT_LINKSYS2_IWN, |
216 | PCMCIA_CIS_NANOSPEED_PRISM2 }, |
217 | |
218 | { PCMCIA_VENDOR_LINKSYS2, PCMCIA_PRODUCT_LINKSYS2_IWN3, |
219 | PCMCIA_CIS_LINKSYS2_IWN3 }, |
220 | |
221 | { PCMCIA_VENDOR_LINKSYS2, PCMCIA_PRODUCT_LINKSYS2_WCF11, |
222 | PCMCIA_CIS_LINKSYS2_WCF11 }, |
223 | |
224 | { PCMCIA_VENDOR_MICROSOFT, PCMCIA_PRODUCT_MICROSOFT_MN_520, |
225 | PCMCIA_CIS_MICROSOFT_MN_520 }, |
226 | |
227 | { PCMCIA_VENDOR_PLANEX, PCMCIA_PRODUCT_PLANEX_GWNS11H, |
228 | PCMCIA_CIS_PLANEX_GWNS11H }, |
229 | |
230 | { PCMCIA_VENDOR_BAY, PCMCIA_PRODUCT_BAY_EMOBILITY_11B, |
231 | PCMCIA_CIS_BAY_EMOBILITY_11B }, |
232 | |
233 | { PCMCIA_VENDOR_ACTIONTEC, PCMCIA_PRODUCT_ACTIONTEC_PRISM, |
234 | PCMCIA_CIS_ACTIONTEC_PRISM }, |
235 | |
236 | { PCMCIA_VENDOR_DLINK_2, PCMCIA_PRODUCT_DLINK_DWL650H, |
237 | PCMCIA_CIS_DLINK_DWL650H }, |
238 | |
239 | { PCMCIA_VENDOR_FUJITSU, PCMCIA_PRODUCT_FUJITSU_WL110, |
240 | PCMCIA_CIS_FUJITSU_WL110 }, |
241 | |
242 | { PCMCIA_VENDOR_ARTEM, PCMCIA_PRODUCT_ARTEM_ONAIR, |
243 | PCMCIA_CIS_ARTEM_ONAIR }, |
244 | |
245 | { PCMCIA_VENDOR_ASUSTEK, PCMCIA_PRODUCT_ASUSTEK_WL_100, |
246 | PCMCIA_CIS_ASUSTEK_WL_100 }, |
247 | |
248 | { PCMCIA_VENDOR_PROXIM, PCMCIA_PRODUCT_PROXIM_RANGELANDS_8430, |
249 | PCMCIA_CIS_PROXIM_RANGELANDS_8430 }, |
250 | |
251 | { PCMCIA_VENDOR_SIEMENS, PCMCIA_PRODUCT_SIEMENS_SS1021, |
252 | PCMCIA_CIS_SIEMENS_SS1021 }, |
253 | }; |
254 | static const size_t wi_pcmcia_nproducts = |
255 | sizeof(wi_pcmcia_products) / sizeof(wi_pcmcia_products[0]); |
256 | |
257 | static int |
258 | wi_pcmcia_match(device_t parent, cfdata_t match, void *aux) |
259 | { |
260 | struct pcmcia_attach_args *pa = aux; |
261 | |
262 | if (pcmcia_product_lookup(pa, wi_pcmcia_products, wi_pcmcia_nproducts, |
263 | sizeof(wi_pcmcia_products[0]), NULL)) |
264 | return 1; |
265 | return 0; |
266 | } |
267 | |
268 | static int |
269 | wi_pcmcia_enable(device_t self, int onoff) |
270 | { |
271 | struct wi_pcmcia_softc *psc = device_private(self); |
272 | struct wi_softc *sc = &psc->sc_wi; |
273 | struct pcmcia_function *pf = psc->sc_pf; |
274 | int error; |
275 | |
276 | if (onoff) { |
277 | /* establish the interrupt. */ |
278 | sc->sc_ih = pcmcia_intr_establish(pf, IPL_NET, wi_intr, sc); |
279 | if (sc->sc_ih == NULL) |
280 | return EIO; |
281 | |
282 | error = pcmcia_function_enable(pf); |
283 | if (error) { |
284 | pcmcia_intr_disestablish(pf, sc->sc_ih); |
285 | sc->sc_ih = NULL; |
286 | return EIO; |
287 | } |
288 | |
289 | if (psc->sc_symbol_cf) { |
290 | #if WI_PCMCIA_SPECTRUM24T_FW |
291 | if (wi_pcmcia_load_firm(sc, |
292 | spectrum24t_primsym, sizeof(spectrum24t_primsym), |
293 | spectrum24t_secsym, sizeof(spectrum24t_secsym))) { |
294 | aprint_error_dev(sc->sc_dev, |
295 | "couldn't load firmware\n" ); |
296 | wi_pcmcia_enable(self, 0); |
297 | return EIO; |
298 | } |
299 | #else |
300 | aprint_error_dev(sc->sc_dev, |
301 | "firmware load not configured\n" ); |
302 | return EIO; |
303 | #endif |
304 | } |
305 | DELAY(1000); |
306 | } else { |
307 | pcmcia_function_disable(psc->sc_pf); |
308 | if (sc->sc_ih != NULL) { |
309 | pcmcia_intr_disestablish(psc->sc_pf, sc->sc_ih); |
310 | sc->sc_ih = NULL; |
311 | } |
312 | } |
313 | |
314 | return 0; |
315 | } |
316 | |
317 | static int |
318 | wi_pcmcia_validate_config(struct pcmcia_config_entry *cfe) |
319 | { |
320 | if (cfe->iftype != PCMCIA_IFTYPE_IO || |
321 | cfe->num_iospace != 1 || |
322 | cfe->iospace[0].length < WI_IOSIZE) |
323 | return EINVAL; |
324 | cfe->num_memspace = 0; |
325 | return 0; |
326 | } |
327 | |
328 | static void |
329 | wi_pcmcia_attach(device_t parent, device_t self, void *aux) |
330 | { |
331 | struct wi_pcmcia_softc *psc = device_private(self); |
332 | struct wi_softc *sc = &psc->sc_wi; |
333 | struct pcmcia_attach_args *pa = aux; |
334 | struct pcmcia_config_entry *cfe; |
335 | int haveaddr; |
336 | int error; |
337 | |
338 | aprint_naive("\n" ); |
339 | |
340 | sc->sc_dev = self; |
341 | psc->sc_pf = pa->pf; |
342 | |
343 | error = pcmcia_function_configure(pa->pf, wi_pcmcia_validate_config); |
344 | if (error) { |
345 | aprint_error_dev(self, "configure failed, error=%d\n" , error); |
346 | return; |
347 | } |
348 | |
349 | cfe = pa->pf->cfe; |
350 | sc->sc_iot = cfe->iospace[0].handle.iot; |
351 | sc->sc_ioh = cfe->iospace[0].handle.ioh; |
352 | |
353 | if (pa->manufacturer == PCMCIA_VENDOR_SYMBOL && |
354 | pa->product == PCMCIA_PRODUCT_SYMBOL_LA4100) |
355 | psc->sc_symbol_cf = 1; |
356 | /* |
357 | * XXX: Sony PEGA-WL100 CF card has a same vendor/product id as |
358 | * Intel PCMCIA card. It may be incorrect to detect by the |
359 | * initial value of COR register. |
360 | */ |
361 | if (pa->manufacturer == PCMCIA_VENDOR_INTEL && |
362 | pa->product == PCMCIA_PRODUCT_INTEL_PRO_WLAN_2011 && |
363 | CSR_READ_2(sc, WI_COR) == WI_COR_IOMODE) |
364 | psc->sc_symbol_cf = 1; |
365 | |
366 | error = wi_pcmcia_enable(self, 1); |
367 | if (error) |
368 | goto fail; |
369 | |
370 | sc->sc_pci = 0; |
371 | sc->sc_enable = wi_pcmcia_enable; |
372 | |
373 | printf("%s:" , device_xname(self)); |
374 | |
375 | haveaddr = pa->pf->pf_funce_lan_nidlen == IEEE80211_ADDR_LEN; |
376 | if (wi_attach(sc, haveaddr ? pa->pf->pf_funce_lan_nid : 0) != 0) { |
377 | aprint_error_dev(self, "failed to attach controller\n" ); |
378 | goto fail2; |
379 | } |
380 | |
381 | if (pmf_device_register(self, NULL, NULL)) |
382 | pmf_class_network_register(self, &sc->sc_if); |
383 | else |
384 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
385 | |
386 | wi_pcmcia_enable(self, 0); |
387 | psc->sc_state = WI_PCMCIA_ATTACHED; |
388 | return; |
389 | |
390 | fail2: |
391 | wi_pcmcia_enable(self, 0); |
392 | fail: |
393 | pcmcia_function_unconfigure(pa->pf); |
394 | } |
395 | |
396 | static int |
397 | wi_pcmcia_detach(device_t self, int flags) |
398 | { |
399 | struct wi_pcmcia_softc *psc = device_private(self); |
400 | struct wi_softc *sc = &psc->sc_wi; |
401 | int error; |
402 | |
403 | if (psc->sc_state != WI_PCMCIA_ATTACHED) |
404 | return 0; |
405 | |
406 | error = wi_detach(&psc->sc_wi); |
407 | if (error != 0) |
408 | return error; |
409 | |
410 | if (sc->sc_ih != NULL) |
411 | pcmcia_intr_disestablish(psc->sc_pf, sc->sc_ih); |
412 | |
413 | pcmcia_function_unconfigure(psc->sc_pf); |
414 | |
415 | return 0; |
416 | } |
417 | |
418 | /* |
419 | * Special routines to download firmware for Symbol CF card. |
420 | * XXX: This should be modified generic into any PRISM-2 based card. |
421 | */ |
422 | |
423 | #define WI_SBCF_PDIADDR 0x3100 |
424 | |
425 | /* unaligned load little endian */ |
426 | #define GETLE32(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24)) |
427 | #define GETLE16(p) ((p)[0] | ((p)[1]<<8)) |
428 | |
429 | #if WI_PCMCIA_SPECTRUM24T_FW |
430 | static int |
431 | wi_pcmcia_load_firm(struct wi_softc *sc, const void *primsym, int primlen, |
432 | const void *secsym, int seclen) |
433 | { |
434 | u_int8_t ebuf[256]; |
435 | int i; |
436 | |
437 | /* load primary code and run it */ |
438 | wi_pcmcia_set_hcr(sc, WI_HCR_EEHOLD); |
439 | if (wi_pcmcia_write_firm(sc, primsym, primlen, NULL, 0)) |
440 | return EIO; |
441 | wi_pcmcia_set_hcr(sc, WI_HCR_RUN); |
442 | for (i = 0; ; i++) { |
443 | if (i == 10) |
444 | return ETIMEDOUT; |
445 | tsleep(sc, PWAIT, "wiinit" , 1); |
446 | if (CSR_READ_2(sc, WI_CNTL) == WI_CNTL_AUX_ENA_STAT) |
447 | break; |
448 | /* write the magic key value to unlock aux port */ |
449 | CSR_WRITE_2(sc, WI_PARAM0, WI_AUX_KEY0); |
450 | CSR_WRITE_2(sc, WI_PARAM1, WI_AUX_KEY1); |
451 | CSR_WRITE_2(sc, WI_PARAM2, WI_AUX_KEY2); |
452 | CSR_WRITE_2(sc, WI_CNTL, WI_CNTL_AUX_ENA_CNTL); |
453 | } |
454 | |
455 | /* issue read EEPROM command: XXX copied from wi_cmd() */ |
456 | CSR_WRITE_2(sc, WI_PARAM0, 0); |
457 | CSR_WRITE_2(sc, WI_PARAM1, 0); |
458 | CSR_WRITE_2(sc, WI_PARAM2, 0); |
459 | CSR_WRITE_2(sc, WI_COMMAND, WI_CMD_READEE); |
460 | for (i = 0; i < WI_TIMEOUT; i++) { |
461 | if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_CMD) |
462 | break; |
463 | DELAY(1); |
464 | } |
465 | CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); |
466 | |
467 | CSR_WRITE_2(sc, WI_AUX_PAGE, WI_SBCF_PDIADDR / WI_AUX_PGSZ); |
468 | CSR_WRITE_2(sc, WI_AUX_OFFSET, WI_SBCF_PDIADDR % WI_AUX_PGSZ); |
469 | CSR_READ_MULTI_STREAM_2(sc, WI_AUX_DATA, |
470 | (u_int16_t *)ebuf, sizeof(ebuf) / 2); |
471 | if (GETLE16(ebuf) > sizeof(ebuf)) |
472 | return EIO; |
473 | if (wi_pcmcia_write_firm(sc, secsym, seclen, ebuf + 4, GETLE16(ebuf))) |
474 | return EIO; |
475 | return 0; |
476 | } |
477 | |
478 | static int |
479 | wi_pcmcia_write_firm(struct wi_softc *sc, const void *buf, int buflen, |
480 | const void *ebuf, int ebuflen) |
481 | { |
482 | const u_int8_t *p, *ep, *q, *eq; |
483 | char *endp; |
484 | u_int32_t addr, id, eid; |
485 | int i, len, elen, nblk, pdrlen; |
486 | |
487 | /* |
488 | * Parse the header of the firmware image. |
489 | */ |
490 | p = buf; |
491 | ep = p + buflen; |
492 | while (p < ep && *p++ != ' '); /* FILE: */ |
493 | while (p < ep && *p++ != ' '); /* filename */ |
494 | while (p < ep && *p++ != ' '); /* type of the firmware */ |
495 | nblk = strtoul(p, &endp, 10); |
496 | p = (void *)endp; |
497 | pdrlen = strtoul(p + 1, &endp, 10); |
498 | p = (void *)endp; |
499 | while (p < ep && *p++ != 0x1a); /* skip rest of header */ |
500 | |
501 | /* |
502 | * Block records: address[4], length[2], data[length]; |
503 | */ |
504 | for (i = 0; i < nblk; i++) { |
505 | addr = GETLE32(p); p += 4; |
506 | len = GETLE16(p); p += 2; |
507 | CSR_WRITE_2(sc, WI_AUX_PAGE, addr / WI_AUX_PGSZ); |
508 | CSR_WRITE_2(sc, WI_AUX_OFFSET, addr % WI_AUX_PGSZ); |
509 | CSR_WRITE_MULTI_STREAM_2(sc, WI_AUX_DATA, |
510 | (const u_int16_t *)p, len / 2); |
511 | p += len; |
512 | } |
513 | |
514 | /* |
515 | * PDR: id[4], address[4], length[4]; |
516 | */ |
517 | for (i = 0; i < pdrlen; ) { |
518 | id = GETLE32(p); p += 4; i += 4; |
519 | addr = GETLE32(p); p += 4; i += 4; |
520 | len = GETLE32(p); p += 4; i += 4; |
521 | /* replace PDR entry with the values from EEPROM, if any */ |
522 | for (q = ebuf, eq = q + ebuflen; q < eq; q += elen * 2) { |
523 | elen = GETLE16(q); q += 2; |
524 | eid = GETLE16(q); q += 2; |
525 | elen--; /* elen includes eid */ |
526 | if (eid == 0) |
527 | break; |
528 | if (eid != id) |
529 | continue; |
530 | CSR_WRITE_2(sc, WI_AUX_PAGE, addr / WI_AUX_PGSZ); |
531 | CSR_WRITE_2(sc, WI_AUX_OFFSET, addr % WI_AUX_PGSZ); |
532 | CSR_WRITE_MULTI_STREAM_2(sc, WI_AUX_DATA, |
533 | (const u_int16_t *)q, len / 2); |
534 | break; |
535 | } |
536 | } |
537 | return 0; |
538 | } |
539 | |
540 | static int |
541 | wi_pcmcia_set_hcr(struct wi_softc *sc, int mode) |
542 | { |
543 | u_int16_t hcr; |
544 | |
545 | CSR_WRITE_2(sc, WI_COR, WI_COR_RESET); |
546 | tsleep(sc, PWAIT, "wiinit" , 1); |
547 | hcr = CSR_READ_2(sc, WI_HCR); |
548 | hcr = (hcr & WI_HCR_4WIRE) | (mode & ~WI_HCR_4WIRE); |
549 | CSR_WRITE_2(sc, WI_HCR, hcr); |
550 | tsleep(sc, PWAIT, "wiinit" , 1); |
551 | CSR_WRITE_2(sc, WI_COR, WI_COR_IOMODE); |
552 | tsleep(sc, PWAIT, "wiinit" , 1); |
553 | return 0; |
554 | } |
555 | #endif |
556 | |