1 | /* $NetBSD: if_cnw.c,v 1.59 2016/10/02 14:16:03 christos Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1998, 2004 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Michael Eriksson. |
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 | * Copyright (c) 1996, 1997 Berkeley Software Design, Inc. |
34 | * All rights reserved. |
35 | * |
36 | * Redistribution and use in source and binary forms, with or without |
37 | * modification, are permitted provided that this notice is retained, |
38 | * the conditions in the following notices are met, and terms applying |
39 | * to contributors in the following notices also apply to Berkeley |
40 | * Software Design, Inc. |
41 | * |
42 | * 1. Redistributions of source code must retain the above copyright |
43 | * notice, this list of conditions and the following disclaimer. |
44 | * 2. Redistributions in binary form must reproduce the above copyright |
45 | * notice, this list of conditions and the following disclaimer in the |
46 | * documentation and/or other materials provided with the distribution. |
47 | * 3. All advertising materials mentioning features or use of this software |
48 | * must display the following acknowledgement: |
49 | * This product includes software developed by |
50 | * Berkeley Software Design, Inc. |
51 | * 4. Neither the name of the Berkeley Software Design, Inc. nor the names |
52 | * of its contributors may be used to endorse or promote products derived |
53 | * from this software without specific prior written permission. |
54 | * |
55 | * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND |
56 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
57 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
58 | * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE |
59 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
60 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
61 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
62 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
63 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
64 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
65 | * SUCH DAMAGE. |
66 | * |
67 | * Paul Borman, December 1996 |
68 | * |
69 | * This driver is derived from a generic frame work which is |
70 | * Copyright(c) 1994,1995,1996 |
71 | * Yoichi Shinoda, Yoshitaka Tokugawa, WIDE Project, Wildboar Project |
72 | * and Foretune. All rights reserved. |
73 | * |
74 | * A linux driver was used as the "hardware reference manual" (i.e., |
75 | * to determine registers and a general outline of how the card works) |
76 | * That driver is publically available and copyright |
77 | * |
78 | * John Markus Bjørndalen |
79 | * Department of Computer Science |
80 | * University of Tromsø |
81 | * Norway |
82 | * johnm@staff.cs.uit.no, http://www.cs.uit.no/~johnm/ |
83 | */ |
84 | |
85 | /* |
86 | * This is a driver for the Xircom CreditCard Netwave (also known as |
87 | * the Netwave Airsurfer) wireless LAN PCMCIA adapter. |
88 | * |
89 | * When this driver was developed, the Linux Netwave driver was used |
90 | * as a hardware manual. That driver is Copyright (c) 1997 University |
91 | * of Tromsø, Norway. It is part of the Linux pcmcia-cs package that |
92 | * can be found at http://pcmcia-cs.sourceforge.net/. The most recent |
93 | * version of the pcmcia-cs package when this driver was written was |
94 | * 3.0.6. |
95 | * |
96 | * Unfortunately, a lot of explicit numeric constants were used in the |
97 | * Linux driver. I have tried to use symbolic names whenever possible, |
98 | * but since I don't have any real hardware documentation, there's |
99 | * still one or two "magic numbers" :-(. |
100 | * |
101 | * Driver limitations: This driver doesn't do multicasting or receiver |
102 | * promiscuity, because of missing hardware documentation. I couldn't |
103 | * get receiver promiscuity to work, and I haven't even tried |
104 | * multicast. Volunteers are welcome, of course :-). |
105 | */ |
106 | |
107 | #include <sys/cdefs.h> |
108 | __KERNEL_RCSID(0, "$NetBSD: if_cnw.c,v 1.59 2016/10/02 14:16:03 christos Exp $" ); |
109 | |
110 | #include "opt_inet.h" |
111 | |
112 | #include <sys/param.h> |
113 | #include <sys/systm.h> |
114 | #include <sys/device.h> |
115 | #include <sys/socket.h> |
116 | #include <sys/mbuf.h> |
117 | #include <sys/ioctl.h> |
118 | #include <sys/proc.h> |
119 | #include <sys/kauth.h> |
120 | |
121 | #include <net/if.h> |
122 | |
123 | #include <dev/pcmcia/if_cnwreg.h> |
124 | #include <dev/pcmcia/if_cnwioctl.h> |
125 | |
126 | #include <dev/pcmcia/pcmciareg.h> |
127 | #include <dev/pcmcia/pcmciavar.h> |
128 | #include <dev/pcmcia/pcmciadevs.h> |
129 | |
130 | #include <net/if_dl.h> |
131 | #include <net/if_ether.h> |
132 | |
133 | #ifdef INET |
134 | #include <netinet/in.h> |
135 | #include <netinet/in_systm.h> |
136 | #include <netinet/in_var.h> |
137 | #include <netinet/ip.h> |
138 | #include <netinet/if_inarp.h> |
139 | #endif |
140 | |
141 | #include <net/bpf.h> |
142 | #include <net/bpfdesc.h> |
143 | |
144 | /* |
145 | * Let these be patchable variables, initialized from macros that can |
146 | * be set in the kernel config file. Someone with lots of spare time |
147 | * could probably write a nice Netwave configuration program to do |
148 | * this a little bit more elegantly :-). |
149 | */ |
150 | #ifndef CNW_DOMAIN |
151 | #define CNW_DOMAIN 0x100 |
152 | #endif |
153 | int cnw_domain = CNW_DOMAIN; /* Domain */ |
154 | #ifndef CNW_SCRAMBLEKEY |
155 | #define CNW_SCRAMBLEKEY 0 |
156 | #endif |
157 | int cnw_skey = CNW_SCRAMBLEKEY; /* Scramble key */ |
158 | |
159 | /* |
160 | * The card appears to work much better when we only allow one packet |
161 | * "in the air" at a time. This is done by not allowing another packet |
162 | * on the card, even if there is room. Turning this off will allow the |
163 | * driver to stuff packets on the card as soon as a transmit buffer is |
164 | * available. This does increase the number of collisions, though. |
165 | * We can que a second packet if there are transmit buffers available, |
166 | * but we do not actually send the packet until the last packet has |
167 | * been written. |
168 | */ |
169 | #define ONE_AT_A_TIME |
170 | |
171 | /* |
172 | * Netwave cards choke if we try to use io memory address >= 0x400. |
173 | * Even though, CIS tuple does not talk about this. |
174 | * Use memory mapped access. |
175 | */ |
176 | #define MEMORY_MAPPED |
177 | |
178 | int cnw_match(device_t, cfdata_t, void *); |
179 | void cnw_attach(device_t, device_t, void *); |
180 | int cnw_detach(device_t, int); |
181 | |
182 | int cnw_activate(device_t, enum devact); |
183 | |
184 | struct cnw_softc { |
185 | device_t sc_dev; /* Device glue (must be first) */ |
186 | struct ethercom sc_ethercom; /* Ethernet common part */ |
187 | int sc_domain; /* Netwave domain */ |
188 | int sc_skey; /* Netwave scramble key */ |
189 | struct cnwstats sc_stats; |
190 | |
191 | /* PCMCIA-specific stuff */ |
192 | struct pcmcia_function *sc_pf; /* PCMCIA function */ |
193 | #ifndef MEMORY_MAPPED |
194 | struct pcmcia_io_handle sc_pcioh; /* PCMCIA I/O space handle */ |
195 | int sc_iowin; /* ...window */ |
196 | bus_space_tag_t sc_iot; /* ...bus_space tag */ |
197 | bus_space_handle_t sc_ioh; /* ...bus_space handle */ |
198 | #endif |
199 | struct pcmcia_mem_handle sc_pcmemh; /* PCMCIA memory handle */ |
200 | bus_size_t sc_memoff; /* ...offset */ |
201 | int sc_memwin; /* ...window */ |
202 | bus_space_tag_t sc_memt; /* ...bus_space tag */ |
203 | bus_space_handle_t sc_memh; /* ...bus_space handle */ |
204 | void *sc_ih; /* Interrupt cookie */ |
205 | struct timeval sc_txlast; /* When the last xmit was made */ |
206 | int sc_active; /* Currently xmitting a packet */ |
207 | |
208 | int sc_resource; /* Resources alloc'ed on attach */ |
209 | #define CNW_RES_PCIC 1 |
210 | #define CNW_RES_IO 2 |
211 | #define CNW_RES_MEM 4 |
212 | #define CNW_RES_NET 8 |
213 | }; |
214 | |
215 | CFATTACH_DECL_NEW(cnw, sizeof(struct cnw_softc), |
216 | cnw_match, cnw_attach, cnw_detach, cnw_activate); |
217 | |
218 | void cnw_reset(struct cnw_softc *); |
219 | void cnw_init(struct cnw_softc *); |
220 | int cnw_enable(struct cnw_softc *sc); |
221 | void cnw_disable(struct cnw_softc *sc); |
222 | void cnw_config(struct cnw_softc *sc, u_int8_t *); |
223 | void cnw_start(struct ifnet *); |
224 | void cnw_transmit(struct cnw_softc *, struct mbuf *); |
225 | struct mbuf *cnw_read(struct cnw_softc *); |
226 | void cnw_recv(struct cnw_softc *); |
227 | int cnw_intr(void *arg); |
228 | int cnw_ioctl(struct ifnet *, u_long, void *); |
229 | void cnw_watchdog(struct ifnet *); |
230 | static int cnw_setdomain(struct cnw_softc *, int); |
231 | static int cnw_setkey(struct cnw_softc *, int); |
232 | |
233 | /* ---------------------------------------------------------------- */ |
234 | |
235 | /* Help routines */ |
236 | static int wait_WOC(struct cnw_softc *, int); |
237 | static int read16(struct cnw_softc *, int); |
238 | static int cnw_cmd(struct cnw_softc *, int, int, int, int); |
239 | |
240 | /* |
241 | * Wait until the WOC (Write Operation Complete) bit in the |
242 | * ASR (Adapter Status Register) is asserted. |
243 | */ |
244 | static int |
245 | wait_WOC(struct cnw_softc *sc, int line) |
246 | { |
247 | int i, asr; |
248 | |
249 | for (i = 0; i < 5000; i++) { |
250 | #ifndef MEMORY_MAPPED |
251 | asr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CNW_REG_ASR); |
252 | #else |
253 | asr = bus_space_read_1(sc->sc_memt, sc->sc_memh, |
254 | sc->sc_memoff + CNW_IOM_OFF + CNW_REG_ASR); |
255 | #endif |
256 | if (asr & CNW_ASR_WOC) |
257 | return (0); |
258 | DELAY(100); |
259 | } |
260 | if (line > 0) |
261 | printf("%s: wedged at line %d\n" , device_xname(sc->sc_dev), line); |
262 | return (1); |
263 | } |
264 | #define WAIT_WOC(sc) wait_WOC(sc, __LINE__) |
265 | |
266 | |
267 | /* |
268 | * Read a 16 bit value from the card. |
269 | */ |
270 | static int |
271 | read16(struct cnw_softc *sc, int offset) |
272 | { |
273 | int hi, lo; |
274 | int offs = sc->sc_memoff + offset; |
275 | |
276 | /* This could presumably be done more efficient with |
277 | * bus_space_read_2(), but I don't know anything about the |
278 | * byte sex guarantees... Besides, this is pretty cheap as |
279 | * well :-) |
280 | */ |
281 | lo = bus_space_read_1(sc->sc_memt, sc->sc_memh, offs); |
282 | hi = bus_space_read_1(sc->sc_memt, sc->sc_memh, offs + 1); |
283 | return ((hi << 8) | lo); |
284 | } |
285 | |
286 | |
287 | /* |
288 | * Send a command to the card by writing it to the command buffer. |
289 | */ |
290 | int |
291 | cnw_cmd(struct cnw_softc *sc, int cmd, int count, int arg1, int arg2) |
292 | { |
293 | int ptr = sc->sc_memoff + CNW_EREG_CB; |
294 | |
295 | if (wait_WOC(sc, 0)) { |
296 | printf("%s: wedged when issuing cmd 0x%x\n" , |
297 | device_xname(sc->sc_dev), cmd); |
298 | /* |
299 | * We'll continue anyway, as that's probably the best |
300 | * thing we can do; at least the user knows there's a |
301 | * problem, and can reset the interface with ifconfig |
302 | * down/up. |
303 | */ |
304 | } |
305 | |
306 | bus_space_write_1(sc->sc_memt, sc->sc_memh, ptr, cmd); |
307 | if (count > 0) { |
308 | bus_space_write_1(sc->sc_memt, sc->sc_memh, ptr + 1, arg1); |
309 | if (count > 1) |
310 | bus_space_write_1(sc->sc_memt, sc->sc_memh, |
311 | ptr + 2, arg2); |
312 | } |
313 | bus_space_write_1(sc->sc_memt, sc->sc_memh, |
314 | ptr + count + 1, CNW_CMD_EOC); |
315 | return (0); |
316 | } |
317 | #define CNW_CMD0(sc, cmd) \ |
318 | do { cnw_cmd(sc, cmd, 0, 0, 0); } while (0) |
319 | #define CNW_CMD1(sc, cmd, arg1) \ |
320 | do { cnw_cmd(sc, cmd, 1, arg1 , 0); } while (0) |
321 | #define CNW_CMD2(sc, cmd, arg1, arg2) \ |
322 | do { cnw_cmd(sc, cmd, 2, arg1, arg2); } while (0) |
323 | |
324 | /* ---------------------------------------------------------------- */ |
325 | |
326 | /* |
327 | * Reset the hardware. |
328 | */ |
329 | void |
330 | cnw_reset(struct cnw_softc *sc) |
331 | { |
332 | #ifdef CNW_DEBUG |
333 | if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) |
334 | printf("%s: resetting\n" , device_xname(sc->sc_dev)); |
335 | #endif |
336 | wait_WOC(sc, 0); |
337 | #ifndef MEMORY_MAPPED |
338 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, CNW_REG_PMR, CNW_PMR_RESET); |
339 | #else |
340 | bus_space_write_1(sc->sc_memt, sc->sc_memh, |
341 | sc->sc_memoff + CNW_IOM_OFF + CNW_REG_PMR, CNW_PMR_RESET); |
342 | #endif |
343 | bus_space_write_1(sc->sc_memt, sc->sc_memh, |
344 | sc->sc_memoff + CNW_EREG_ASCC, CNW_ASR_WOC); |
345 | #ifndef MEMORY_MAPPED |
346 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, CNW_REG_PMR, 0); |
347 | #else |
348 | bus_space_write_1(sc->sc_memt, sc->sc_memh, |
349 | sc->sc_memoff + CNW_IOM_OFF + CNW_REG_PMR, 0); |
350 | #endif |
351 | } |
352 | |
353 | |
354 | /* |
355 | * Initialize the card. |
356 | */ |
357 | void |
358 | cnw_init(struct cnw_softc *sc) |
359 | { |
360 | struct ifnet *ifp = &sc->sc_ethercom.ec_if; |
361 | const u_int8_t rxmode = |
362 | CNW_RXCONF_RXENA | CNW_RXCONF_BCAST | CNW_RXCONF_AMP; |
363 | |
364 | /* Reset the card */ |
365 | cnw_reset(sc); |
366 | |
367 | /* Issue a NOP to check the card */ |
368 | CNW_CMD0(sc, CNW_CMD_NOP); |
369 | |
370 | /* Set up receive configuration */ |
371 | CNW_CMD1(sc, CNW_CMD_SRC, |
372 | rxmode | ((ifp->if_flags & IFF_PROMISC) ? CNW_RXCONF_PRO : 0)); |
373 | |
374 | /* Set up transmit configuration */ |
375 | CNW_CMD1(sc, CNW_CMD_STC, CNW_TXCONF_TXENA); |
376 | |
377 | /* Set domain */ |
378 | CNW_CMD2(sc, CNW_CMD_SMD, sc->sc_domain, sc->sc_domain >> 8); |
379 | |
380 | /* Set scramble key */ |
381 | CNW_CMD2(sc, CNW_CMD_SSK, sc->sc_skey, sc->sc_skey >> 8); |
382 | |
383 | /* Enable interrupts */ |
384 | WAIT_WOC(sc); |
385 | #ifndef MEMORY_MAPPED |
386 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, |
387 | CNW_REG_IMR, CNW_IMR_IENA | CNW_IMR_RFU1); |
388 | #else |
389 | bus_space_write_1(sc->sc_memt, sc->sc_memh, |
390 | sc->sc_memoff + CNW_IOM_OFF + CNW_REG_IMR, |
391 | CNW_IMR_IENA | CNW_IMR_RFU1); |
392 | #endif |
393 | |
394 | /* Enable receiver */ |
395 | CNW_CMD0(sc, CNW_CMD_ER); |
396 | |
397 | /* "Set the IENA bit in COR" */ |
398 | WAIT_WOC(sc); |
399 | #ifndef MEMORY_MAPPED |
400 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, CNW_REG_COR, |
401 | CNW_COR_IENA | CNW_COR_LVLREQ); |
402 | #else |
403 | bus_space_write_1(sc->sc_memt, sc->sc_memh, |
404 | sc->sc_memoff + CNW_IOM_OFF + CNW_REG_COR, |
405 | CNW_COR_IENA | CNW_COR_LVLREQ); |
406 | #endif |
407 | } |
408 | |
409 | |
410 | /* |
411 | * Enable and initialize the card. |
412 | */ |
413 | int |
414 | cnw_enable(struct cnw_softc *sc) |
415 | { |
416 | struct ifnet *ifp = &sc->sc_ethercom.ec_if; |
417 | |
418 | if ((ifp->if_flags & IFF_RUNNING) != 0) |
419 | return (0); |
420 | |
421 | sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_NET, cnw_intr, sc); |
422 | if (sc->sc_ih == NULL) { |
423 | aprint_error_dev(sc->sc_dev, "couldn't establish interrupt handler\n" ); |
424 | return (EIO); |
425 | } |
426 | if (pcmcia_function_enable(sc->sc_pf) != 0) { |
427 | aprint_error_dev(sc->sc_dev, "couldn't enable card\n" ); |
428 | return (EIO); |
429 | } |
430 | sc->sc_resource |= CNW_RES_PCIC; |
431 | cnw_init(sc); |
432 | ifp->if_flags &= ~IFF_OACTIVE; |
433 | ifp->if_flags |= IFF_RUNNING; |
434 | return (0); |
435 | } |
436 | |
437 | |
438 | /* |
439 | * Stop and disable the card. |
440 | */ |
441 | void |
442 | cnw_disable(struct cnw_softc *sc) |
443 | { |
444 | struct ifnet *ifp = &sc->sc_ethercom.ec_if; |
445 | |
446 | if ((ifp->if_flags & IFF_RUNNING) == 0) |
447 | return; |
448 | |
449 | pcmcia_function_disable(sc->sc_pf); |
450 | sc->sc_resource &= ~CNW_RES_PCIC; |
451 | pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); |
452 | ifp->if_flags &= ~IFF_RUNNING; |
453 | ifp->if_timer = 0; |
454 | } |
455 | |
456 | |
457 | /* |
458 | * Match the hardware we handle. |
459 | */ |
460 | int |
461 | cnw_match(device_t parent, cfdata_t match, void *aux) |
462 | { |
463 | struct pcmcia_attach_args *pa = aux; |
464 | |
465 | if (pa->manufacturer == PCMCIA_VENDOR_XIRCOM && |
466 | pa->product == PCMCIA_PRODUCT_XIRCOM_CNW_801) |
467 | return 1; |
468 | if (pa->manufacturer == PCMCIA_VENDOR_XIRCOM && |
469 | pa->product == PCMCIA_PRODUCT_XIRCOM_CNW_802) |
470 | return 1; |
471 | return 0; |
472 | } |
473 | |
474 | |
475 | /* |
476 | * Attach the card. |
477 | */ |
478 | void |
479 | cnw_attach(device_t parent, device_t self, void *aux) |
480 | { |
481 | struct cnw_softc *sc = device_private(self); |
482 | struct pcmcia_attach_args *pa = aux; |
483 | struct ifnet *ifp = &sc->sc_ethercom.ec_if; |
484 | u_int8_t macaddr[ETHER_ADDR_LEN]; |
485 | int i; |
486 | bus_size_t memsize; |
487 | |
488 | sc->sc_dev = self; |
489 | sc->sc_resource = 0; |
490 | |
491 | /* Enable the card */ |
492 | sc->sc_pf = pa->pf; |
493 | pcmcia_function_init(sc->sc_pf, SIMPLEQ_FIRST(&sc->sc_pf->cfe_head)); |
494 | if (pcmcia_function_enable(sc->sc_pf)) { |
495 | aprint_error_dev(self, "function enable failed\n" ); |
496 | return; |
497 | } |
498 | sc->sc_resource |= CNW_RES_PCIC; |
499 | |
500 | /* Map I/O register and "memory" */ |
501 | #ifndef MEMORY_MAPPED |
502 | if (pcmcia_io_alloc(sc->sc_pf, 0, CNW_IO_SIZE, CNW_IO_SIZE, |
503 | &sc->sc_pcioh) != 0) { |
504 | aprint_error_dev(self, "can't allocate i/o space\n" ); |
505 | goto fail; |
506 | } |
507 | if (pcmcia_io_map(sc->sc_pf, PCMCIA_WIDTH_IO16, &sc->sc_pcioh, |
508 | &sc->sc_iowin) != 0) { |
509 | aprint_error_dev(self, "can't map i/o space\n" ); |
510 | pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh); |
511 | goto fail; |
512 | } |
513 | sc->sc_iot = sc->sc_pcioh.iot; |
514 | sc->sc_ioh = sc->sc_pcioh.ioh; |
515 | sc->sc_resource |= CNW_RES_IO; |
516 | #endif |
517 | #ifndef MEMORY_MAPPED |
518 | memsize = CNW_MEM_SIZE; |
519 | #else |
520 | memsize = CNW_MEM_SIZE + CNW_IOM_SIZE; |
521 | #endif |
522 | if (pcmcia_mem_alloc(sc->sc_pf, memsize, &sc->sc_pcmemh) != 0) { |
523 | aprint_error_dev(self, "can't allocate memory\n" ); |
524 | goto fail; |
525 | } |
526 | if (pcmcia_mem_map(sc->sc_pf, PCMCIA_WIDTH_MEM8|PCMCIA_MEM_COMMON, |
527 | CNW_MEM_ADDR, memsize, &sc->sc_pcmemh, &sc->sc_memoff, |
528 | &sc->sc_memwin) != 0) { |
529 | aprint_error_dev(self, "can't map memory\n" ); |
530 | pcmcia_mem_free(sc->sc_pf, &sc->sc_pcmemh); |
531 | goto fail; |
532 | } |
533 | sc->sc_memt = sc->sc_pcmemh.memt; |
534 | sc->sc_memh = sc->sc_pcmemh.memh; |
535 | sc->sc_resource |= CNW_RES_MEM; |
536 | |
537 | /* Finish setup of softc */ |
538 | sc->sc_domain = cnw_domain; |
539 | sc->sc_skey = cnw_skey; |
540 | |
541 | /* Get MAC address */ |
542 | cnw_reset(sc); |
543 | for (i = 0; i < ETHER_ADDR_LEN; i++) |
544 | macaddr[i] = bus_space_read_1(sc->sc_memt, sc->sc_memh, |
545 | sc->sc_memoff + CNW_EREG_PA + i); |
546 | printf("%s: address %s\n" , device_xname(sc->sc_dev), |
547 | ether_sprintf(macaddr)); |
548 | |
549 | /* Set up ifnet structure */ |
550 | strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ); |
551 | ifp->if_softc = sc; |
552 | ifp->if_start = cnw_start; |
553 | ifp->if_ioctl = cnw_ioctl; |
554 | ifp->if_watchdog = cnw_watchdog; |
555 | ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | |
556 | IFF_NOTRAILERS; |
557 | IFQ_SET_READY(&ifp->if_snd); |
558 | |
559 | /* Attach the interface */ |
560 | if_attach(ifp); |
561 | ether_ifattach(ifp, macaddr); |
562 | |
563 | sc->sc_resource |= CNW_RES_NET; |
564 | |
565 | ifp->if_baudrate = IF_Mbps(1); |
566 | |
567 | /* Disable the card now, and turn it on when the interface goes up */ |
568 | pcmcia_function_disable(sc->sc_pf); |
569 | sc->sc_resource &= ~CNW_RES_PCIC; |
570 | return; |
571 | |
572 | fail: |
573 | #ifndef MEMORY_MAPPED |
574 | if ((sc->sc_resource & CNW_RES_IO) != 0) { |
575 | pcmcia_io_unmap(sc->sc_pf, sc->sc_iowin); |
576 | pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh); |
577 | sc->sc_resource &= ~CNW_RES_IO; |
578 | } |
579 | #endif |
580 | if ((sc->sc_resource & CNW_RES_PCIC) != 0) { |
581 | pcmcia_function_disable(sc->sc_pf); |
582 | sc->sc_resource &= ~CNW_RES_PCIC; |
583 | } |
584 | } |
585 | |
586 | /* |
587 | * Start outputting on the interface. |
588 | */ |
589 | void |
590 | cnw_start(struct ifnet *ifp) |
591 | { |
592 | struct cnw_softc *sc = ifp->if_softc; |
593 | struct mbuf *m0; |
594 | int lif; |
595 | int asr; |
596 | #ifdef ONE_AT_A_TIME |
597 | struct timeval now; |
598 | #endif |
599 | |
600 | #ifdef CNW_DEBUG |
601 | if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) |
602 | printf("%s: cnw_start\n" , ifp->if_xname); |
603 | if (ifp->if_flags & IFF_OACTIVE) |
604 | printf("%s: cnw_start reentered\n" , ifp->if_xname); |
605 | #endif |
606 | |
607 | ifp->if_flags |= IFF_OACTIVE; |
608 | |
609 | for (;;) { |
610 | #ifdef ONE_AT_A_TIME |
611 | microtime(&now); |
612 | now.tv_sec -= sc->sc_txlast.tv_sec; |
613 | now.tv_usec -= sc->sc_txlast.tv_usec; |
614 | if (now.tv_usec < 0) { |
615 | now.tv_usec += 1000000; |
616 | now.tv_sec--; |
617 | } |
618 | |
619 | /* |
620 | * Don't ship this packet out until the last |
621 | * packet has left the building. |
622 | * If we have not tried to send a packet for 1/5 |
623 | * a second then we assume we lost an interrupt, |
624 | * lets go on and send the next packet anyhow. |
625 | * |
626 | * I suppose we could check to see if it is okay |
627 | * to put additional packets on the card (beyond |
628 | * the one already waiting to be sent) but I don't |
629 | * think we would get any improvement in speed as |
630 | * we should have ample time to put the next packet |
631 | * on while this one is going out. |
632 | */ |
633 | if (sc->sc_active && now.tv_sec == 0 && now.tv_usec < 200000) |
634 | break; |
635 | #endif |
636 | |
637 | /* Make sure the link integrity field is on */ |
638 | WAIT_WOC(sc); |
639 | lif = bus_space_read_1(sc->sc_memt, sc->sc_memh, |
640 | sc->sc_memoff + CNW_EREG_LIF); |
641 | if (lif == 0) { |
642 | #ifdef CNW_DEBUG |
643 | if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) |
644 | printf("%s: link integrity %d\n" , ifp->if_xname, lif); |
645 | #endif |
646 | break; |
647 | } |
648 | |
649 | /* Is there any buffer space available on the card? */ |
650 | WAIT_WOC(sc); |
651 | #ifndef MEMORY_MAPPED |
652 | asr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CNW_REG_ASR); |
653 | #else |
654 | asr = bus_space_read_1(sc->sc_memt, sc->sc_memh, |
655 | sc->sc_memoff + CNW_IOM_OFF + CNW_REG_ASR); |
656 | #endif |
657 | if (!(asr & CNW_ASR_TXBA)) { |
658 | #ifdef CNW_DEBUG |
659 | if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) |
660 | printf("%s: no buffer space\n" , ifp->if_xname); |
661 | #endif |
662 | break; |
663 | } |
664 | |
665 | sc->sc_stats.nws_tx++; |
666 | |
667 | IFQ_DEQUEUE(&ifp->if_snd, m0); |
668 | if (m0 == 0) |
669 | break; |
670 | |
671 | bpf_mtap(ifp, m0); |
672 | |
673 | cnw_transmit(sc, m0); |
674 | ++ifp->if_opackets; |
675 | ifp->if_timer = 3; /* start watchdog timer */ |
676 | |
677 | microtime(&sc->sc_txlast); |
678 | sc->sc_active = 1; |
679 | } |
680 | |
681 | ifp->if_flags &= ~IFF_OACTIVE; |
682 | } |
683 | |
684 | /* |
685 | * Transmit a packet. |
686 | */ |
687 | void |
688 | cnw_transmit(struct cnw_softc *sc, struct mbuf *m0) |
689 | { |
690 | int buffer, bufsize, bufoffset, bufptr, bufspace, len, mbytes, n; |
691 | struct mbuf *m; |
692 | u_int8_t *mptr; |
693 | |
694 | /* Get buffer info from card */ |
695 | buffer = read16(sc, CNW_EREG_TDP); |
696 | bufsize = read16(sc, CNW_EREG_TDP + 2); |
697 | bufoffset = read16(sc, CNW_EREG_TDP + 4); |
698 | #ifdef CNW_DEBUG |
699 | if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) |
700 | printf("%s: cnw_transmit b=0x%x s=%d o=0x%x\n" , |
701 | device_xname(sc->sc_dev), buffer, bufsize, bufoffset); |
702 | #endif |
703 | |
704 | /* Copy data from mbuf chain to card buffers */ |
705 | bufptr = sc->sc_memoff + buffer + bufoffset; |
706 | bufspace = bufsize; |
707 | len = 0; |
708 | for (m = m0; m; ) { |
709 | mptr = mtod(m, u_int8_t *); |
710 | mbytes = m->m_len; |
711 | len += mbytes; |
712 | while (mbytes > 0) { |
713 | if (bufspace == 0) { |
714 | buffer = read16(sc, buffer); |
715 | bufptr = sc->sc_memoff + buffer + bufoffset; |
716 | bufspace = bufsize; |
717 | #ifdef CNW_DEBUG |
718 | if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) |
719 | printf("%s: next buffer @0x%x\n" , |
720 | device_xname(sc->sc_dev), buffer); |
721 | #endif |
722 | } |
723 | n = mbytes <= bufspace ? mbytes : bufspace; |
724 | bus_space_write_region_1(sc->sc_memt, sc->sc_memh, |
725 | bufptr, mptr, n); |
726 | bufptr += n; |
727 | bufspace -= n; |
728 | mptr += n; |
729 | mbytes -= n; |
730 | } |
731 | m = m0 = m_free(m); |
732 | } |
733 | |
734 | /* Issue transmit command */ |
735 | CNW_CMD2(sc, CNW_CMD_TL, len, len >> 8); |
736 | } |
737 | |
738 | |
739 | /* |
740 | * Pull a packet from the card into an mbuf chain. |
741 | */ |
742 | struct mbuf * |
743 | cnw_read(struct cnw_softc *sc) |
744 | { |
745 | struct mbuf *m, *top, **mp; |
746 | int totbytes, buffer, bufbytes, bufptr, mbytes, n; |
747 | u_int8_t *mptr; |
748 | |
749 | WAIT_WOC(sc); |
750 | totbytes = read16(sc, CNW_EREG_RDP); |
751 | #ifdef CNW_DEBUG |
752 | if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) |
753 | printf("%s: recv %d bytes\n" , device_xname(sc->sc_dev), totbytes); |
754 | #endif |
755 | buffer = CNW_EREG_RDP + 2; |
756 | bufbytes = 0; |
757 | bufptr = 0; /* XXX make gcc happy */ |
758 | |
759 | MGETHDR(m, M_DONTWAIT, MT_DATA); |
760 | if (m == 0) |
761 | return (0); |
762 | m_set_rcvif(m, &sc->sc_ethercom.ec_if); |
763 | m->m_pkthdr.len = totbytes; |
764 | mbytes = MHLEN; |
765 | top = 0; |
766 | mp = ⊤ |
767 | |
768 | while (totbytes > 0) { |
769 | if (top) { |
770 | MGET(m, M_DONTWAIT, MT_DATA); |
771 | if (m == 0) { |
772 | m_freem(top); |
773 | return (0); |
774 | } |
775 | mbytes = MLEN; |
776 | } |
777 | if (totbytes >= MINCLSIZE) { |
778 | MCLGET(m, M_DONTWAIT); |
779 | if ((m->m_flags & M_EXT) == 0) { |
780 | m_free(m); |
781 | m_freem(top); |
782 | return (0); |
783 | } |
784 | mbytes = MCLBYTES; |
785 | } |
786 | if (!top) { |
787 | int pad = ALIGN(sizeof(struct ether_header)) - |
788 | sizeof(struct ether_header); |
789 | m->m_data += pad; |
790 | mbytes -= pad; |
791 | } |
792 | mptr = mtod(m, u_int8_t *); |
793 | mbytes = m->m_len = min(totbytes, mbytes); |
794 | totbytes -= mbytes; |
795 | while (mbytes > 0) { |
796 | if (bufbytes == 0) { |
797 | buffer = read16(sc, buffer); |
798 | bufbytes = read16(sc, buffer + 2); |
799 | bufptr = sc->sc_memoff + buffer + |
800 | read16(sc, buffer + 4); |
801 | #ifdef CNW_DEBUG |
802 | if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) |
803 | printf("%s: %d bytes @0x%x+0x%lx\n" , |
804 | device_xname(sc->sc_dev), bufbytes, |
805 | buffer, (u_long)(bufptr - buffer - |
806 | sc->sc_memoff)); |
807 | #endif |
808 | } |
809 | n = mbytes <= bufbytes ? mbytes : bufbytes; |
810 | bus_space_read_region_1(sc->sc_memt, sc->sc_memh, |
811 | bufptr, mptr, n); |
812 | bufbytes -= n; |
813 | bufptr += n; |
814 | mbytes -= n; |
815 | mptr += n; |
816 | } |
817 | *mp = m; |
818 | mp = &m->m_next; |
819 | } |
820 | |
821 | return (top); |
822 | } |
823 | |
824 | |
825 | /* |
826 | * Handle received packets. |
827 | */ |
828 | void |
829 | cnw_recv(struct cnw_softc *sc) |
830 | { |
831 | int rser; |
832 | struct ifnet *ifp = &sc->sc_ethercom.ec_if; |
833 | struct mbuf *m; |
834 | |
835 | for (;;) { |
836 | WAIT_WOC(sc); |
837 | rser = bus_space_read_1(sc->sc_memt, sc->sc_memh, |
838 | sc->sc_memoff + CNW_EREG_RSER); |
839 | if (!(rser & CNW_RSER_RXAVAIL)) |
840 | return; |
841 | |
842 | /* Pull packet off card */ |
843 | m = cnw_read(sc); |
844 | |
845 | /* Acknowledge packet */ |
846 | CNW_CMD0(sc, CNW_CMD_SRP); |
847 | |
848 | /* Did we manage to get the packet from the interface? */ |
849 | if (m == 0) { |
850 | ++ifp->if_ierrors; |
851 | return; |
852 | } |
853 | ++ifp->if_ipackets; |
854 | |
855 | bpf_mtap(ifp, m); |
856 | |
857 | /* Pass the packet up. */ |
858 | if_percpuq_enqueue(ifp->if_percpuq, m); |
859 | } |
860 | } |
861 | |
862 | |
863 | /* |
864 | * Interrupt handler. |
865 | */ |
866 | int |
867 | cnw_intr(void *arg) |
868 | { |
869 | struct cnw_softc *sc = arg; |
870 | struct ifnet *ifp = &sc->sc_ethercom.ec_if; |
871 | int ret, status, rser, tser; |
872 | |
873 | if ((sc->sc_ethercom.ec_if.if_flags & IFF_RUNNING) == 0 || |
874 | !device_is_active(sc->sc_dev)) |
875 | return (0); |
876 | ifp->if_timer = 0; /* stop watchdog timer */ |
877 | |
878 | ret = 0; |
879 | for (;;) { |
880 | WAIT_WOC(sc); |
881 | #ifndef MEMORY_MAPPED |
882 | status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, |
883 | CNW_REG_CCSR); |
884 | #else |
885 | status = bus_space_read_1(sc->sc_memt, sc->sc_memh, |
886 | sc->sc_memoff + CNW_IOM_OFF + CNW_REG_CCSR); |
887 | #endif |
888 | if (!(status & 0x02)) |
889 | /* No more commands, or shared IRQ */ |
890 | return (ret); |
891 | ret = 1; |
892 | #ifndef MEMORY_MAPPED |
893 | status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CNW_REG_ASR); |
894 | #else |
895 | status = bus_space_read_1(sc->sc_memt, sc->sc_memh, |
896 | sc->sc_memoff + CNW_IOM_OFF + CNW_REG_ASR); |
897 | #endif |
898 | |
899 | /* Anything to receive? */ |
900 | if (status & CNW_ASR_RXRDY) { |
901 | sc->sc_stats.nws_rx++; |
902 | cnw_recv(sc); |
903 | } |
904 | |
905 | /* Receive error */ |
906 | if (status & CNW_ASR_RXERR) { |
907 | /* |
908 | * I get a *lot* of spurious receive errors |
909 | * (many per second), even when the interface |
910 | * is quiescent, so we don't increment |
911 | * if_ierrors here. |
912 | */ |
913 | rser = bus_space_read_1(sc->sc_memt, sc->sc_memh, |
914 | sc->sc_memoff + CNW_EREG_RSER); |
915 | |
916 | /* RX statistics */ |
917 | sc->sc_stats.nws_rxerr++; |
918 | if (rser & CNW_RSER_RXBIG) |
919 | sc->sc_stats.nws_rxframe++; |
920 | if (rser & CNW_RSER_RXCRC) |
921 | sc->sc_stats.nws_rxcrcerror++; |
922 | if (rser & CNW_RSER_RXOVERRUN) |
923 | sc->sc_stats.nws_rxoverrun++; |
924 | if (rser & CNW_RSER_RXOVERFLOW) |
925 | sc->sc_stats.nws_rxoverflow++; |
926 | if (rser & CNW_RSER_RXERR) |
927 | sc->sc_stats.nws_rxerrors++; |
928 | if (rser & CNW_RSER_RXAVAIL) |
929 | sc->sc_stats.nws_rxavail++; |
930 | |
931 | /* Clear error bits in RSER */ |
932 | WAIT_WOC(sc); |
933 | bus_space_write_1(sc->sc_memt, sc->sc_memh, |
934 | sc->sc_memoff + CNW_EREG_RSERW, |
935 | CNW_RSER_RXERR | |
936 | (rser & (CNW_RSER_RXCRC | CNW_RSER_RXBIG))); |
937 | /* Clear RXERR in ASR */ |
938 | WAIT_WOC(sc); |
939 | bus_space_write_1(sc->sc_memt, sc->sc_memh, |
940 | sc->sc_memoff + CNW_EREG_ASCC, CNW_ASR_RXERR); |
941 | } |
942 | |
943 | /* Transmit done */ |
944 | if (status & CNW_ASR_TXDN) { |
945 | tser = bus_space_read_1(sc->sc_memt, sc->sc_memh, |
946 | CNW_EREG_TSER); |
947 | |
948 | /* TX statistics */ |
949 | if (tser & CNW_TSER_TXERR) |
950 | sc->sc_stats.nws_txerrors++; |
951 | if (tser & CNW_TSER_TXNOAP) |
952 | sc->sc_stats.nws_txlostcd++; |
953 | if (tser & CNW_TSER_TXGU) |
954 | sc->sc_stats.nws_txabort++; |
955 | |
956 | if (tser & CNW_TSER_TXOK) { |
957 | sc->sc_stats.nws_txokay++; |
958 | sc->sc_stats.nws_txretries[status & 0xf]++; |
959 | WAIT_WOC(sc); |
960 | bus_space_write_1(sc->sc_memt, sc->sc_memh, |
961 | sc->sc_memoff + CNW_EREG_TSERW, |
962 | CNW_TSER_TXOK | CNW_TSER_RTRY); |
963 | } |
964 | |
965 | if (tser & CNW_TSER_ERROR) { |
966 | ++ifp->if_oerrors; |
967 | WAIT_WOC(sc); |
968 | bus_space_write_1(sc->sc_memt, sc->sc_memh, |
969 | sc->sc_memoff + CNW_EREG_TSERW, |
970 | (tser & CNW_TSER_ERROR) | |
971 | CNW_TSER_RTRY); |
972 | } |
973 | |
974 | sc->sc_active = 0; |
975 | ifp->if_flags &= ~IFF_OACTIVE; |
976 | |
977 | /* Continue to send packets from the queue */ |
978 | cnw_start(&sc->sc_ethercom.ec_if); |
979 | } |
980 | |
981 | } |
982 | } |
983 | |
984 | |
985 | /* |
986 | * Handle device ioctls. |
987 | */ |
988 | int |
989 | cnw_ioctl(struct ifnet *ifp, u_long cmd, void *data) |
990 | { |
991 | struct cnw_softc *sc = ifp->if_softc; |
992 | struct ifaddr *ifa = (struct ifaddr *)data; |
993 | struct ifreq *ifr = (struct ifreq *)data; |
994 | int s, error = 0; |
995 | struct lwp *l = curlwp; /*XXX*/ |
996 | |
997 | switch (cmd) { |
998 | case SIOCINITIFADDR: |
999 | case SIOCSIFFLAGS: |
1000 | case SIOCADDMULTI: |
1001 | case SIOCDELMULTI: |
1002 | case SIOCGCNWDOMAIN: |
1003 | case SIOCGCNWSTATS: |
1004 | break; |
1005 | case SIOCSCNWDOMAIN: |
1006 | case SIOCSCNWKEY: |
1007 | error = kauth_authorize_network(l->l_cred, |
1008 | KAUTH_NETWORK_INTERFACE, |
1009 | KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, KAUTH_ARG(cmd), |
1010 | NULL); |
1011 | if (error) |
1012 | return (error); |
1013 | break; |
1014 | case SIOCGCNWSTATUS: |
1015 | error = kauth_authorize_network(l->l_cred, |
1016 | KAUTH_NETWORK_INTERFACE, |
1017 | KAUTH_REQ_NETWORK_INTERFACE_GETPRIV, ifp, KAUTH_ARG(cmd), |
1018 | NULL); |
1019 | if (error) |
1020 | return (error); |
1021 | break; |
1022 | default: |
1023 | return (EINVAL); |
1024 | } |
1025 | |
1026 | s = splnet(); |
1027 | |
1028 | switch (cmd) { |
1029 | |
1030 | case SIOCINITIFADDR: |
1031 | if (!(ifp->if_flags & IFF_RUNNING) && |
1032 | (error = cnw_enable(sc)) != 0) |
1033 | break; |
1034 | ifp->if_flags |= IFF_UP; |
1035 | cnw_init(sc); |
1036 | switch (ifa->ifa_addr->sa_family) { |
1037 | #ifdef INET |
1038 | case AF_INET: |
1039 | arp_ifinit(&sc->sc_ethercom.ec_if, ifa); |
1040 | break; |
1041 | #endif |
1042 | default: |
1043 | break; |
1044 | } |
1045 | break; |
1046 | |
1047 | case SIOCSIFFLAGS: |
1048 | if ((error = ifioctl_common(ifp, cmd, data)) != 0) |
1049 | break; |
1050 | /* XXX re-use ether_ioctl() */ |
1051 | switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) { |
1052 | case IFF_RUNNING: |
1053 | /* |
1054 | * The interface is marked down and it is running, so |
1055 | * stop it. |
1056 | */ |
1057 | cnw_disable(sc); |
1058 | break; |
1059 | case IFF_UP: |
1060 | /* |
1061 | * The interface is marked up and it is stopped, so |
1062 | * start it. |
1063 | */ |
1064 | error = cnw_enable(sc); |
1065 | break; |
1066 | default: |
1067 | /* IFF_PROMISC may be changed */ |
1068 | cnw_init(sc); |
1069 | break; |
1070 | } |
1071 | break; |
1072 | |
1073 | case SIOCADDMULTI: |
1074 | case SIOCDELMULTI: |
1075 | /* Update our multicast list. */ |
1076 | if ((error = ether_ioctl(ifp, cmd, data)) == ENETRESET) { |
1077 | if (ifp->if_flags & IFF_RUNNING) |
1078 | cnw_init(sc); |
1079 | error = 0; |
1080 | } |
1081 | break; |
1082 | |
1083 | case SIOCGCNWDOMAIN: |
1084 | ifr->ifr_domain = sc->sc_domain; |
1085 | break; |
1086 | |
1087 | case SIOCSCNWDOMAIN: |
1088 | error = cnw_setdomain(sc, ifr->ifr_domain); |
1089 | break; |
1090 | |
1091 | case SIOCSCNWKEY: |
1092 | error = cnw_setkey(sc, ifr->ifr_key); |
1093 | break; |
1094 | |
1095 | case SIOCGCNWSTATUS: |
1096 | if ((ifp->if_flags & IFF_RUNNING) == 0) |
1097 | break; |
1098 | bus_space_read_region_1(sc->sc_memt, sc->sc_memh, |
1099 | sc->sc_memoff + CNW_EREG_CB, |
1100 | ((struct cnwstatus *)data)->data, |
1101 | sizeof(((struct cnwstatus *)data)->data)); |
1102 | break; |
1103 | |
1104 | case SIOCGCNWSTATS: |
1105 | memcpy((void *)&(((struct cnwistats *)data)->stats), |
1106 | (void *)&sc->sc_stats, sizeof(struct cnwstats)); |
1107 | break; |
1108 | |
1109 | default: |
1110 | error = ether_ioctl(ifp, cmd, data); |
1111 | break; |
1112 | } |
1113 | |
1114 | splx(s); |
1115 | return (error); |
1116 | } |
1117 | |
1118 | |
1119 | /* |
1120 | * Device timeout/watchdog routine. Entered if the device neglects to |
1121 | * generate an interrupt after a transmit has been started on it. |
1122 | */ |
1123 | void |
1124 | cnw_watchdog(struct ifnet *ifp) |
1125 | { |
1126 | struct cnw_softc *sc = ifp->if_softc; |
1127 | |
1128 | printf("%s: device timeout; card reset\n" , device_xname(sc->sc_dev)); |
1129 | ++ifp->if_oerrors; |
1130 | cnw_init(sc); |
1131 | } |
1132 | |
1133 | int |
1134 | cnw_setdomain(struct cnw_softc *sc, int domain) |
1135 | { |
1136 | int s; |
1137 | |
1138 | if (domain & ~0x1ff) |
1139 | return EINVAL; |
1140 | |
1141 | s = splnet(); |
1142 | CNW_CMD2(sc, CNW_CMD_SMD, domain, domain >> 8); |
1143 | splx(s); |
1144 | |
1145 | sc->sc_domain = domain; |
1146 | return 0; |
1147 | } |
1148 | |
1149 | int |
1150 | cnw_setkey(struct cnw_softc *sc, int key) |
1151 | { |
1152 | int s; |
1153 | |
1154 | if (key & ~0xffff) |
1155 | return EINVAL; |
1156 | |
1157 | s = splnet(); |
1158 | CNW_CMD2(sc, CNW_CMD_SSK, key, key >> 8); |
1159 | splx(s); |
1160 | |
1161 | sc->sc_skey = key; |
1162 | return 0; |
1163 | } |
1164 | |
1165 | int |
1166 | cnw_activate(device_t self, enum devact act) |
1167 | { |
1168 | struct cnw_softc *sc = device_private(self); |
1169 | |
1170 | switch (act) { |
1171 | case DVACT_DEACTIVATE: |
1172 | if_deactivate(&sc->sc_ethercom.ec_if); |
1173 | return 0; |
1174 | default: |
1175 | return EOPNOTSUPP; |
1176 | } |
1177 | } |
1178 | |
1179 | int |
1180 | cnw_detach(device_t self, int flags) |
1181 | { |
1182 | struct cnw_softc *sc = device_private(self); |
1183 | struct ifnet *ifp = &sc->sc_ethercom.ec_if; |
1184 | |
1185 | /* cnw_disable() checks IFF_RUNNING */ |
1186 | cnw_disable(sc); |
1187 | |
1188 | if ((sc->sc_resource & CNW_RES_NET) != 0) { |
1189 | ether_ifdetach(ifp); |
1190 | if_detach(ifp); |
1191 | } |
1192 | |
1193 | #ifndef MEMORY_MAPPED |
1194 | /* unmap and free our i/o windows */ |
1195 | if ((sc->sc_resource & CNW_RES_IO) != 0) { |
1196 | pcmcia_io_unmap(sc->sc_pf, sc->sc_iowin); |
1197 | pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh); |
1198 | } |
1199 | #endif |
1200 | |
1201 | /* unmap and free our memory windows */ |
1202 | if ((sc->sc_resource & CNW_RES_MEM) != 0) { |
1203 | pcmcia_mem_unmap(sc->sc_pf, sc->sc_memwin); |
1204 | pcmcia_mem_free(sc->sc_pf, &sc->sc_pcmemh); |
1205 | } |
1206 | |
1207 | return (0); |
1208 | } |
1209 | |