Line data Source code
1 : /* $OpenBSD: if_cdce.c,v 1.74 2017/04/21 09:42:53 mpi Exp $ */
2 :
3 : /*
4 : * Copyright (c) 1997, 1998, 1999, 2000-2003 Bill Paul <wpaul@windriver.com>
5 : * Copyright (c) 2003 Craig Boston
6 : * Copyright (c) 2004 Daniel Hartmeier
7 : * All rights reserved.
8 : *
9 : * Redistribution and use in source and binary forms, with or without
10 : * modification, are permitted provided that the following conditions
11 : * are met:
12 : * 1. Redistributions of source code must retain the above copyright
13 : * notice, this list of conditions and the following disclaimer.
14 : * 2. Redistributions in binary form must reproduce the above copyright
15 : * notice, this list of conditions and the following disclaimer in the
16 : * documentation and/or other materials provided with the distribution.
17 : * 3. All advertising materials mentioning features or use of this software
18 : * must display the following acknowledgement:
19 : * This product includes software developed by Bill Paul.
20 : * 4. Neither the name of the author nor the names of any co-contributors
21 : * may be used to endorse or promote products derived from this software
22 : * without specific prior written permission.
23 : *
24 : * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
25 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 : * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul, THE VOICES IN HIS HEAD OR
28 : * THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
31 : * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
32 : * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
33 : * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
34 : * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 : */
36 :
37 : /*
38 : * USB Communication Device Class (Ethernet Networking Control Model)
39 : * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
40 : *
41 : */
42 :
43 : #include <bpfilter.h>
44 :
45 : #include <sys/param.h>
46 : #include <sys/systm.h>
47 : #include <sys/sockio.h>
48 : #include <sys/mbuf.h>
49 : #include <sys/kernel.h>
50 : #include <sys/socket.h>
51 : #include <sys/device.h>
52 :
53 : #include <net/if.h>
54 :
55 : #if NBPFILTER > 0
56 : #include <net/bpf.h>
57 : #endif
58 :
59 : #include <netinet/in.h>
60 : #include <netinet/if_ether.h>
61 :
62 : #include <dev/usb/usb.h>
63 : #include <dev/usb/usbdi.h>
64 : #include <dev/usb/usbdi_util.h>
65 : #include <dev/usb/usbdevs.h>
66 : #include <dev/usb/usbcdc.h>
67 :
68 : #include <dev/usb/if_cdcereg.h>
69 :
70 : #ifdef CDCE_DEBUG
71 : #define DPRINTFN(n, x) do { if (cdcedebug > (n)) printf x; } while (0)
72 : int cdcedebug = 0;
73 : #else
74 : #define DPRINTFN(n, x)
75 : #endif
76 : #define DPRINTF(x) DPRINTFN(0, x)
77 :
78 : int cdce_tx_list_init(struct cdce_softc *);
79 : int cdce_rx_list_init(struct cdce_softc *);
80 : int cdce_newbuf(struct cdce_softc *, struct cdce_chain *,
81 : struct mbuf *);
82 : int cdce_encap(struct cdce_softc *, struct mbuf *, int);
83 : void cdce_rxeof(struct usbd_xfer *, void *, usbd_status);
84 : void cdce_txeof(struct usbd_xfer *, void *, usbd_status);
85 : void cdce_start(struct ifnet *);
86 : int cdce_ioctl(struct ifnet *, u_long, caddr_t);
87 : void cdce_init(void *);
88 : void cdce_watchdog(struct ifnet *);
89 : void cdce_stop(struct cdce_softc *);
90 : void cdce_intr(struct usbd_xfer *, void *, usbd_status);
91 :
92 : const struct cdce_type cdce_devs[] = {
93 : {{ USB_VENDOR_ACERLABS, USB_PRODUCT_ACERLABS_M5632 }, 0 },
94 : {{ USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2501 }, 0 },
95 : {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5500 }, CDCE_CRC32 },
96 : {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_A300 }, CDCE_CRC32 },
97 : {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5600 }, CDCE_CRC32 },
98 : {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_C700 }, CDCE_CRC32 },
99 : {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_C750 }, CDCE_CRC32 },
100 : {{ USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN }, CDCE_CRC32 },
101 : {{ USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN2 }, CDCE_CRC32 },
102 : {{ USB_VENDOR_GMATE, USB_PRODUCT_GMATE_YP3X00 }, 0 },
103 : {{ USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_ETHERNETGADGET }, 0 },
104 : {{ USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQLINUX }, 0 },
105 : {{ USB_VENDOR_AMBIT, USB_PRODUCT_AMBIT_NTL_250 }, CDCE_SWAPUNION },
106 : };
107 : #define cdce_lookup(v, p) \
108 : ((const struct cdce_type *)usb_lookup(cdce_devs, v, p))
109 :
110 : int cdce_match(struct device *, void *, void *);
111 : void cdce_attach(struct device *, struct device *, void *);
112 : int cdce_detach(struct device *, int);
113 :
114 : struct cfdriver cdce_cd = {
115 : NULL, "cdce", DV_IFNET
116 : };
117 :
118 : const struct cfattach cdce_ca = {
119 : sizeof(struct cdce_softc), cdce_match, cdce_attach, cdce_detach
120 : };
121 :
122 : int
123 0 : cdce_match(struct device *parent, void *match, void *aux)
124 : {
125 0 : struct usb_attach_arg *uaa = aux;
126 : usb_interface_descriptor_t *id;
127 :
128 0 : if (uaa->iface == NULL)
129 0 : return (UMATCH_NONE);
130 :
131 0 : id = usbd_get_interface_descriptor(uaa->iface);
132 0 : if (id == NULL)
133 0 : return (UMATCH_NONE);
134 :
135 0 : if (cdce_lookup(uaa->vendor, uaa->product) != NULL)
136 0 : return (UMATCH_VENDOR_PRODUCT);
137 :
138 0 : if (id->bInterfaceClass == UICLASS_CDC &&
139 0 : (id->bInterfaceSubClass ==
140 0 : UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL ||
141 0 : id->bInterfaceSubClass == UISUBCLASS_MOBILE_DIRECT_LINE_MODEL))
142 0 : return (UMATCH_IFACECLASS_GENERIC);
143 :
144 0 : return (UMATCH_NONE);
145 0 : }
146 :
147 : void
148 0 : cdce_attach(struct device *parent, struct device *self, void *aux)
149 : {
150 0 : struct cdce_softc *sc = (struct cdce_softc *)self;
151 0 : struct usb_attach_arg *uaa = aux;
152 : int s;
153 0 : struct ifnet *ifp = GET_IFP(sc);
154 0 : struct usbd_device *dev = uaa->device;
155 : const struct cdce_type *t;
156 : usb_interface_descriptor_t *id;
157 : usb_endpoint_descriptor_t *ed;
158 : struct usb_cdc_union_descriptor *ud;
159 : struct usb_cdc_ethernet_descriptor *ethd;
160 : usb_config_descriptor_t *cd;
161 : const usb_descriptor_t *desc;
162 0 : struct usbd_desc_iter iter;
163 0 : usb_string_descriptor_t eaddr_str;
164 0 : int i, j, numalts, len;
165 : int ctl_ifcno = -1;
166 : int data_ifcno = -1;
167 :
168 0 : sc->cdce_udev = uaa->device;
169 0 : sc->cdce_ctl_iface = uaa->iface;
170 0 : id = usbd_get_interface_descriptor(sc->cdce_ctl_iface);
171 0 : ctl_ifcno = id->bInterfaceNumber;
172 :
173 0 : t = cdce_lookup(uaa->vendor, uaa->product);
174 0 : if (t)
175 0 : sc->cdce_flags = t->cdce_flags;
176 :
177 : /* Get the data interface no. and capabilities */
178 : ethd = NULL;
179 0 : usbd_desc_iter_init(dev, &iter);
180 0 : desc = usbd_desc_iter_next(&iter);
181 0 : while (desc) {
182 0 : if (desc->bDescriptorType != UDESC_CS_INTERFACE) {
183 0 : desc = usbd_desc_iter_next(&iter);
184 0 : continue;
185 : }
186 0 : switch(desc->bDescriptorSubtype) {
187 : case UDESCSUB_CDC_UNION:
188 0 : ud = (struct usb_cdc_union_descriptor *)desc;
189 0 : if ((sc->cdce_flags & CDCE_SWAPUNION) == 0 &&
190 0 : ud->bMasterInterface == ctl_ifcno)
191 0 : data_ifcno = ud->bSlaveInterface[0];
192 0 : if ((sc->cdce_flags & CDCE_SWAPUNION) &&
193 0 : ud->bSlaveInterface[0] == ctl_ifcno)
194 0 : data_ifcno = ud->bMasterInterface;
195 : break;
196 : case UDESCSUB_CDC_ENF:
197 0 : if (ethd) {
198 0 : printf("%s: ", sc->cdce_dev.dv_xname);
199 0 : printf("extra ethernet descriptor\n");
200 0 : return;
201 : }
202 0 : ethd = (struct usb_cdc_ethernet_descriptor *)desc;
203 0 : break;
204 : }
205 0 : desc = usbd_desc_iter_next(&iter);
206 : }
207 :
208 0 : if (data_ifcno == -1) {
209 : DPRINTF(("cdce_attach: no union interface\n"));
210 0 : sc->cdce_data_iface = sc->cdce_ctl_iface;
211 0 : } else {
212 : DPRINTF(("cdce_attach: union interface: ctl=%d, data=%d\n",
213 : ctl_ifcno, data_ifcno));
214 0 : for (i = 0; i < uaa->nifaces; i++) {
215 0 : if (usbd_iface_claimed(sc->cdce_udev, i))
216 : continue;
217 0 : id = usbd_get_interface_descriptor(uaa->ifaces[i]);
218 0 : if (id != NULL && id->bInterfaceNumber == data_ifcno) {
219 0 : sc->cdce_data_iface = uaa->ifaces[i];
220 0 : usbd_claim_iface(sc->cdce_udev, i);
221 0 : }
222 : }
223 : }
224 :
225 0 : if (sc->cdce_data_iface == NULL) {
226 0 : printf("%s: no data interface\n", sc->cdce_dev.dv_xname);
227 0 : return;
228 : }
229 :
230 0 : id = usbd_get_interface_descriptor(sc->cdce_ctl_iface);
231 0 : sc->cdce_intr_no = -1;
232 0 : for (i = 0; i < id->bNumEndpoints && sc->cdce_intr_no == -1; i++) {
233 0 : ed = usbd_interface2endpoint_descriptor(sc->cdce_ctl_iface, i);
234 0 : if (!ed) {
235 0 : printf("%s: no descriptor for interrupt endpoint %d\n",
236 0 : sc->cdce_dev.dv_xname, i);
237 0 : return;
238 : }
239 0 : if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
240 0 : UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
241 0 : sc->cdce_intr_no = ed->bEndpointAddress;
242 0 : sc->cdce_intr_size = sizeof(sc->cdce_intr_buf);
243 0 : }
244 : }
245 :
246 0 : id = usbd_get_interface_descriptor(sc->cdce_data_iface);
247 0 : cd = usbd_get_config_descriptor(sc->cdce_udev);
248 0 : numalts = usbd_get_no_alts(cd, id->bInterfaceNumber);
249 :
250 0 : for (j = 0; j < numalts; j++) {
251 0 : if (usbd_set_interface(sc->cdce_data_iface, j)) {
252 0 : printf("%s: interface alternate setting %d failed\n",
253 0 : sc->cdce_dev.dv_xname, j);
254 0 : return;
255 : }
256 : /* Find endpoints. */
257 0 : id = usbd_get_interface_descriptor(sc->cdce_data_iface);
258 0 : sc->cdce_bulkin_no = sc->cdce_bulkout_no = -1;
259 0 : for (i = 0; i < id->bNumEndpoints; i++) {
260 0 : ed = usbd_interface2endpoint_descriptor(
261 0 : sc->cdce_data_iface, i);
262 0 : if (!ed) {
263 0 : printf("%s: no descriptor for bulk endpoint "
264 0 : "%d\n", sc->cdce_dev.dv_xname, i);
265 0 : return;
266 : }
267 0 : if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
268 0 : UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
269 0 : sc->cdce_bulkin_no = ed->bEndpointAddress;
270 0 : } else if (
271 0 : UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
272 0 : UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
273 0 : sc->cdce_bulkout_no = ed->bEndpointAddress;
274 0 : }
275 : #ifdef CDCE_DEBUG
276 : else if (
277 : UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN &&
278 : UE_GET_XFERTYPE(ed->bmAttributes) != UE_INTERRUPT) {
279 : printf("%s: unexpected endpoint, ep=%x attr=%x"
280 : "\n", sc->cdce_dev.dv_xname,
281 : ed->bEndpointAddress, ed->bmAttributes);
282 : }
283 : #endif
284 : }
285 0 : if ((sc->cdce_bulkin_no != -1) && (sc->cdce_bulkout_no != -1)) {
286 : DPRINTF(("cdce_attach: intr=0x%x, in=0x%x, out=0x%x\n",
287 : sc->cdce_intr_no, sc->cdce_bulkin_no,
288 : sc->cdce_bulkout_no));
289 : goto found;
290 : }
291 : }
292 :
293 0 : if (sc->cdce_bulkin_no == -1) {
294 0 : printf("%s: could not find data bulk in\n",
295 0 : sc->cdce_dev.dv_xname);
296 0 : return;
297 : }
298 0 : if (sc->cdce_bulkout_no == -1 ) {
299 0 : printf("%s: could not find data bulk out\n",
300 0 : sc->cdce_dev.dv_xname);
301 0 : return;
302 : }
303 :
304 : found:
305 0 : s = splnet();
306 :
307 0 : if (!ethd || usbd_get_string_desc(sc->cdce_udev, ethd->iMacAddress, 0,
308 : &eaddr_str, &len)) {
309 0 : ether_fakeaddr(ifp);
310 0 : } else {
311 0 : for (i = 0; i < ETHER_ADDR_LEN * 2; i++) {
312 0 : int c = UGETW(eaddr_str.bString[i]);
313 :
314 0 : if ('0' <= c && c <= '9')
315 0 : c -= '0';
316 0 : else if ('A' <= c && c <= 'F')
317 0 : c -= 'A' - 10;
318 0 : else if ('a' <= c && c <= 'f')
319 0 : c -= 'a' - 10;
320 0 : c &= 0xf;
321 0 : if (i % 2 == 0)
322 0 : c <<= 4;
323 0 : sc->cdce_arpcom.ac_enaddr[i / 2] |= c;
324 : }
325 : }
326 :
327 0 : printf("%s: address %s\n", sc->cdce_dev.dv_xname,
328 0 : ether_sprintf(sc->cdce_arpcom.ac_enaddr));
329 :
330 0 : ifp->if_softc = sc;
331 0 : ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
332 0 : ifp->if_ioctl = cdce_ioctl;
333 0 : ifp->if_start = cdce_start;
334 0 : ifp->if_watchdog = cdce_watchdog;
335 0 : strlcpy(ifp->if_xname, sc->cdce_dev.dv_xname, IFNAMSIZ);
336 :
337 0 : if_attach(ifp);
338 0 : ether_ifattach(ifp);
339 :
340 0 : sc->cdce_attached = 1;
341 0 : splx(s);
342 0 : }
343 :
344 : int
345 0 : cdce_detach(struct device *self, int flags)
346 : {
347 0 : struct cdce_softc *sc = (struct cdce_softc *)self;
348 0 : struct ifnet *ifp = GET_IFP(sc);
349 : int s;
350 :
351 0 : if (!sc->cdce_attached)
352 0 : return (0);
353 :
354 0 : s = splusb();
355 :
356 0 : if (ifp->if_flags & IFF_RUNNING)
357 0 : cdce_stop(sc);
358 :
359 0 : if (ifp->if_softc != NULL) {
360 0 : ether_ifdetach(ifp);
361 0 : if_detach(ifp);
362 0 : }
363 :
364 0 : sc->cdce_attached = 0;
365 0 : splx(s);
366 :
367 0 : return (0);
368 0 : }
369 :
370 : void
371 0 : cdce_start(struct ifnet *ifp)
372 : {
373 0 : struct cdce_softc *sc = ifp->if_softc;
374 : struct mbuf *m_head = NULL;
375 :
376 0 : if (usbd_is_dying(sc->cdce_udev) || ifq_is_oactive(&ifp->if_snd))
377 0 : return;
378 :
379 0 : m_head = ifq_deq_begin(&ifp->if_snd);
380 0 : if (m_head == NULL)
381 0 : return;
382 :
383 0 : if (cdce_encap(sc, m_head, 0)) {
384 0 : ifq_deq_rollback(&ifp->if_snd, m_head);
385 0 : ifq_set_oactive(&ifp->if_snd);
386 0 : return;
387 : }
388 :
389 0 : ifq_deq_commit(&ifp->if_snd, m_head);
390 :
391 : #if NBPFILTER > 0
392 0 : if (ifp->if_bpf)
393 0 : bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT);
394 : #endif
395 :
396 0 : ifq_set_oactive(&ifp->if_snd);
397 :
398 0 : ifp->if_timer = 6;
399 0 : }
400 :
401 : int
402 0 : cdce_encap(struct cdce_softc *sc, struct mbuf *m, int idx)
403 : {
404 : struct cdce_chain *c;
405 : usbd_status err;
406 : int extra = 0;
407 :
408 0 : c = &sc->cdce_cdata.cdce_tx_chain[idx];
409 :
410 0 : m_copydata(m, 0, m->m_pkthdr.len, c->cdce_buf);
411 0 : if (sc->cdce_flags & CDCE_CRC32) {
412 : /* Some devices want a 32-bit CRC appended to every frame */
413 0 : u_int32_t crc;
414 :
415 0 : crc = ether_crc32_le(c->cdce_buf, m->m_pkthdr.len) ^ ~0U;
416 0 : bcopy(&crc, c->cdce_buf + m->m_pkthdr.len, 4);
417 : extra = 4;
418 0 : }
419 0 : c->cdce_mbuf = m;
420 :
421 0 : usbd_setup_xfer(c->cdce_xfer, sc->cdce_bulkout_pipe, c, c->cdce_buf,
422 0 : m->m_pkthdr.len + extra, USBD_FORCE_SHORT_XFER | USBD_NO_COPY,
423 : 10000, cdce_txeof);
424 0 : err = usbd_transfer(c->cdce_xfer);
425 0 : if (err != USBD_IN_PROGRESS) {
426 0 : cdce_stop(sc);
427 0 : return (EIO);
428 : }
429 :
430 0 : sc->cdce_cdata.cdce_tx_cnt++;
431 :
432 0 : return (0);
433 0 : }
434 :
435 : void
436 0 : cdce_stop(struct cdce_softc *sc)
437 : {
438 : usbd_status err;
439 0 : struct ifnet *ifp = GET_IFP(sc);
440 : int i;
441 :
442 0 : ifp->if_timer = 0;
443 0 : ifp->if_flags &= ~IFF_RUNNING;
444 0 : ifq_clr_oactive(&ifp->if_snd);
445 :
446 0 : if (sc->cdce_bulkin_pipe != NULL) {
447 0 : usbd_abort_pipe(sc->cdce_bulkin_pipe);
448 0 : err = usbd_close_pipe(sc->cdce_bulkin_pipe);
449 0 : if (err)
450 0 : printf("%s: close rx pipe failed: %s\n",
451 0 : sc->cdce_dev.dv_xname, usbd_errstr(err));
452 0 : sc->cdce_bulkin_pipe = NULL;
453 0 : }
454 :
455 0 : if (sc->cdce_bulkout_pipe != NULL) {
456 0 : usbd_abort_pipe(sc->cdce_bulkout_pipe);
457 0 : err = usbd_close_pipe(sc->cdce_bulkout_pipe);
458 0 : if (err)
459 0 : printf("%s: close tx pipe failed: %s\n",
460 0 : sc->cdce_dev.dv_xname, usbd_errstr(err));
461 0 : sc->cdce_bulkout_pipe = NULL;
462 0 : }
463 :
464 0 : if (sc->cdce_intr_pipe != NULL) {
465 0 : usbd_abort_pipe(sc->cdce_intr_pipe);
466 0 : err = usbd_close_pipe(sc->cdce_intr_pipe);
467 0 : if (err)
468 0 : printf("%s: close interrupt pipe failed: %s\n",
469 0 : sc->cdce_dev.dv_xname, usbd_errstr(err));
470 0 : sc->cdce_intr_pipe = NULL;
471 0 : }
472 :
473 0 : for (i = 0; i < CDCE_RX_LIST_CNT; i++) {
474 0 : if (sc->cdce_cdata.cdce_rx_chain[i].cdce_mbuf != NULL) {
475 0 : m_freem(sc->cdce_cdata.cdce_rx_chain[i].cdce_mbuf);
476 0 : sc->cdce_cdata.cdce_rx_chain[i].cdce_mbuf = NULL;
477 0 : }
478 0 : if (sc->cdce_cdata.cdce_rx_chain[i].cdce_xfer != NULL) {
479 0 : usbd_free_xfer(sc->cdce_cdata.cdce_rx_chain[i].cdce_xfer);
480 0 : sc->cdce_cdata.cdce_rx_chain[i].cdce_xfer = NULL;
481 0 : }
482 : }
483 :
484 0 : for (i = 0; i < CDCE_TX_LIST_CNT; i++) {
485 0 : if (sc->cdce_cdata.cdce_tx_chain[i].cdce_mbuf != NULL) {
486 0 : m_freem(sc->cdce_cdata.cdce_tx_chain[i].cdce_mbuf);
487 0 : sc->cdce_cdata.cdce_tx_chain[i].cdce_mbuf = NULL;
488 0 : }
489 0 : if (sc->cdce_cdata.cdce_tx_chain[i].cdce_xfer != NULL) {
490 0 : usbd_free_xfer(
491 : sc->cdce_cdata.cdce_tx_chain[i].cdce_xfer);
492 0 : sc->cdce_cdata.cdce_tx_chain[i].cdce_xfer = NULL;
493 0 : }
494 : }
495 0 : }
496 :
497 : int
498 0 : cdce_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
499 : {
500 0 : struct cdce_softc *sc = ifp->if_softc;
501 : int s, error = 0;
502 :
503 0 : if (usbd_is_dying(sc->cdce_udev))
504 0 : return (EIO);
505 :
506 0 : s = splnet();
507 :
508 0 : switch(command) {
509 : case SIOCSIFADDR:
510 0 : ifp->if_flags |= IFF_UP;
511 0 : if (!(ifp->if_flags & IFF_RUNNING))
512 0 : cdce_init(sc);
513 : break;
514 :
515 : case SIOCSIFFLAGS:
516 0 : if (ifp->if_flags & IFF_UP) {
517 0 : if (ifp->if_flags & IFF_RUNNING)
518 0 : error = ENETRESET;
519 : else
520 0 : cdce_init(sc);
521 : } else {
522 0 : if (ifp->if_flags & IFF_RUNNING)
523 0 : cdce_stop(sc);
524 : }
525 : break;
526 :
527 : default:
528 0 : error = ether_ioctl(ifp, &sc->cdce_arpcom, command, data);
529 0 : break;
530 : }
531 :
532 0 : if (error == ENETRESET)
533 0 : error = 0;
534 :
535 0 : splx(s);
536 0 : return (error);
537 0 : }
538 :
539 : void
540 0 : cdce_watchdog(struct ifnet *ifp)
541 : {
542 0 : struct cdce_softc *sc = ifp->if_softc;
543 :
544 0 : if (usbd_is_dying(sc->cdce_udev))
545 0 : return;
546 :
547 0 : ifp->if_oerrors++;
548 0 : printf("%s: watchdog timeout\n", sc->cdce_dev.dv_xname);
549 0 : }
550 :
551 : void
552 0 : cdce_init(void *xsc)
553 : {
554 0 : struct cdce_softc *sc = xsc;
555 0 : struct ifnet *ifp = GET_IFP(sc);
556 : struct cdce_chain *c;
557 : usbd_status err;
558 : int s, i;
559 :
560 0 : s = splnet();
561 :
562 0 : if (sc->cdce_intr_no != -1 && sc->cdce_intr_pipe == NULL) {
563 : DPRINTFN(1, ("cdce_init: establish interrupt pipe\n"));
564 0 : err = usbd_open_pipe_intr(sc->cdce_ctl_iface, sc->cdce_intr_no,
565 : USBD_SHORT_XFER_OK, &sc->cdce_intr_pipe, sc,
566 0 : &sc->cdce_intr_buf, sc->cdce_intr_size, cdce_intr,
567 : USBD_DEFAULT_INTERVAL);
568 0 : if (err) {
569 0 : printf("%s: open interrupt pipe failed: %s\n",
570 0 : sc->cdce_dev.dv_xname, usbd_errstr(err));
571 0 : splx(s);
572 0 : return;
573 : }
574 : }
575 :
576 0 : if (cdce_tx_list_init(sc) == ENOBUFS) {
577 0 : printf("%s: tx list init failed\n", sc->cdce_dev.dv_xname);
578 0 : splx(s);
579 0 : return;
580 : }
581 :
582 0 : if (cdce_rx_list_init(sc) == ENOBUFS) {
583 0 : printf("%s: rx list init failed\n", sc->cdce_dev.dv_xname);
584 0 : splx(s);
585 0 : return;
586 : }
587 :
588 : /* Maybe set multicast / broadcast here??? */
589 :
590 0 : err = usbd_open_pipe(sc->cdce_data_iface, sc->cdce_bulkin_no,
591 0 : USBD_EXCLUSIVE_USE, &sc->cdce_bulkin_pipe);
592 0 : if (err) {
593 0 : printf("%s: open rx pipe failed: %s\n", sc->cdce_dev.dv_xname,
594 0 : usbd_errstr(err));
595 0 : splx(s);
596 0 : return;
597 : }
598 :
599 0 : err = usbd_open_pipe(sc->cdce_data_iface, sc->cdce_bulkout_no,
600 0 : USBD_EXCLUSIVE_USE, &sc->cdce_bulkout_pipe);
601 0 : if (err) {
602 0 : printf("%s: open tx pipe failed: %s\n", sc->cdce_dev.dv_xname,
603 0 : usbd_errstr(err));
604 0 : splx(s);
605 0 : return;
606 : }
607 :
608 0 : for (i = 0; i < CDCE_RX_LIST_CNT; i++) {
609 0 : c = &sc->cdce_cdata.cdce_rx_chain[i];
610 0 : usbd_setup_xfer(c->cdce_xfer, sc->cdce_bulkin_pipe, c,
611 0 : c->cdce_buf, CDCE_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
612 : USBD_NO_TIMEOUT, cdce_rxeof);
613 0 : usbd_transfer(c->cdce_xfer);
614 : }
615 :
616 0 : ifp->if_flags |= IFF_RUNNING;
617 0 : ifq_clr_oactive(&ifp->if_snd);
618 :
619 0 : splx(s);
620 0 : }
621 :
622 : int
623 0 : cdce_newbuf(struct cdce_softc *sc, struct cdce_chain *c, struct mbuf *m)
624 : {
625 : struct mbuf *m_new = NULL;
626 :
627 0 : if (m == NULL) {
628 0 : MGETHDR(m_new, M_DONTWAIT, MT_DATA);
629 0 : if (m_new == NULL) {
630 0 : printf("%s: no memory for rx list "
631 0 : "-- packet dropped!\n", sc->cdce_dev.dv_xname);
632 0 : return (ENOBUFS);
633 : }
634 0 : MCLGET(m_new, M_DONTWAIT);
635 0 : if (!(m_new->m_flags & M_EXT)) {
636 0 : printf("%s: no memory for rx list "
637 0 : "-- packet dropped!\n", sc->cdce_dev.dv_xname);
638 0 : m_freem(m_new);
639 0 : return (ENOBUFS);
640 : }
641 0 : m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
642 0 : } else {
643 : m_new = m;
644 0 : m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
645 0 : m_new->m_data = m_new->m_ext.ext_buf;
646 : }
647 :
648 0 : m_adj(m_new, ETHER_ALIGN);
649 0 : c->cdce_mbuf = m_new;
650 0 : return (0);
651 0 : }
652 :
653 : int
654 0 : cdce_rx_list_init(struct cdce_softc *sc)
655 : {
656 : struct cdce_cdata *cd;
657 : struct cdce_chain *c;
658 : int i;
659 :
660 0 : cd = &sc->cdce_cdata;
661 0 : for (i = 0; i < CDCE_RX_LIST_CNT; i++) {
662 0 : c = &cd->cdce_rx_chain[i];
663 0 : c->cdce_sc = sc;
664 0 : c->cdce_idx = i;
665 0 : if (cdce_newbuf(sc, c, NULL) == ENOBUFS)
666 0 : return (ENOBUFS);
667 0 : if (c->cdce_xfer == NULL) {
668 0 : c->cdce_xfer = usbd_alloc_xfer(sc->cdce_udev);
669 0 : if (c->cdce_xfer == NULL)
670 0 : return (ENOBUFS);
671 0 : c->cdce_buf = usbd_alloc_buffer(c->cdce_xfer,
672 : CDCE_BUFSZ);
673 0 : if (c->cdce_buf == NULL)
674 0 : return (ENOBUFS);
675 : }
676 : }
677 :
678 0 : return (0);
679 0 : }
680 :
681 : int
682 0 : cdce_tx_list_init(struct cdce_softc *sc)
683 : {
684 : struct cdce_cdata *cd;
685 : struct cdce_chain *c;
686 : int i;
687 :
688 0 : cd = &sc->cdce_cdata;
689 0 : for (i = 0; i < CDCE_TX_LIST_CNT; i++) {
690 0 : c = &cd->cdce_tx_chain[i];
691 0 : c->cdce_sc = sc;
692 0 : c->cdce_idx = i;
693 0 : c->cdce_mbuf = NULL;
694 0 : if (c->cdce_xfer == NULL) {
695 0 : c->cdce_xfer = usbd_alloc_xfer(sc->cdce_udev);
696 0 : if (c->cdce_xfer == NULL)
697 0 : return (ENOBUFS);
698 0 : c->cdce_buf = usbd_alloc_buffer(c->cdce_xfer,
699 : CDCE_BUFSZ);
700 0 : if (c->cdce_buf == NULL)
701 0 : return (ENOBUFS);
702 : }
703 : }
704 :
705 0 : return (0);
706 0 : }
707 :
708 : void
709 0 : cdce_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
710 : {
711 0 : struct cdce_chain *c = priv;
712 0 : struct cdce_softc *sc = c->cdce_sc;
713 0 : struct ifnet *ifp = GET_IFP(sc);
714 : struct mbuf *m;
715 0 : struct mbuf_list ml = MBUF_LIST_INITIALIZER();
716 0 : int total_len = 0;
717 : int s;
718 :
719 0 : if (usbd_is_dying(sc->cdce_udev) || !(ifp->if_flags & IFF_RUNNING))
720 0 : return;
721 :
722 0 : if (status != USBD_NORMAL_COMPLETION) {
723 0 : if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
724 0 : return;
725 0 : if (sc->cdce_rxeof_errors == 0)
726 0 : printf("%s: usb error on rx: %s\n",
727 0 : sc->cdce_dev.dv_xname, usbd_errstr(status));
728 0 : if (status == USBD_STALLED)
729 0 : usbd_clear_endpoint_stall_async(sc->cdce_bulkin_pipe);
730 0 : DELAY(sc->cdce_rxeof_errors * 10000);
731 0 : if (sc->cdce_rxeof_errors++ > 10) {
732 0 : printf("%s: too many errors, disabling\n",
733 0 : sc->cdce_dev.dv_xname);
734 0 : usbd_deactivate(sc->cdce_udev);
735 0 : return;
736 : }
737 : goto done;
738 : }
739 :
740 0 : sc->cdce_rxeof_errors = 0;
741 :
742 0 : usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
743 0 : if (sc->cdce_flags & CDCE_CRC32)
744 0 : total_len -= 4; /* Strip off added CRC */
745 0 : if (total_len <= 1)
746 : goto done;
747 :
748 0 : m = c->cdce_mbuf;
749 0 : memcpy(mtod(m, char *), c->cdce_buf, total_len);
750 :
751 0 : if (total_len < sizeof(struct ether_header)) {
752 0 : ifp->if_ierrors++;
753 0 : goto done;
754 : }
755 :
756 0 : m->m_pkthdr.len = m->m_len = total_len;
757 0 : ml_enqueue(&ml, m);
758 :
759 0 : if (cdce_newbuf(sc, c, NULL) == ENOBUFS) {
760 0 : ifp->if_ierrors++;
761 0 : goto done;
762 : }
763 :
764 0 : s = splnet();
765 0 : if_input(ifp, &ml);
766 0 : splx(s);
767 :
768 : done:
769 : /* Setup new transfer. */
770 0 : usbd_setup_xfer(c->cdce_xfer, sc->cdce_bulkin_pipe, c, c->cdce_buf,
771 : CDCE_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT,
772 : cdce_rxeof);
773 0 : usbd_transfer(c->cdce_xfer);
774 0 : }
775 :
776 : void
777 0 : cdce_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
778 : {
779 0 : struct cdce_chain *c = priv;
780 0 : struct cdce_softc *sc = c->cdce_sc;
781 0 : struct ifnet *ifp = GET_IFP(sc);
782 0 : usbd_status err;
783 : int s;
784 :
785 0 : if (usbd_is_dying(sc->cdce_udev))
786 0 : return;
787 :
788 0 : s = splnet();
789 :
790 0 : ifp->if_timer = 0;
791 0 : ifq_clr_oactive(&ifp->if_snd);
792 :
793 0 : if (status != USBD_NORMAL_COMPLETION) {
794 0 : if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
795 0 : splx(s);
796 0 : return;
797 : }
798 0 : ifp->if_oerrors++;
799 0 : printf("%s: usb error on tx: %s\n", sc->cdce_dev.dv_xname,
800 0 : usbd_errstr(status));
801 0 : if (status == USBD_STALLED)
802 0 : usbd_clear_endpoint_stall_async(sc->cdce_bulkout_pipe);
803 0 : splx(s);
804 0 : return;
805 : }
806 :
807 0 : usbd_get_xfer_status(c->cdce_xfer, NULL, NULL, NULL, &err);
808 :
809 0 : if (c->cdce_mbuf != NULL) {
810 0 : m_freem(c->cdce_mbuf);
811 0 : c->cdce_mbuf = NULL;
812 0 : }
813 :
814 0 : if (err)
815 0 : ifp->if_oerrors++;
816 :
817 0 : if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
818 0 : cdce_start(ifp);
819 :
820 0 : splx(s);
821 0 : }
822 :
823 : void
824 0 : cdce_intr(struct usbd_xfer *xfer, void *addr, usbd_status status)
825 : {
826 0 : struct cdce_softc *sc = addr;
827 0 : struct usb_cdc_notification *buf = &sc->cdce_intr_buf;
828 : struct usb_cdc_connection_speed *speed;
829 0 : u_int32_t count;
830 :
831 0 : if (status == USBD_CANCELLED)
832 0 : return;
833 :
834 0 : if (status != USBD_NORMAL_COMPLETION) {
835 : DPRINTFN(2, ("cdce_intr: status=%d\n", status));
836 0 : if (status == USBD_STALLED)
837 0 : usbd_clear_endpoint_stall_async(sc->cdce_intr_pipe);
838 0 : return;
839 : }
840 :
841 0 : usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
842 :
843 0 : if (buf->bmRequestType == UCDC_NOTIFICATION) {
844 0 : switch (buf->bNotification) {
845 : case UCDC_N_NETWORK_CONNECTION:
846 : DPRINTFN(1, ("cdce_intr: network %s\n",
847 : UGETW(buf->wValue) ? "connected" : "disconnected"));
848 : break;
849 : case UCDC_N_CONNECTION_SPEED_CHANGE:
850 0 : speed = (struct usb_cdc_connection_speed *)&buf->data;
851 : DPRINTFN(1, ("cdce_intr: up=%d, down=%d\n",
852 : UGETDW(speed->dwUSBitRate),
853 : UGETDW(speed->dwDSBitRate)));
854 0 : break;
855 : default:
856 : DPRINTF(("cdce_intr: bNotification 0x%x\n",
857 : buf->bNotification));
858 : }
859 : }
860 : #ifdef CDCE_DEBUG
861 : else {
862 : printf("cdce_intr: bmRequestType=%d ", buf->bmRequestType);
863 : printf("wValue=%d wIndex=%d wLength=%d\n", UGETW(buf->wValue),
864 : UGETW(buf->wIndex), UGETW(buf->wLength));
865 : }
866 : #endif
867 0 : }
|