1 | /* $NetBSD: if_cue.c,v 1.73 2016/06/10 13:27:15 ozaki-r Exp $ */ |
2 | /* |
3 | * Copyright (c) 1997, 1998, 1999, 2000 |
4 | * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions |
8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * 3. All advertising materials mentioning features or use of this software |
15 | * must display the following acknowledgement: |
16 | * This product includes software developed by Bill Paul. |
17 | * 4. Neither the name of the author nor the names of any co-contributors |
18 | * may be used to endorse or promote products derived from this software |
19 | * without specific prior written permission. |
20 | * |
21 | * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD |
25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
31 | * THE POSSIBILITY OF SUCH DAMAGE. |
32 | * |
33 | * $FreeBSD: src/sys/dev/usb/if_cue.c,v 1.4 2000/01/16 22:45:06 wpaul Exp $ |
34 | */ |
35 | |
36 | /* |
37 | * CATC USB-EL1210A USB to ethernet driver. Used in the CATC Netmate |
38 | * adapters and others. |
39 | * |
40 | * Written by Bill Paul <wpaul@ee.columbia.edu> |
41 | * Electrical Engineering Department |
42 | * Columbia University, New York City |
43 | */ |
44 | |
45 | /* |
46 | * The CATC USB-EL1210A provides USB ethernet support at 10Mbps. The |
47 | * RX filter uses a 512-bit multicast hash table, single perfect entry |
48 | * for the station address, and promiscuous mode. Unlike the ADMtek |
49 | * and KLSI chips, the CATC ASIC supports read and write combining |
50 | * mode where multiple packets can be transfered using a single bulk |
51 | * transaction, which helps performance a great deal. |
52 | */ |
53 | |
54 | /* |
55 | * Ported to NetBSD and somewhat rewritten by Lennart Augustsson. |
56 | */ |
57 | |
58 | #include <sys/cdefs.h> |
59 | __KERNEL_RCSID(0, "$NetBSD: if_cue.c,v 1.73 2016/06/10 13:27:15 ozaki-r Exp $" ); |
60 | |
61 | #ifdef _KERNEL_OPT |
62 | #include "opt_inet.h" |
63 | #endif |
64 | |
65 | #include <sys/param.h> |
66 | #include <sys/systm.h> |
67 | #include <sys/callout.h> |
68 | #include <sys/sockio.h> |
69 | #include <sys/mbuf.h> |
70 | #include <sys/kernel.h> |
71 | #include <sys/socket.h> |
72 | #include <sys/bus.h> |
73 | #include <sys/device.h> |
74 | |
75 | #include <net/if.h> |
76 | #include <net/if_arp.h> |
77 | #include <net/if_dl.h> |
78 | #include <net/bpf.h> |
79 | #include <net/if_ether.h> |
80 | |
81 | #ifdef INET |
82 | #include <netinet/in.h> |
83 | #include <netinet/if_inarp.h> |
84 | #endif |
85 | |
86 | #include <dev/usb/usb.h> |
87 | #include <dev/usb/usbdi.h> |
88 | #include <dev/usb/usbdi_util.h> |
89 | #include <dev/usb/usbdivar.h> |
90 | #include <dev/usb/usbdevs.h> |
91 | |
92 | #include <dev/usb/if_cuereg.h> |
93 | |
94 | #ifdef CUE_DEBUG |
95 | #define DPRINTF(x) if (cuedebug) printf x |
96 | #define DPRINTFN(n,x) if (cuedebug >= (n)) printf x |
97 | int cuedebug = 0; |
98 | #else |
99 | #define DPRINTF(x) |
100 | #define DPRINTFN(n,x) |
101 | #endif |
102 | |
103 | /* |
104 | * Various supported device vendors/products. |
105 | */ |
106 | Static struct usb_devno cue_devs[] = { |
107 | { USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE }, |
108 | { USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE2 }, |
109 | { USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTLINK }, |
110 | /* Belkin F5U111 adapter covered by NETMATE entry */ |
111 | }; |
112 | #define cue_lookup(v, p) (usb_lookup(cue_devs, v, p)) |
113 | |
114 | int cue_match(device_t, cfdata_t, void *); |
115 | void cue_attach(device_t, device_t, void *); |
116 | int cue_detach(device_t, int); |
117 | int cue_activate(device_t, enum devact); |
118 | extern struct cfdriver cue_cd; |
119 | CFATTACH_DECL_NEW(cue, sizeof(struct cue_softc), cue_match, cue_attach, |
120 | cue_detach, cue_activate); |
121 | |
122 | Static int cue_open_pipes(struct cue_softc *); |
123 | Static int cue_tx_list_init(struct cue_softc *); |
124 | Static int cue_rx_list_init(struct cue_softc *); |
125 | Static int cue_newbuf(struct cue_softc *, struct cue_chain *, struct mbuf *); |
126 | Static int cue_send(struct cue_softc *, struct mbuf *, int); |
127 | Static void cue_rxeof(struct usbd_xfer *, void *, usbd_status); |
128 | Static void cue_txeof(struct usbd_xfer *, void *, usbd_status); |
129 | Static void cue_tick(void *); |
130 | Static void cue_tick_task(void *); |
131 | Static void cue_start(struct ifnet *); |
132 | Static int cue_ioctl(struct ifnet *, u_long, void *); |
133 | Static void cue_init(void *); |
134 | Static void cue_stop(struct cue_softc *); |
135 | Static void cue_watchdog(struct ifnet *); |
136 | |
137 | Static void cue_setmulti(struct cue_softc *); |
138 | Static uint32_t cue_crc(const char *); |
139 | Static void cue_reset(struct cue_softc *); |
140 | |
141 | Static int cue_csr_read_1(struct cue_softc *, int); |
142 | Static int cue_csr_write_1(struct cue_softc *, int, int); |
143 | Static int cue_csr_read_2(struct cue_softc *, int); |
144 | #if 0 |
145 | Static int cue_csr_write_2(struct cue_softc *, int, int); |
146 | #endif |
147 | Static int cue_mem(struct cue_softc *, int, int, void *, int); |
148 | Static int cue_getmac(struct cue_softc *, void *); |
149 | |
150 | #define CUE_SETBIT(sc, reg, x) \ |
151 | cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) | (x)) |
152 | |
153 | #define CUE_CLRBIT(sc, reg, x) \ |
154 | cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) & ~(x)) |
155 | |
156 | Static int |
157 | cue_csr_read_1(struct cue_softc *sc, int reg) |
158 | { |
159 | usb_device_request_t req; |
160 | usbd_status err; |
161 | uint8_t val = 0; |
162 | |
163 | if (sc->cue_dying) |
164 | return 0; |
165 | |
166 | req.bmRequestType = UT_READ_VENDOR_DEVICE; |
167 | req.bRequest = CUE_CMD_READREG; |
168 | USETW(req.wValue, 0); |
169 | USETW(req.wIndex, reg); |
170 | USETW(req.wLength, 1); |
171 | |
172 | err = usbd_do_request(sc->cue_udev, &req, &val); |
173 | |
174 | if (err) { |
175 | DPRINTF(("%s: cue_csr_read_1: reg=0x%x err=%s\n" , |
176 | device_xname(sc->cue_dev), reg, usbd_errstr(err))); |
177 | return 0; |
178 | } |
179 | |
180 | DPRINTFN(10,("%s: cue_csr_read_1 reg=0x%x val=0x%x\n" , |
181 | device_xname(sc->cue_dev), reg, val)); |
182 | |
183 | return val; |
184 | } |
185 | |
186 | Static int |
187 | cue_csr_read_2(struct cue_softc *sc, int reg) |
188 | { |
189 | usb_device_request_t req; |
190 | usbd_status err; |
191 | uWord val; |
192 | |
193 | if (sc->cue_dying) |
194 | return 0; |
195 | |
196 | req.bmRequestType = UT_READ_VENDOR_DEVICE; |
197 | req.bRequest = CUE_CMD_READREG; |
198 | USETW(req.wValue, 0); |
199 | USETW(req.wIndex, reg); |
200 | USETW(req.wLength, 2); |
201 | |
202 | err = usbd_do_request(sc->cue_udev, &req, &val); |
203 | |
204 | DPRINTFN(10,("%s: cue_csr_read_2 reg=0x%x val=0x%x\n" , |
205 | device_xname(sc->cue_dev), reg, UGETW(val))); |
206 | |
207 | if (err) { |
208 | DPRINTF(("%s: cue_csr_read_2: reg=0x%x err=%s\n" , |
209 | device_xname(sc->cue_dev), reg, usbd_errstr(err))); |
210 | return 0; |
211 | } |
212 | |
213 | return UGETW(val); |
214 | } |
215 | |
216 | Static int |
217 | cue_csr_write_1(struct cue_softc *sc, int reg, int val) |
218 | { |
219 | usb_device_request_t req; |
220 | usbd_status err; |
221 | |
222 | if (sc->cue_dying) |
223 | return 0; |
224 | |
225 | DPRINTFN(10,("%s: cue_csr_write_1 reg=0x%x val=0x%x\n" , |
226 | device_xname(sc->cue_dev), reg, val)); |
227 | |
228 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE; |
229 | req.bRequest = CUE_CMD_WRITEREG; |
230 | USETW(req.wValue, val); |
231 | USETW(req.wIndex, reg); |
232 | USETW(req.wLength, 0); |
233 | |
234 | err = usbd_do_request(sc->cue_udev, &req, NULL); |
235 | |
236 | if (err) { |
237 | DPRINTF(("%s: cue_csr_write_1: reg=0x%x err=%s\n" , |
238 | device_xname(sc->cue_dev), reg, usbd_errstr(err))); |
239 | return -1; |
240 | } |
241 | |
242 | DPRINTFN(20,("%s: cue_csr_write_1, after reg=0x%x val=0x%x\n" , |
243 | device_xname(sc->cue_dev), reg, cue_csr_read_1(sc, reg))); |
244 | |
245 | return 0; |
246 | } |
247 | |
248 | #if 0 |
249 | Static int |
250 | cue_csr_write_2(struct cue_softc *sc, int reg, int aval) |
251 | { |
252 | usb_device_request_t req; |
253 | usbd_status err; |
254 | uWord val; |
255 | int s; |
256 | |
257 | if (sc->cue_dying) |
258 | return 0; |
259 | |
260 | DPRINTFN(10,("%s: cue_csr_write_2 reg=0x%x val=0x%x\n" , |
261 | device_xname(sc->cue_dev), reg, aval)); |
262 | |
263 | USETW(val, aval); |
264 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE; |
265 | req.bRequest = CUE_CMD_WRITEREG; |
266 | USETW(req.wValue, val); |
267 | USETW(req.wIndex, reg); |
268 | USETW(req.wLength, 0); |
269 | |
270 | err = usbd_do_request(sc->cue_udev, &req, NULL); |
271 | |
272 | if (err) { |
273 | DPRINTF(("%s: cue_csr_write_2: reg=0x%x err=%s\n" , |
274 | device_xname(sc->cue_dev), reg, usbd_errstr(err))); |
275 | return -1; |
276 | } |
277 | |
278 | return 0; |
279 | } |
280 | #endif |
281 | |
282 | Static int |
283 | cue_mem(struct cue_softc *sc, int cmd, int addr, void *buf, int len) |
284 | { |
285 | usb_device_request_t req; |
286 | usbd_status err; |
287 | |
288 | DPRINTFN(10,("%s: cue_mem cmd=0x%x addr=0x%x len=%d\n" , |
289 | device_xname(sc->cue_dev), cmd, addr, len)); |
290 | |
291 | if (cmd == CUE_CMD_READSRAM) |
292 | req.bmRequestType = UT_READ_VENDOR_DEVICE; |
293 | else |
294 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE; |
295 | req.bRequest = cmd; |
296 | USETW(req.wValue, 0); |
297 | USETW(req.wIndex, addr); |
298 | USETW(req.wLength, len); |
299 | |
300 | err = usbd_do_request(sc->cue_udev, &req, buf); |
301 | |
302 | if (err) { |
303 | DPRINTF(("%s: cue_csr_mem: addr=0x%x err=%s\n" , |
304 | device_xname(sc->cue_dev), addr, usbd_errstr(err))); |
305 | return -1; |
306 | } |
307 | |
308 | return 0; |
309 | } |
310 | |
311 | Static int |
312 | cue_getmac(struct cue_softc *sc, void *buf) |
313 | { |
314 | usb_device_request_t req; |
315 | usbd_status err; |
316 | |
317 | DPRINTFN(10,("%s: cue_getmac\n" , device_xname(sc->cue_dev))); |
318 | |
319 | req.bmRequestType = UT_READ_VENDOR_DEVICE; |
320 | req.bRequest = CUE_CMD_GET_MACADDR; |
321 | USETW(req.wValue, 0); |
322 | USETW(req.wIndex, 0); |
323 | USETW(req.wLength, ETHER_ADDR_LEN); |
324 | |
325 | err = usbd_do_request(sc->cue_udev, &req, buf); |
326 | |
327 | if (err) { |
328 | printf("%s: read MAC address failed\n" , |
329 | device_xname(sc->cue_dev)); |
330 | return -1; |
331 | } |
332 | |
333 | return 0; |
334 | } |
335 | |
336 | #define CUE_POLY 0xEDB88320 |
337 | #define CUE_BITS 9 |
338 | |
339 | Static uint32_t |
340 | cue_crc(const char *addr) |
341 | { |
342 | uint32_t idx, bit, data, crc; |
343 | |
344 | /* Compute CRC for the address value. */ |
345 | crc = 0xFFFFFFFF; /* initial value */ |
346 | |
347 | for (idx = 0; idx < 6; idx++) { |
348 | for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) |
349 | crc = (crc >> 1) ^ (((crc ^ data) & 1) ? CUE_POLY : 0); |
350 | } |
351 | |
352 | return crc & ((1 << CUE_BITS) - 1); |
353 | } |
354 | |
355 | Static void |
356 | cue_setmulti(struct cue_softc *sc) |
357 | { |
358 | struct ifnet *ifp; |
359 | struct ether_multi *enm; |
360 | struct ether_multistep step; |
361 | uint32_t h, i; |
362 | |
363 | ifp = GET_IFP(sc); |
364 | |
365 | DPRINTFN(2,("%s: cue_setmulti if_flags=0x%x\n" , |
366 | device_xname(sc->cue_dev), ifp->if_flags)); |
367 | |
368 | if (ifp->if_flags & IFF_PROMISC) { |
369 | allmulti: |
370 | ifp->if_flags |= IFF_ALLMULTI; |
371 | for (i = 0; i < CUE_MCAST_TABLE_LEN; i++) |
372 | sc->cue_mctab[i] = 0xFF; |
373 | cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, |
374 | &sc->cue_mctab, CUE_MCAST_TABLE_LEN); |
375 | return; |
376 | } |
377 | |
378 | /* first, zot all the existing hash bits */ |
379 | for (i = 0; i < CUE_MCAST_TABLE_LEN; i++) |
380 | sc->cue_mctab[i] = 0; |
381 | |
382 | /* now program new ones */ |
383 | ETHER_FIRST_MULTI(step, &sc->cue_ec, enm); |
384 | while (enm != NULL) { |
385 | if (memcmp(enm->enm_addrlo, |
386 | enm->enm_addrhi, ETHER_ADDR_LEN) != 0) |
387 | goto allmulti; |
388 | |
389 | h = cue_crc(enm->enm_addrlo); |
390 | sc->cue_mctab[h >> 3] |= 1 << (h & 0x7); |
391 | ETHER_NEXT_MULTI(step, enm); |
392 | } |
393 | |
394 | ifp->if_flags &= ~IFF_ALLMULTI; |
395 | |
396 | /* |
397 | * Also include the broadcast address in the filter |
398 | * so we can receive broadcast frames. |
399 | */ |
400 | if (ifp->if_flags & IFF_BROADCAST) { |
401 | h = cue_crc(etherbroadcastaddr); |
402 | sc->cue_mctab[h >> 3] |= 1 << (h & 0x7); |
403 | } |
404 | |
405 | cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, |
406 | &sc->cue_mctab, CUE_MCAST_TABLE_LEN); |
407 | } |
408 | |
409 | Static void |
410 | cue_reset(struct cue_softc *sc) |
411 | { |
412 | usb_device_request_t req; |
413 | usbd_status err; |
414 | |
415 | DPRINTFN(2,("%s: cue_reset\n" , device_xname(sc->cue_dev))); |
416 | |
417 | if (sc->cue_dying) |
418 | return; |
419 | |
420 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE; |
421 | req.bRequest = CUE_CMD_RESET; |
422 | USETW(req.wValue, 0); |
423 | USETW(req.wIndex, 0); |
424 | USETW(req.wLength, 0); |
425 | |
426 | err = usbd_do_request(sc->cue_udev, &req, NULL); |
427 | |
428 | if (err) |
429 | printf("%s: reset failed\n" , device_xname(sc->cue_dev)); |
430 | |
431 | /* Wait a little while for the chip to get its brains in order. */ |
432 | usbd_delay_ms(sc->cue_udev, 1); |
433 | } |
434 | |
435 | /* |
436 | * Probe for a CATC chip. |
437 | */ |
438 | int |
439 | cue_match(device_t parent, cfdata_t match, void *aux) |
440 | { |
441 | struct usb_attach_arg *uaa = aux; |
442 | |
443 | return cue_lookup(uaa->uaa_vendor, uaa->uaa_product) != NULL ? |
444 | UMATCH_VENDOR_PRODUCT : UMATCH_NONE; |
445 | } |
446 | |
447 | /* |
448 | * Attach the interface. Allocate softc structures, do ifmedia |
449 | * setup and ethernet/BPF attach. |
450 | */ |
451 | void |
452 | cue_attach(device_t parent, device_t self, void *aux) |
453 | { |
454 | struct cue_softc *sc = device_private(self); |
455 | struct usb_attach_arg *uaa = aux; |
456 | char *devinfop; |
457 | int s; |
458 | u_char eaddr[ETHER_ADDR_LEN]; |
459 | struct usbd_device * dev = uaa->uaa_device; |
460 | struct usbd_interface * iface; |
461 | usbd_status err; |
462 | struct ifnet *ifp; |
463 | usb_interface_descriptor_t *id; |
464 | usb_endpoint_descriptor_t *ed; |
465 | int i; |
466 | |
467 | DPRINTFN(5,(" : cue_attach: sc=%p, dev=%p" , sc, dev)); |
468 | |
469 | sc->cue_dev = self; |
470 | |
471 | aprint_naive("\n" ); |
472 | aprint_normal("\n" ); |
473 | |
474 | devinfop = usbd_devinfo_alloc(dev, 0); |
475 | aprint_normal_dev(self, "%s\n" , devinfop); |
476 | usbd_devinfo_free(devinfop); |
477 | |
478 | err = usbd_set_config_no(dev, CUE_CONFIG_NO, 1); |
479 | if (err) { |
480 | aprint_error_dev(self, "failed to set configuration" |
481 | ", err=%s\n" , usbd_errstr(err)); |
482 | return; |
483 | } |
484 | |
485 | sc->cue_udev = dev; |
486 | sc->cue_product = uaa->uaa_product; |
487 | sc->cue_vendor = uaa->uaa_vendor; |
488 | |
489 | usb_init_task(&sc->cue_tick_task, cue_tick_task, sc, 0); |
490 | usb_init_task(&sc->cue_stop_task, (void (*)(void *))cue_stop, sc, 0); |
491 | |
492 | err = usbd_device2interface_handle(dev, CUE_IFACE_IDX, &iface); |
493 | if (err) { |
494 | aprint_error_dev(self, "getting interface handle failed\n" ); |
495 | return; |
496 | } |
497 | |
498 | sc->cue_iface = iface; |
499 | id = usbd_get_interface_descriptor(iface); |
500 | |
501 | /* Find endpoints. */ |
502 | for (i = 0; i < id->bNumEndpoints; i++) { |
503 | ed = usbd_interface2endpoint_descriptor(iface, i); |
504 | if (ed == NULL) { |
505 | aprint_error_dev(self, "couldn't get ep %d\n" , i); |
506 | return; |
507 | } |
508 | if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
509 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
510 | sc->cue_ed[CUE_ENDPT_RX] = ed->bEndpointAddress; |
511 | } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && |
512 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
513 | sc->cue_ed[CUE_ENDPT_TX] = ed->bEndpointAddress; |
514 | } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
515 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { |
516 | sc->cue_ed[CUE_ENDPT_INTR] = ed->bEndpointAddress; |
517 | } |
518 | } |
519 | |
520 | #if 0 |
521 | /* Reset the adapter. */ |
522 | cue_reset(sc); |
523 | #endif |
524 | /* |
525 | * Get station address. |
526 | */ |
527 | cue_getmac(sc, &eaddr); |
528 | |
529 | s = splnet(); |
530 | |
531 | /* |
532 | * A CATC chip was detected. Inform the world. |
533 | */ |
534 | aprint_normal_dev(self, "Ethernet address %s\n" , ether_sprintf(eaddr)); |
535 | |
536 | /* Initialize interface info.*/ |
537 | ifp = GET_IFP(sc); |
538 | ifp->if_softc = sc; |
539 | ifp->if_mtu = ETHERMTU; |
540 | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; |
541 | ifp->if_ioctl = cue_ioctl; |
542 | ifp->if_start = cue_start; |
543 | ifp->if_watchdog = cue_watchdog; |
544 | strncpy(ifp->if_xname, device_xname(sc->cue_dev), IFNAMSIZ); |
545 | |
546 | IFQ_SET_READY(&ifp->if_snd); |
547 | |
548 | /* Attach the interface. */ |
549 | if_attach(ifp); |
550 | ether_ifattach(ifp, eaddr); |
551 | rnd_attach_source(&sc->rnd_source, device_xname(sc->cue_dev), |
552 | RND_TYPE_NET, RND_FLAG_DEFAULT); |
553 | |
554 | callout_init(&(sc->cue_stat_ch), 0); |
555 | |
556 | sc->cue_attached = 1; |
557 | splx(s); |
558 | |
559 | usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->cue_udev, sc->cue_dev); |
560 | |
561 | return; |
562 | } |
563 | |
564 | int |
565 | cue_detach(device_t self, int flags) |
566 | { |
567 | struct cue_softc *sc = device_private(self); |
568 | struct ifnet *ifp = GET_IFP(sc); |
569 | int s; |
570 | |
571 | DPRINTFN(2,("%s: %s: enter\n" , device_xname(sc->cue_dev), __func__)); |
572 | |
573 | callout_stop(&sc->cue_stat_ch); |
574 | /* |
575 | * Remove any pending task. It cannot be executing because it run |
576 | * in the same thread as detach. |
577 | */ |
578 | usb_rem_task(sc->cue_udev, &sc->cue_tick_task); |
579 | usb_rem_task(sc->cue_udev, &sc->cue_stop_task); |
580 | |
581 | if (!sc->cue_attached) { |
582 | /* Detached before attached finished, so just bail out. */ |
583 | return 0; |
584 | } |
585 | |
586 | s = splusb(); |
587 | |
588 | if (ifp->if_flags & IFF_RUNNING) |
589 | cue_stop(sc); |
590 | |
591 | rnd_detach_source(&sc->rnd_source); |
592 | ether_ifdetach(ifp); |
593 | |
594 | if_detach(ifp); |
595 | |
596 | #ifdef DIAGNOSTIC |
597 | if (sc->cue_ep[CUE_ENDPT_TX] != NULL || |
598 | sc->cue_ep[CUE_ENDPT_RX] != NULL || |
599 | sc->cue_ep[CUE_ENDPT_INTR] != NULL) |
600 | aprint_debug_dev(self, "detach has active endpoints\n" ); |
601 | #endif |
602 | |
603 | sc->cue_attached = 0; |
604 | splx(s); |
605 | |
606 | usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->cue_udev, sc->cue_dev); |
607 | |
608 | return 0; |
609 | } |
610 | |
611 | int |
612 | cue_activate(device_t self, enum devact act) |
613 | { |
614 | struct cue_softc *sc = device_private(self); |
615 | |
616 | DPRINTFN(2,("%s: %s: enter\n" , device_xname(sc->cue_dev), __func__)); |
617 | |
618 | switch (act) { |
619 | case DVACT_DEACTIVATE: |
620 | /* Deactivate the interface. */ |
621 | if_deactivate(&sc->cue_ec.ec_if); |
622 | sc->cue_dying = 1; |
623 | return 0; |
624 | default: |
625 | return EOPNOTSUPP; |
626 | } |
627 | } |
628 | |
629 | /* |
630 | * Initialize an RX descriptor and attach an MBUF cluster. |
631 | */ |
632 | Static int |
633 | cue_newbuf(struct cue_softc *sc, struct cue_chain *c, struct mbuf *m) |
634 | { |
635 | struct mbuf *m_new = NULL; |
636 | |
637 | if (m == NULL) { |
638 | MGETHDR(m_new, M_DONTWAIT, MT_DATA); |
639 | if (m_new == NULL) { |
640 | printf("%s: no memory for rx list " |
641 | "-- packet dropped!\n" , device_xname(sc->cue_dev)); |
642 | return ENOBUFS; |
643 | } |
644 | |
645 | MCLGET(m_new, M_DONTWAIT); |
646 | if (!(m_new->m_flags & M_EXT)) { |
647 | printf("%s: no memory for rx list " |
648 | "-- packet dropped!\n" , device_xname(sc->cue_dev)); |
649 | m_freem(m_new); |
650 | return ENOBUFS; |
651 | } |
652 | m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; |
653 | } else { |
654 | m_new = m; |
655 | m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; |
656 | m_new->m_data = m_new->m_ext.ext_buf; |
657 | } |
658 | |
659 | m_adj(m_new, ETHER_ALIGN); |
660 | c->cue_mbuf = m_new; |
661 | |
662 | return 0; |
663 | } |
664 | |
665 | Static int |
666 | cue_rx_list_init(struct cue_softc *sc) |
667 | { |
668 | struct cue_cdata *cd; |
669 | struct cue_chain *c; |
670 | int i; |
671 | |
672 | cd = &sc->cue_cdata; |
673 | for (i = 0; i < CUE_RX_LIST_CNT; i++) { |
674 | c = &cd->cue_rx_chain[i]; |
675 | c->cue_sc = sc; |
676 | c->cue_idx = i; |
677 | if (cue_newbuf(sc, c, NULL) == ENOBUFS) |
678 | return ENOBUFS; |
679 | if (c->cue_xfer == NULL) { |
680 | int error = usbd_create_xfer(sc->cue_ep[CUE_ENDPT_RX], |
681 | CUE_BUFSZ, USBD_SHORT_XFER_OK, 0, &c->cue_xfer); |
682 | if (error) |
683 | return error; |
684 | c->cue_buf = usbd_get_buffer(c->cue_xfer); |
685 | } |
686 | } |
687 | |
688 | return 0; |
689 | } |
690 | |
691 | Static int |
692 | cue_tx_list_init(struct cue_softc *sc) |
693 | { |
694 | struct cue_cdata *cd; |
695 | struct cue_chain *c; |
696 | int i; |
697 | |
698 | cd = &sc->cue_cdata; |
699 | for (i = 0; i < CUE_TX_LIST_CNT; i++) { |
700 | c = &cd->cue_tx_chain[i]; |
701 | c->cue_sc = sc; |
702 | c->cue_idx = i; |
703 | c->cue_mbuf = NULL; |
704 | if (c->cue_xfer == NULL) { |
705 | int error = usbd_create_xfer(sc->cue_ep[CUE_ENDPT_TX], |
706 | CUE_BUFSZ, 0, 0, &c->cue_xfer); |
707 | if (error) |
708 | return error; |
709 | c->cue_buf = usbd_get_buffer(c->cue_xfer); |
710 | } |
711 | } |
712 | |
713 | return 0; |
714 | } |
715 | |
716 | /* |
717 | * A frame has been uploaded: pass the resulting mbuf chain up to |
718 | * the higher level protocols. |
719 | */ |
720 | Static void |
721 | cue_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status) |
722 | { |
723 | struct cue_chain *c = priv; |
724 | struct cue_softc *sc = c->cue_sc; |
725 | struct ifnet *ifp = GET_IFP(sc); |
726 | struct mbuf *m; |
727 | int total_len = 0; |
728 | uint16_t len; |
729 | int s; |
730 | |
731 | DPRINTFN(10,("%s: %s: enter status=%d\n" , device_xname(sc->cue_dev), |
732 | __func__, status)); |
733 | |
734 | if (sc->cue_dying) |
735 | return; |
736 | |
737 | if (!(ifp->if_flags & IFF_RUNNING)) |
738 | return; |
739 | |
740 | if (status != USBD_NORMAL_COMPLETION) { |
741 | if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) |
742 | return; |
743 | sc->cue_rx_errs++; |
744 | if (usbd_ratecheck(&sc->cue_rx_notice)) { |
745 | printf("%s: %u usb errors on rx: %s\n" , |
746 | device_xname(sc->cue_dev), sc->cue_rx_errs, |
747 | usbd_errstr(status)); |
748 | sc->cue_rx_errs = 0; |
749 | } |
750 | if (status == USBD_STALLED) |
751 | usbd_clear_endpoint_stall_async(sc->cue_ep[CUE_ENDPT_RX]); |
752 | goto done; |
753 | } |
754 | |
755 | usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); |
756 | |
757 | memcpy(mtod(c->cue_mbuf, char *), c->cue_buf, total_len); |
758 | |
759 | m = c->cue_mbuf; |
760 | len = UGETW(mtod(m, uint8_t *)); |
761 | |
762 | /* No errors; receive the packet. */ |
763 | total_len = len; |
764 | |
765 | if (len < sizeof(struct ether_header)) { |
766 | ifp->if_ierrors++; |
767 | goto done; |
768 | } |
769 | |
770 | ifp->if_ipackets++; |
771 | m_adj(m, sizeof(uint16_t)); |
772 | m->m_pkthdr.len = m->m_len = total_len; |
773 | |
774 | m_set_rcvif(m, ifp); |
775 | |
776 | s = splnet(); |
777 | |
778 | /* XXX ugly */ |
779 | if (cue_newbuf(sc, c, NULL) == ENOBUFS) { |
780 | ifp->if_ierrors++; |
781 | goto done1; |
782 | } |
783 | |
784 | /* |
785 | * Handle BPF listeners. Let the BPF user see the packet, but |
786 | * don't pass it up to the ether_input() layer unless it's |
787 | * a broadcast packet, multicast packet, matches our ethernet |
788 | * address or the interface is in promiscuous mode. |
789 | */ |
790 | bpf_mtap(ifp, m); |
791 | |
792 | DPRINTFN(10,("%s: %s: deliver %d\n" , device_xname(sc->cue_dev), |
793 | __func__, m->m_len)); |
794 | if_percpuq_enqueue(ifp->if_percpuq, m); |
795 | done1: |
796 | splx(s); |
797 | |
798 | done: |
799 | |
800 | /* Setup new transfer. */ |
801 | usbd_setup_xfer(c->cue_xfer, c, c->cue_buf, CUE_BUFSZ, |
802 | USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, cue_rxeof); |
803 | usbd_transfer(c->cue_xfer); |
804 | |
805 | DPRINTFN(10,("%s: %s: start rx\n" , device_xname(sc->cue_dev), |
806 | __func__)); |
807 | } |
808 | |
809 | /* |
810 | * A frame was downloaded to the chip. It's safe for us to clean up |
811 | * the list buffers. |
812 | */ |
813 | Static void |
814 | cue_txeof(struct usbd_xfer *xfer, void *priv, |
815 | usbd_status status) |
816 | { |
817 | struct cue_chain *c = priv; |
818 | struct cue_softc *sc = c->cue_sc; |
819 | struct ifnet *ifp = GET_IFP(sc); |
820 | int s; |
821 | |
822 | if (sc->cue_dying) |
823 | return; |
824 | |
825 | s = splnet(); |
826 | |
827 | DPRINTFN(10,("%s: %s: enter status=%d\n" , device_xname(sc->cue_dev), |
828 | __func__, status)); |
829 | |
830 | ifp->if_timer = 0; |
831 | ifp->if_flags &= ~IFF_OACTIVE; |
832 | |
833 | if (status != USBD_NORMAL_COMPLETION) { |
834 | if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { |
835 | splx(s); |
836 | return; |
837 | } |
838 | ifp->if_oerrors++; |
839 | printf("%s: usb error on tx: %s\n" , device_xname(sc->cue_dev), |
840 | usbd_errstr(status)); |
841 | if (status == USBD_STALLED) |
842 | usbd_clear_endpoint_stall_async(sc->cue_ep[CUE_ENDPT_TX]); |
843 | splx(s); |
844 | return; |
845 | } |
846 | |
847 | ifp->if_opackets++; |
848 | |
849 | m_freem(c->cue_mbuf); |
850 | c->cue_mbuf = NULL; |
851 | |
852 | if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) |
853 | cue_start(ifp); |
854 | |
855 | splx(s); |
856 | } |
857 | |
858 | Static void |
859 | cue_tick(void *xsc) |
860 | { |
861 | struct cue_softc *sc = xsc; |
862 | |
863 | if (sc == NULL) |
864 | return; |
865 | |
866 | if (sc->cue_dying) |
867 | return; |
868 | |
869 | DPRINTFN(2,("%s: %s: enter\n" , device_xname(sc->cue_dev), __func__)); |
870 | |
871 | /* Perform statistics update in process context. */ |
872 | usb_add_task(sc->cue_udev, &sc->cue_tick_task, USB_TASKQ_DRIVER); |
873 | } |
874 | |
875 | Static void |
876 | cue_tick_task(void *xsc) |
877 | { |
878 | struct cue_softc *sc = xsc; |
879 | struct ifnet *ifp; |
880 | |
881 | if (sc->cue_dying) |
882 | return; |
883 | |
884 | DPRINTFN(2,("%s: %s: enter\n" , device_xname(sc->cue_dev), __func__)); |
885 | |
886 | ifp = GET_IFP(sc); |
887 | |
888 | ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_SINGLECOLL); |
889 | ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_MULTICOLL); |
890 | ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_EXCESSCOLL); |
891 | |
892 | if (cue_csr_read_2(sc, CUE_RX_FRAMEERR)) |
893 | ifp->if_ierrors++; |
894 | } |
895 | |
896 | Static int |
897 | cue_send(struct cue_softc *sc, struct mbuf *m, int idx) |
898 | { |
899 | int total_len; |
900 | struct cue_chain *c; |
901 | usbd_status err; |
902 | |
903 | c = &sc->cue_cdata.cue_tx_chain[idx]; |
904 | |
905 | /* |
906 | * Copy the mbuf data into a contiguous buffer, leaving two |
907 | * bytes at the beginning to hold the frame length. |
908 | */ |
909 | m_copydata(m, 0, m->m_pkthdr.len, c->cue_buf + 2); |
910 | c->cue_mbuf = m; |
911 | |
912 | total_len = m->m_pkthdr.len + 2; |
913 | |
914 | DPRINTFN(10,("%s: %s: total_len=%d\n" , |
915 | device_xname(sc->cue_dev), __func__, total_len)); |
916 | |
917 | /* The first two bytes are the frame length */ |
918 | c->cue_buf[0] = (uint8_t)m->m_pkthdr.len; |
919 | c->cue_buf[1] = (uint8_t)(m->m_pkthdr.len >> 8); |
920 | |
921 | /* XXX 10000 */ |
922 | usbd_setup_xfer(c->cue_xfer, c, c->cue_buf, total_len, 0, 10000, |
923 | cue_txeof); |
924 | |
925 | /* Transmit */ |
926 | err = usbd_transfer(c->cue_xfer); |
927 | if (err != USBD_IN_PROGRESS) { |
928 | printf("%s: cue_send error=%s\n" , device_xname(sc->cue_dev), |
929 | usbd_errstr(err)); |
930 | /* Stop the interface from process context. */ |
931 | usb_add_task(sc->cue_udev, &sc->cue_stop_task, |
932 | USB_TASKQ_DRIVER); |
933 | return EIO; |
934 | } |
935 | |
936 | sc->cue_cdata.cue_tx_cnt++; |
937 | |
938 | return 0; |
939 | } |
940 | |
941 | Static void |
942 | cue_start(struct ifnet *ifp) |
943 | { |
944 | struct cue_softc *sc = ifp->if_softc; |
945 | struct mbuf *m_head = NULL; |
946 | |
947 | if (sc->cue_dying) |
948 | return; |
949 | |
950 | DPRINTFN(10,("%s: %s: enter\n" , device_xname(sc->cue_dev),__func__)); |
951 | |
952 | if (ifp->if_flags & IFF_OACTIVE) |
953 | return; |
954 | |
955 | IFQ_POLL(&ifp->if_snd, m_head); |
956 | if (m_head == NULL) |
957 | return; |
958 | |
959 | if (cue_send(sc, m_head, 0)) { |
960 | ifp->if_flags |= IFF_OACTIVE; |
961 | return; |
962 | } |
963 | |
964 | IFQ_DEQUEUE(&ifp->if_snd, m_head); |
965 | |
966 | /* |
967 | * If there's a BPF listener, bounce a copy of this frame |
968 | * to him. |
969 | */ |
970 | bpf_mtap(ifp, m_head); |
971 | |
972 | ifp->if_flags |= IFF_OACTIVE; |
973 | |
974 | /* |
975 | * Set a timeout in case the chip goes out to lunch. |
976 | */ |
977 | ifp->if_timer = 5; |
978 | } |
979 | |
980 | Static void |
981 | cue_init(void *xsc) |
982 | { |
983 | struct cue_softc *sc = xsc; |
984 | struct ifnet *ifp = GET_IFP(sc); |
985 | int i, s, ctl; |
986 | const u_char *eaddr; |
987 | |
988 | if (sc->cue_dying) |
989 | return; |
990 | |
991 | DPRINTFN(10,("%s: %s: enter\n" , device_xname(sc->cue_dev),__func__)); |
992 | |
993 | if (ifp->if_flags & IFF_RUNNING) |
994 | return; |
995 | |
996 | s = splnet(); |
997 | |
998 | /* |
999 | * Cancel pending I/O and free all RX/TX buffers. |
1000 | */ |
1001 | #if 1 |
1002 | cue_reset(sc); |
1003 | #endif |
1004 | |
1005 | /* Set advanced operation modes. */ |
1006 | cue_csr_write_1(sc, CUE_ADVANCED_OPMODES, |
1007 | CUE_AOP_EMBED_RXLEN | 0x03); /* 1 wait state */ |
1008 | |
1009 | eaddr = CLLADDR(ifp->if_sadl); |
1010 | /* Set MAC address */ |
1011 | for (i = 0; i < ETHER_ADDR_LEN; i++) |
1012 | cue_csr_write_1(sc, CUE_PAR0 - i, eaddr[i]); |
1013 | |
1014 | /* Enable RX logic. */ |
1015 | ctl = CUE_ETHCTL_RX_ON | CUE_ETHCTL_MCAST_ON; |
1016 | if (ifp->if_flags & IFF_PROMISC) |
1017 | ctl |= CUE_ETHCTL_PROMISC; |
1018 | cue_csr_write_1(sc, CUE_ETHCTL, ctl); |
1019 | |
1020 | /* Load the multicast filter. */ |
1021 | cue_setmulti(sc); |
1022 | |
1023 | /* |
1024 | * Set the number of RX and TX buffers that we want |
1025 | * to reserve inside the ASIC. |
1026 | */ |
1027 | cue_csr_write_1(sc, CUE_RX_BUFPKTS, CUE_RX_FRAMES); |
1028 | cue_csr_write_1(sc, CUE_TX_BUFPKTS, CUE_TX_FRAMES); |
1029 | |
1030 | /* Set advanced operation modes. */ |
1031 | cue_csr_write_1(sc, CUE_ADVANCED_OPMODES, |
1032 | CUE_AOP_EMBED_RXLEN | 0x01); /* 1 wait state */ |
1033 | |
1034 | /* Program the LED operation. */ |
1035 | cue_csr_write_1(sc, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK); |
1036 | |
1037 | if (sc->cue_ep[CUE_ENDPT_RX] == NULL) { |
1038 | if (cue_open_pipes(sc)) { |
1039 | splx(s); |
1040 | return; |
1041 | } |
1042 | } |
1043 | /* Init TX ring. */ |
1044 | if (cue_tx_list_init(sc)) { |
1045 | printf("%s: tx list init failed\n" , device_xname(sc->cue_dev)); |
1046 | splx(s); |
1047 | return; |
1048 | } |
1049 | |
1050 | /* Init RX ring. */ |
1051 | if (cue_rx_list_init(sc)) { |
1052 | printf("%s: rx list init failed\n" , device_xname(sc->cue_dev)); |
1053 | splx(s); |
1054 | return; |
1055 | } |
1056 | |
1057 | |
1058 | ifp->if_flags |= IFF_RUNNING; |
1059 | ifp->if_flags &= ~IFF_OACTIVE; |
1060 | |
1061 | splx(s); |
1062 | |
1063 | callout_reset(&(sc->cue_stat_ch), (hz), (cue_tick), (sc)); |
1064 | } |
1065 | |
1066 | Static int |
1067 | cue_open_pipes(struct cue_softc *sc) |
1068 | { |
1069 | struct cue_chain *c; |
1070 | usbd_status err; |
1071 | int i; |
1072 | |
1073 | /* Open RX and TX pipes. */ |
1074 | err = usbd_open_pipe(sc->cue_iface, sc->cue_ed[CUE_ENDPT_RX], |
1075 | USBD_EXCLUSIVE_USE, &sc->cue_ep[CUE_ENDPT_RX]); |
1076 | if (err) { |
1077 | printf("%s: open rx pipe failed: %s\n" , |
1078 | device_xname(sc->cue_dev), usbd_errstr(err)); |
1079 | return EIO; |
1080 | } |
1081 | err = usbd_open_pipe(sc->cue_iface, sc->cue_ed[CUE_ENDPT_TX], |
1082 | USBD_EXCLUSIVE_USE, &sc->cue_ep[CUE_ENDPT_TX]); |
1083 | if (err) { |
1084 | printf("%s: open tx pipe failed: %s\n" , |
1085 | device_xname(sc->cue_dev), usbd_errstr(err)); |
1086 | return EIO; |
1087 | } |
1088 | |
1089 | /* Start up the receive pipe. */ |
1090 | for (i = 0; i < CUE_RX_LIST_CNT; i++) { |
1091 | c = &sc->cue_cdata.cue_rx_chain[i]; |
1092 | |
1093 | usbd_setup_xfer(c->cue_xfer, c, c->cue_buf, CUE_BUFSZ, |
1094 | USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, cue_rxeof); |
1095 | usbd_transfer(c->cue_xfer); |
1096 | } |
1097 | |
1098 | return 0; |
1099 | } |
1100 | |
1101 | Static int |
1102 | cue_ioctl(struct ifnet *ifp, u_long command, void *data) |
1103 | { |
1104 | struct cue_softc *sc = ifp->if_softc; |
1105 | struct ifaddr *ifa = (struct ifaddr *)data; |
1106 | struct ifreq *ifr = (struct ifreq *)data; |
1107 | int s, error = 0; |
1108 | |
1109 | if (sc->cue_dying) |
1110 | return EIO; |
1111 | |
1112 | s = splnet(); |
1113 | |
1114 | switch(command) { |
1115 | case SIOCINITIFADDR: |
1116 | ifp->if_flags |= IFF_UP; |
1117 | cue_init(sc); |
1118 | |
1119 | switch (ifa->ifa_addr->sa_family) { |
1120 | #ifdef INET |
1121 | case AF_INET: |
1122 | arp_ifinit(ifp, ifa); |
1123 | break; |
1124 | #endif /* INET */ |
1125 | } |
1126 | break; |
1127 | |
1128 | case SIOCSIFMTU: |
1129 | if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > ETHERMTU) |
1130 | error = EINVAL; |
1131 | else if ((error = ifioctl_common(ifp, command, data)) == ENETRESET) |
1132 | error = 0; |
1133 | break; |
1134 | |
1135 | case SIOCSIFFLAGS: |
1136 | if ((error = ifioctl_common(ifp, command, data)) != 0) |
1137 | break; |
1138 | if (ifp->if_flags & IFF_UP) { |
1139 | if (ifp->if_flags & IFF_RUNNING && |
1140 | ifp->if_flags & IFF_PROMISC && |
1141 | !(sc->cue_if_flags & IFF_PROMISC)) { |
1142 | CUE_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); |
1143 | cue_setmulti(sc); |
1144 | } else if (ifp->if_flags & IFF_RUNNING && |
1145 | !(ifp->if_flags & IFF_PROMISC) && |
1146 | sc->cue_if_flags & IFF_PROMISC) { |
1147 | CUE_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); |
1148 | cue_setmulti(sc); |
1149 | } else if (!(ifp->if_flags & IFF_RUNNING)) |
1150 | cue_init(sc); |
1151 | } else { |
1152 | if (ifp->if_flags & IFF_RUNNING) |
1153 | cue_stop(sc); |
1154 | } |
1155 | sc->cue_if_flags = ifp->if_flags; |
1156 | error = 0; |
1157 | break; |
1158 | case SIOCADDMULTI: |
1159 | case SIOCDELMULTI: |
1160 | cue_setmulti(sc); |
1161 | error = 0; |
1162 | break; |
1163 | default: |
1164 | error = ether_ioctl(ifp, command, data); |
1165 | break; |
1166 | } |
1167 | |
1168 | splx(s); |
1169 | |
1170 | return error; |
1171 | } |
1172 | |
1173 | Static void |
1174 | cue_watchdog(struct ifnet *ifp) |
1175 | { |
1176 | struct cue_softc *sc = ifp->if_softc; |
1177 | struct cue_chain *c; |
1178 | usbd_status stat; |
1179 | int s; |
1180 | |
1181 | DPRINTFN(5,("%s: %s: enter\n" , device_xname(sc->cue_dev), __func__)); |
1182 | |
1183 | if (sc->cue_dying) |
1184 | return; |
1185 | |
1186 | ifp->if_oerrors++; |
1187 | printf("%s: watchdog timeout\n" , device_xname(sc->cue_dev)); |
1188 | |
1189 | s = splusb(); |
1190 | c = &sc->cue_cdata.cue_tx_chain[0]; |
1191 | usbd_get_xfer_status(c->cue_xfer, NULL, NULL, NULL, &stat); |
1192 | cue_txeof(c->cue_xfer, c, stat); |
1193 | |
1194 | if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) |
1195 | cue_start(ifp); |
1196 | splx(s); |
1197 | } |
1198 | |
1199 | /* |
1200 | * Stop the adapter and free any mbufs allocated to the |
1201 | * RX and TX lists. |
1202 | */ |
1203 | Static void |
1204 | cue_stop(struct cue_softc *sc) |
1205 | { |
1206 | usbd_status err; |
1207 | struct ifnet *ifp; |
1208 | int i; |
1209 | |
1210 | DPRINTFN(10,("%s: %s: enter\n" , device_xname(sc->cue_dev),__func__)); |
1211 | |
1212 | ifp = GET_IFP(sc); |
1213 | ifp->if_timer = 0; |
1214 | |
1215 | cue_csr_write_1(sc, CUE_ETHCTL, 0); |
1216 | cue_reset(sc); |
1217 | callout_stop(&sc->cue_stat_ch); |
1218 | |
1219 | /* Stop transfers. */ |
1220 | if (sc->cue_ep[CUE_ENDPT_RX] != NULL) { |
1221 | err = usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_RX]); |
1222 | if (err) { |
1223 | printf("%s: abort rx pipe failed: %s\n" , |
1224 | device_xname(sc->cue_dev), usbd_errstr(err)); |
1225 | } |
1226 | } |
1227 | |
1228 | if (sc->cue_ep[CUE_ENDPT_TX] != NULL) { |
1229 | err = usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_TX]); |
1230 | if (err) { |
1231 | printf("%s: abort tx pipe failed: %s\n" , |
1232 | device_xname(sc->cue_dev), usbd_errstr(err)); |
1233 | } |
1234 | } |
1235 | |
1236 | if (sc->cue_ep[CUE_ENDPT_INTR] != NULL) { |
1237 | err = usbd_abort_pipe(sc->cue_ep[CUE_ENDPT_INTR]); |
1238 | if (err) { |
1239 | printf("%s: abort intr pipe failed: %s\n" , |
1240 | device_xname(sc->cue_dev), usbd_errstr(err)); |
1241 | } |
1242 | } |
1243 | |
1244 | /* Free RX resources. */ |
1245 | for (i = 0; i < CUE_RX_LIST_CNT; i++) { |
1246 | if (sc->cue_cdata.cue_rx_chain[i].cue_xfer != NULL) { |
1247 | usbd_destroy_xfer(sc->cue_cdata.cue_rx_chain[i].cue_xfer); |
1248 | sc->cue_cdata.cue_rx_chain[i].cue_xfer = NULL; |
1249 | } |
1250 | } |
1251 | |
1252 | /* Free TX resources. */ |
1253 | for (i = 0; i < CUE_TX_LIST_CNT; i++) { |
1254 | if (sc->cue_cdata.cue_tx_chain[i].cue_mbuf != NULL) { |
1255 | m_freem(sc->cue_cdata.cue_tx_chain[i].cue_mbuf); |
1256 | sc->cue_cdata.cue_tx_chain[i].cue_mbuf = NULL; |
1257 | } |
1258 | if (sc->cue_cdata.cue_tx_chain[i].cue_xfer != NULL) { |
1259 | usbd_destroy_xfer(sc->cue_cdata.cue_tx_chain[i].cue_xfer); |
1260 | sc->cue_cdata.cue_tx_chain[i].cue_xfer = NULL; |
1261 | } |
1262 | } |
1263 | |
1264 | /* Stop transfers. */ |
1265 | if (sc->cue_ep[CUE_ENDPT_RX] != NULL) { |
1266 | err = usbd_close_pipe(sc->cue_ep[CUE_ENDPT_RX]); |
1267 | if (err) { |
1268 | printf("%s: close rx pipe failed: %s\n" , |
1269 | device_xname(sc->cue_dev), usbd_errstr(err)); |
1270 | } |
1271 | sc->cue_ep[CUE_ENDPT_RX] = NULL; |
1272 | } |
1273 | |
1274 | if (sc->cue_ep[CUE_ENDPT_TX] != NULL) { |
1275 | err = usbd_close_pipe(sc->cue_ep[CUE_ENDPT_TX]); |
1276 | if (err) { |
1277 | printf("%s: close tx pipe failed: %s\n" , |
1278 | device_xname(sc->cue_dev), usbd_errstr(err)); |
1279 | } |
1280 | sc->cue_ep[CUE_ENDPT_TX] = NULL; |
1281 | } |
1282 | |
1283 | if (sc->cue_ep[CUE_ENDPT_INTR] != NULL) { |
1284 | err = usbd_close_pipe(sc->cue_ep[CUE_ENDPT_INTR]); |
1285 | if (err) { |
1286 | printf("%s: close intr pipe failed: %s\n" , |
1287 | device_xname(sc->cue_dev), usbd_errstr(err)); |
1288 | } |
1289 | sc->cue_ep[CUE_ENDPT_INTR] = NULL; |
1290 | } |
1291 | |
1292 | ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); |
1293 | } |
1294 | |