Line data Source code
1 : /* $OpenBSD: if_ugl.c,v 1.22 2018/03/15 21:36:59 uaa Exp $ */
2 : /* $NetBSD: if_upl.c,v 1.19 2002/07/11 21:14:26 augustss Exp $ */
3 : /*
4 : * Copyright (c) 2013 SASANO Takayoshi <uaa@uaa.org.uk>
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 :
19 : /*
20 : * Copyright (c) 2000 The NetBSD Foundation, Inc.
21 : * All rights reserved.
22 : *
23 : * This code is derived from software contributed to The NetBSD Foundation
24 : * by Lennart Augustsson (lennart@augustsson.net) at
25 : * Carlstedt Research & Technology.
26 : *
27 : * Redistribution and use in source and binary forms, with or without
28 : * modification, are permitted provided that the following conditions
29 : * are met:
30 : * 1. Redistributions of source code must retain the above copyright
31 : * notice, this list of conditions and the following disclaimer.
32 : * 2. Redistributions in binary form must reproduce the above copyright
33 : * notice, this list of conditions and the following disclaimer in the
34 : * documentation and/or other materials provided with the distribution.
35 : *
36 : * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
37 : * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
38 : * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
39 : * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
40 : * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
41 : * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
42 : * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
43 : * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
44 : * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45 : * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : * POSSIBILITY OF SUCH DAMAGE.
47 : */
48 :
49 : /*
50 : * Genesys Logic GL620USB-A driver
51 : * This driver is based on Prolific PL2301/PL2302 driver (if_upl.c).
52 : */
53 :
54 : #include <bpfilter.h>
55 :
56 : #include <sys/param.h>
57 : #include <sys/systm.h>
58 : #include <sys/timeout.h>
59 : #include <sys/sockio.h>
60 : #include <sys/mbuf.h>
61 : #include <sys/kernel.h>
62 : #include <sys/socket.h>
63 :
64 : #include <sys/device.h>
65 :
66 : #include <net/if.h>
67 :
68 : #if NBPFILTER > 0
69 : #include <net/bpf.h>
70 : #endif
71 :
72 : #include <netinet/in.h>
73 : #include <netinet/if_ether.h>
74 :
75 : #include <dev/usb/usb.h>
76 : #include <dev/usb/usbdi.h>
77 : #include <dev/usb/usbdi_util.h>
78 : #include <dev/usb/usbdevs.h>
79 :
80 : #define UGL_INTR_PKTLEN 8
81 : #define UGL_BULK_PKTLEN 64
82 :
83 : /***/
84 :
85 : #define UGL_INTR_INTERVAL 20
86 :
87 : #define UGL_MAX_MTU 1514
88 : #define UGL_BUFSZ roundup(sizeof(struct ugl_packet), UGL_BULK_PKTLEN)
89 :
90 : #define UGL_RX_FRAMES 1 /* must be one */
91 : #define UGL_TX_FRAMES 1 /* must be one */
92 :
93 : #define UGL_RX_LIST_CNT 1
94 : #define UGL_TX_LIST_CNT 1
95 :
96 : #define UGL_ENDPT_RX 0x0
97 : #define UGL_ENDPT_TX 0x1
98 : #define UGL_ENDPT_INTR 0x2
99 : #define UGL_ENDPT_MAX 0x3
100 :
101 : struct ugl_softc;
102 :
103 : struct ugl_packet {
104 : uDWord pkt_count;
105 : uDWord pkt_length;
106 : char pkt_data[UGL_MAX_MTU];
107 : } __packed;
108 :
109 : struct ugl_chain {
110 : struct ugl_softc *ugl_sc;
111 : struct usbd_xfer *ugl_xfer;
112 : struct ugl_packet *ugl_buf;
113 : struct mbuf *ugl_mbuf;
114 : int ugl_idx;
115 : };
116 :
117 : struct ugl_cdata {
118 : struct ugl_chain ugl_tx_chain[UGL_TX_LIST_CNT];
119 : struct ugl_chain ugl_rx_chain[UGL_RX_LIST_CNT];
120 : int ugl_tx_prod;
121 : int ugl_tx_cons;
122 : int ugl_tx_cnt;
123 : int ugl_rx_prod;
124 : };
125 :
126 : struct ugl_softc {
127 : struct device sc_dev;
128 :
129 : struct arpcom sc_arpcom;
130 : #define GET_IFP(sc) (&(sc)->sc_arpcom.ac_if)
131 : struct timeout sc_stat_ch;
132 :
133 : struct usbd_device *sc_udev;
134 : struct usbd_interface *sc_iface;
135 : int sc_ed[UGL_ENDPT_MAX];
136 : struct usbd_pipe *sc_ep[UGL_ENDPT_MAX];
137 : struct ugl_cdata sc_cdata;
138 :
139 : uByte sc_ibuf[UGL_INTR_PKTLEN];
140 :
141 : u_int sc_rx_errs;
142 : struct timeval sc_rx_notice;
143 : u_int sc_intr_errs;
144 : };
145 :
146 : #ifdef UGL_DEBUG
147 : #define DPRINTF(x) do { if (ugldebug) printf x; } while (0)
148 : #define DPRINTFN(n,x) do { if (ugldebug >= (n)) printf x; } while (0)
149 : int ugldebug = 0;
150 : #else
151 : #define DPRINTF(x)
152 : #define DPRINTFN(n,x)
153 : #endif
154 :
155 : /*
156 : * Various supported device vendors/products.
157 : */
158 : struct usb_devno ugl_devs[] = {
159 : { USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL620USB_A },
160 : };
161 :
162 : int ugl_match(struct device *, void *, void *);
163 : void ugl_attach(struct device *, struct device *, void *);
164 : int ugl_detach(struct device *, int);
165 :
166 : struct cfdriver ugl_cd = {
167 : NULL, "ugl", DV_IFNET
168 : };
169 :
170 : const struct cfattach ugl_ca = {
171 : sizeof(struct ugl_softc), ugl_match, ugl_attach, ugl_detach
172 : };
173 :
174 : int ugl_openpipes(struct ugl_softc *);
175 : int ugl_tx_list_init(struct ugl_softc *);
176 : int ugl_rx_list_init(struct ugl_softc *);
177 : int ugl_newbuf(struct ugl_softc *, struct ugl_chain *, struct mbuf *);
178 : int ugl_send(struct ugl_softc *, struct mbuf *, int);
179 : void ugl_intr(struct usbd_xfer *, void *, usbd_status);
180 : void ugl_rxeof(struct usbd_xfer *, void *, usbd_status);
181 : void ugl_txeof(struct usbd_xfer *, void *, usbd_status);
182 : void ugl_start(struct ifnet *);
183 : int ugl_ioctl(struct ifnet *, u_long, caddr_t);
184 : void ugl_init(void *);
185 : void ugl_stop(struct ugl_softc *);
186 : void ugl_watchdog(struct ifnet *);
187 :
188 : /*
189 : * Probe for a Genesys Logic chip.
190 : */
191 : int
192 0 : ugl_match(struct device *parent, void *match, void *aux)
193 : {
194 0 : struct usb_attach_arg *uaa = aux;
195 :
196 0 : if (uaa->iface == NULL || uaa->configno != 1)
197 0 : return (UMATCH_NONE);
198 :
199 0 : return (usb_lookup(ugl_devs, uaa->vendor, uaa->product) != NULL ?
200 : UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE);
201 0 : }
202 :
203 : void
204 0 : ugl_attach(struct device *parent, struct device *self, void *aux)
205 : {
206 0 : struct ugl_softc *sc = (struct ugl_softc *)self;
207 0 : struct usb_attach_arg *uaa = aux;
208 : int s;
209 0 : struct usbd_device *dev = uaa->device;
210 0 : struct usbd_interface *iface = uaa->iface;
211 0 : struct ifnet *ifp = GET_IFP(sc);
212 : usb_interface_descriptor_t *id;
213 : usb_endpoint_descriptor_t *ed;
214 : int i;
215 :
216 : DPRINTFN(5,(" : ugl_attach: sc=%p, dev=%p", sc, dev));
217 :
218 0 : sc->sc_udev = dev;
219 0 : sc->sc_iface = iface;
220 0 : id = usbd_get_interface_descriptor(iface);
221 :
222 : /* Find endpoints. */
223 0 : for (i = 0; i < id->bNumEndpoints; i++) {
224 0 : ed = usbd_interface2endpoint_descriptor(iface, i);
225 0 : if (ed == NULL) {
226 0 : printf("%s: couldn't get ep %d\n",
227 0 : sc->sc_dev.dv_xname, i);
228 0 : return;
229 : }
230 0 : if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
231 0 : UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
232 0 : sc->sc_ed[UGL_ENDPT_RX] = ed->bEndpointAddress;
233 0 : } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
234 0 : UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
235 0 : sc->sc_ed[UGL_ENDPT_TX] = ed->bEndpointAddress;
236 0 : } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
237 0 : UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
238 0 : sc->sc_ed[UGL_ENDPT_INTR] = ed->bEndpointAddress;
239 0 : }
240 : }
241 :
242 0 : if (sc->sc_ed[UGL_ENDPT_RX] == 0 || sc->sc_ed[UGL_ENDPT_TX] == 0 ||
243 0 : sc->sc_ed[UGL_ENDPT_INTR] == 0) {
244 0 : printf("%s: missing endpoint\n", sc->sc_dev.dv_xname);
245 0 : return;
246 : }
247 :
248 0 : s = splnet();
249 :
250 0 : ether_fakeaddr(ifp);
251 0 : printf("%s: address %s\n",
252 0 : sc->sc_dev.dv_xname, ether_sprintf(sc->sc_arpcom.ac_enaddr));
253 :
254 : /* Initialize interface info.*/
255 0 : ifp->if_softc = sc;
256 0 : ifp->if_hardmtu = UGL_MAX_MTU;
257 0 : ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
258 0 : ifp->if_ioctl = ugl_ioctl;
259 0 : ifp->if_start = ugl_start;
260 0 : ifp->if_watchdog = ugl_watchdog;
261 0 : strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
262 :
263 : /* Attach the interface. */
264 0 : if_attach(ifp);
265 0 : ether_ifattach(ifp);
266 :
267 0 : splx(s);
268 0 : }
269 :
270 : int
271 0 : ugl_detach(struct device *self, int flags)
272 : {
273 0 : struct ugl_softc *sc = (struct ugl_softc *)self;
274 0 : struct ifnet *ifp = GET_IFP(sc);
275 : int s;
276 :
277 : DPRINTFN(2,("%s: %s: enter\n", sc->sc_dev.dv_xname, __func__));
278 :
279 0 : s = splusb();
280 :
281 0 : if (ifp->if_flags & IFF_RUNNING)
282 0 : ugl_stop(sc);
283 :
284 0 : if (ifp->if_softc != NULL)
285 0 : if_detach(ifp);
286 :
287 : #ifdef DIAGNOSTIC
288 0 : if (sc->sc_ep[UGL_ENDPT_TX] != NULL ||
289 0 : sc->sc_ep[UGL_ENDPT_RX] != NULL ||
290 0 : sc->sc_ep[UGL_ENDPT_INTR] != NULL)
291 0 : printf("%s: detach has active endpoints\n",
292 0 : sc->sc_dev.dv_xname);
293 : #endif
294 :
295 0 : splx(s);
296 :
297 0 : return (0);
298 : }
299 :
300 : /*
301 : * Initialize an RX descriptor and attach an MBUF cluster.
302 : */
303 : int
304 0 : ugl_newbuf(struct ugl_softc *sc, struct ugl_chain *c, struct mbuf *m)
305 : {
306 : struct mbuf *m_new = NULL;
307 :
308 : DPRINTFN(8,("%s: %s: enter\n", sc->sc_dev.dv_xname, __func__));
309 :
310 0 : if (m == NULL) {
311 0 : MGETHDR(m_new, M_DONTWAIT, MT_DATA);
312 0 : if (m_new == NULL) {
313 0 : printf("%s: no memory for rx list "
314 0 : "-- packet dropped!\n", sc->sc_dev.dv_xname);
315 0 : return (ENOBUFS);
316 : }
317 :
318 0 : MCLGET(m_new, M_DONTWAIT);
319 0 : if (!(m_new->m_flags & M_EXT)) {
320 0 : printf("%s: no memory for rx list "
321 0 : "-- packet dropped!\n", sc->sc_dev.dv_xname);
322 0 : m_freem(m_new);
323 0 : return (ENOBUFS);
324 : }
325 0 : m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
326 0 : } else {
327 : m_new = m;
328 0 : m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
329 0 : m_new->m_data = m_new->m_ext.ext_buf;
330 : }
331 :
332 0 : c->ugl_mbuf = m_new;
333 :
334 0 : return (0);
335 0 : }
336 :
337 : int
338 0 : ugl_rx_list_init(struct ugl_softc *sc)
339 : {
340 : struct ugl_cdata *cd;
341 : struct ugl_chain *c;
342 : int i;
343 :
344 : DPRINTFN(5,("%s: %s: enter\n", sc->sc_dev.dv_xname, __func__));
345 :
346 0 : cd = &sc->sc_cdata;
347 0 : for (i = 0; i < UGL_RX_LIST_CNT; i++) {
348 0 : c = &cd->ugl_rx_chain[i];
349 0 : c->ugl_sc = sc;
350 0 : c->ugl_idx = i;
351 0 : if (ugl_newbuf(sc, c, NULL) == ENOBUFS)
352 0 : return (ENOBUFS);
353 0 : if (c->ugl_xfer == NULL) {
354 0 : c->ugl_xfer = usbd_alloc_xfer(sc->sc_udev);
355 0 : if (c->ugl_xfer == NULL)
356 0 : return (ENOBUFS);
357 0 : c->ugl_buf = usbd_alloc_buffer(c->ugl_xfer, UGL_BUFSZ);
358 0 : if (c->ugl_buf == NULL) {
359 0 : usbd_free_xfer(c->ugl_xfer);
360 0 : return (ENOBUFS);
361 : }
362 : }
363 : }
364 :
365 0 : return (0);
366 0 : }
367 :
368 : int
369 0 : ugl_tx_list_init(struct ugl_softc *sc)
370 : {
371 : struct ugl_cdata *cd;
372 : struct ugl_chain *c;
373 : int i;
374 :
375 : DPRINTFN(5,("%s: %s: enter\n", sc->sc_dev.dv_xname, __func__));
376 :
377 0 : cd = &sc->sc_cdata;
378 0 : for (i = 0; i < UGL_TX_LIST_CNT; i++) {
379 0 : c = &cd->ugl_tx_chain[i];
380 0 : c->ugl_sc = sc;
381 0 : c->ugl_idx = i;
382 0 : c->ugl_mbuf = NULL;
383 0 : if (c->ugl_xfer == NULL) {
384 0 : c->ugl_xfer = usbd_alloc_xfer(sc->sc_udev);
385 0 : if (c->ugl_xfer == NULL)
386 0 : return (ENOBUFS);
387 0 : c->ugl_buf = usbd_alloc_buffer(c->ugl_xfer, UGL_BUFSZ);
388 0 : if (c->ugl_buf == NULL) {
389 0 : usbd_free_xfer(c->ugl_xfer);
390 0 : return (ENOBUFS);
391 : }
392 : }
393 : }
394 :
395 0 : return (0);
396 0 : }
397 :
398 : /*
399 : * A frame has been uploaded: pass the resulting mbuf chain up to
400 : * the higher level protocols.
401 : */
402 : void
403 0 : ugl_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
404 : {
405 0 : struct ugl_chain *c = priv;
406 0 : struct ugl_softc *sc = c->ugl_sc;
407 0 : struct ifnet *ifp = GET_IFP(sc);
408 0 : struct mbuf_list ml = MBUF_LIST_INITIALIZER();
409 : struct mbuf *m;
410 0 : int total_len = 0;
411 : unsigned int packet_len, packet_count;
412 : int s;
413 :
414 0 : if (usbd_is_dying(sc->sc_udev))
415 0 : return;
416 :
417 0 : if (!(ifp->if_flags & IFF_RUNNING))
418 0 : return;
419 :
420 0 : if (status != USBD_NORMAL_COMPLETION) {
421 0 : if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
422 0 : return;
423 0 : sc->sc_rx_errs++;
424 0 : if (usbd_ratecheck(&sc->sc_rx_notice)) {
425 0 : printf("%s: %u usb errors on rx: %s\n",
426 0 : sc->sc_dev.dv_xname, sc->sc_rx_errs,
427 0 : usbd_errstr(status));
428 0 : sc->sc_rx_errs = 0;
429 0 : }
430 0 : if (status == USBD_STALLED)
431 0 : usbd_clear_endpoint_stall_async(sc->sc_ep[UGL_ENDPT_RX]);
432 : goto done;
433 : }
434 :
435 0 : usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
436 :
437 : DPRINTFN(9,("%s: %s: enter status=%d length=%d\n",
438 : sc->sc_dev.dv_xname, __func__, status, total_len));
439 :
440 0 : if (total_len < offsetof(struct ugl_packet, pkt_data)) {
441 0 : printf("%s: bad header (length=%d)\n",
442 0 : sc->sc_dev.dv_xname, total_len);
443 :
444 0 : goto done;
445 : }
446 :
447 0 : packet_count = UGETDW(c->ugl_buf->pkt_count);
448 0 : if (packet_count != UGL_RX_FRAMES) {
449 0 : printf("%s: bad packet count (%d)\n",
450 0 : sc->sc_dev.dv_xname, packet_count);
451 :
452 0 : if (packet_count == 0)
453 : goto done;
454 : }
455 :
456 0 : packet_len = UGETDW(c->ugl_buf->pkt_length);
457 0 : if (total_len < packet_len) {
458 0 : printf("%s: bad packet size(%d), length=%d\n",
459 0 : sc->sc_dev.dv_xname, packet_len, total_len);
460 :
461 0 : if (packet_len == 0)
462 : goto done;
463 : }
464 :
465 0 : m = c->ugl_mbuf;
466 0 : memcpy(mtod(c->ugl_mbuf, char *), c->ugl_buf->pkt_data, packet_len);
467 :
468 0 : m->m_pkthdr.len = m->m_len = packet_len;
469 0 : ml_enqueue(&ml, m);
470 :
471 0 : if (ugl_newbuf(sc, c, NULL) == ENOBUFS) {
472 0 : ifp->if_ierrors++;
473 0 : goto done;
474 : }
475 :
476 0 : s = splnet();
477 0 : if_input(ifp, &ml);
478 0 : splx(s);
479 :
480 : done:
481 : /* Setup new transfer. */
482 0 : usbd_setup_xfer(c->ugl_xfer, sc->sc_ep[UGL_ENDPT_RX],
483 0 : c, c->ugl_buf, UGL_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
484 : USBD_NO_TIMEOUT, ugl_rxeof);
485 0 : usbd_transfer(c->ugl_xfer);
486 :
487 : DPRINTFN(10,("%s: %s: start rx\n", sc->sc_dev.dv_xname,
488 : __func__));
489 0 : }
490 :
491 : /*
492 : * A frame was downloaded to the chip. It's safe for us to clean up
493 : * the list buffers.
494 : */
495 : void
496 0 : ugl_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
497 : {
498 0 : struct ugl_chain *c = priv;
499 0 : struct ugl_softc *sc = c->ugl_sc;
500 0 : struct ifnet *ifp = GET_IFP(sc);
501 : int s;
502 :
503 0 : if (usbd_is_dying(sc->sc_udev))
504 0 : return;
505 :
506 0 : s = splnet();
507 :
508 : DPRINTFN(10,("%s: %s: enter status=%d\n", sc->sc_dev.dv_xname,
509 : __func__, status));
510 :
511 0 : ifp->if_timer = 0;
512 0 : ifq_clr_oactive(&ifp->if_snd);
513 :
514 0 : if (status != USBD_NORMAL_COMPLETION) {
515 0 : if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
516 0 : splx(s);
517 0 : return;
518 : }
519 0 : ifp->if_oerrors++;
520 0 : printf("%s: usb error on tx: %s\n", sc->sc_dev.dv_xname,
521 0 : usbd_errstr(status));
522 0 : if (status == USBD_STALLED)
523 0 : usbd_clear_endpoint_stall_async(sc->sc_ep[UGL_ENDPT_TX]);
524 0 : splx(s);
525 0 : return;
526 : }
527 :
528 0 : m_freem(c->ugl_mbuf);
529 0 : c->ugl_mbuf = NULL;
530 :
531 0 : if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
532 0 : ugl_start(ifp);
533 :
534 0 : splx(s);
535 0 : }
536 :
537 : int
538 0 : ugl_send(struct ugl_softc *sc, struct mbuf *m, int idx)
539 : {
540 : int total_len;
541 : struct ugl_chain *c;
542 : usbd_status err;
543 :
544 0 : c = &sc->sc_cdata.ugl_tx_chain[idx];
545 :
546 : /*
547 : * Copy the mbuf data into a contiguous buffer, leaving two
548 : * bytes at the beginning to hold the frame length.
549 : */
550 0 : USETDW(c->ugl_buf->pkt_count, UGL_TX_FRAMES);
551 0 : USETDW(c->ugl_buf->pkt_length, m->m_pkthdr.len);
552 0 : m_copydata(m, 0, m->m_pkthdr.len, c->ugl_buf->pkt_data);
553 0 : c->ugl_mbuf = m;
554 :
555 0 : total_len = offsetof(struct ugl_packet, pkt_data[m->m_pkthdr.len]);
556 :
557 : DPRINTFN(10,("%s: %s: total_len=%d\n",
558 : sc->sc_dev.dv_xname, __func__, total_len));
559 :
560 0 : usbd_setup_xfer(c->ugl_xfer, sc->sc_ep[UGL_ENDPT_TX],
561 0 : c, c->ugl_buf, total_len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY,
562 : USBD_DEFAULT_TIMEOUT, ugl_txeof);
563 :
564 : /* Transmit */
565 0 : err = usbd_transfer(c->ugl_xfer);
566 0 : if (err != USBD_IN_PROGRESS) {
567 0 : printf("%s: ugl_send error=%s\n", sc->sc_dev.dv_xname,
568 0 : usbd_errstr(err));
569 0 : ugl_stop(sc);
570 0 : return (EIO);
571 : }
572 :
573 0 : sc->sc_cdata.ugl_tx_cnt++;
574 :
575 0 : return (0);
576 0 : }
577 :
578 : void
579 0 : ugl_start(struct ifnet *ifp)
580 : {
581 0 : struct ugl_softc *sc = ifp->if_softc;
582 : struct mbuf *m_head = NULL;
583 :
584 0 : if (usbd_is_dying(sc->sc_udev))
585 0 : return;
586 :
587 : DPRINTFN(10,("%s: %s: enter\n", sc->sc_dev.dv_xname,__func__));
588 :
589 0 : if (ifq_is_oactive(&ifp->if_snd))
590 0 : return;
591 :
592 0 : m_head = ifq_deq_begin(&ifp->if_snd);
593 0 : if (m_head == NULL)
594 0 : return;
595 :
596 0 : if (ugl_send(sc, m_head, 0)) {
597 0 : ifq_deq_rollback(&ifp->if_snd, m_head);
598 0 : ifq_set_oactive(&ifp->if_snd);
599 0 : return;
600 : }
601 :
602 0 : ifq_deq_commit(&ifp->if_snd, m_head);
603 :
604 : #if NBPFILTER > 0
605 : /*
606 : * If there's a BPF listener, bounce a copy of this frame
607 : * to him.
608 : */
609 0 : if (ifp->if_bpf)
610 0 : bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT);
611 : #endif
612 :
613 0 : ifq_set_oactive(&ifp->if_snd);
614 :
615 : /*
616 : * Set a timeout in case the chip goes out to lunch.
617 : */
618 0 : ifp->if_timer = 5;
619 0 : }
620 :
621 : void
622 0 : ugl_init(void *xsc)
623 : {
624 0 : struct ugl_softc *sc = xsc;
625 0 : struct ifnet *ifp = GET_IFP(sc);
626 : int s;
627 :
628 0 : if (usbd_is_dying(sc->sc_udev))
629 0 : return;
630 :
631 : DPRINTFN(10,("%s: %s: enter\n", sc->sc_dev.dv_xname,__func__));
632 :
633 0 : s = splnet();
634 :
635 : /* Init TX ring. */
636 0 : if (ugl_tx_list_init(sc) == ENOBUFS) {
637 0 : printf("%s: tx list init failed\n", sc->sc_dev.dv_xname);
638 0 : splx(s);
639 0 : return;
640 : }
641 :
642 : /* Init RX ring. */
643 0 : if (ugl_rx_list_init(sc) == ENOBUFS) {
644 0 : printf("%s: rx list init failed\n", sc->sc_dev.dv_xname);
645 0 : splx(s);
646 0 : return;
647 : }
648 :
649 0 : if (sc->sc_ep[UGL_ENDPT_RX] == NULL) {
650 0 : if (ugl_openpipes(sc)) {
651 0 : splx(s);
652 0 : return;
653 : }
654 : }
655 :
656 0 : ifp->if_flags |= IFF_RUNNING;
657 0 : ifq_clr_oactive(&ifp->if_snd);
658 :
659 0 : splx(s);
660 0 : }
661 :
662 : int
663 0 : ugl_openpipes(struct ugl_softc *sc)
664 : {
665 : struct ugl_chain *c;
666 : usbd_status err;
667 : int i;
668 :
669 : /* Open RX and TX pipes. */
670 0 : err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[UGL_ENDPT_RX],
671 0 : USBD_EXCLUSIVE_USE, &sc->sc_ep[UGL_ENDPT_RX]);
672 0 : if (err) {
673 0 : printf("%s: open rx pipe failed: %s\n",
674 0 : sc->sc_dev.dv_xname, usbd_errstr(err));
675 0 : return (EIO);
676 : }
677 0 : err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[UGL_ENDPT_TX],
678 0 : USBD_EXCLUSIVE_USE, &sc->sc_ep[UGL_ENDPT_TX]);
679 0 : if (err) {
680 0 : printf("%s: open tx pipe failed: %s\n",
681 0 : sc->sc_dev.dv_xname, usbd_errstr(err));
682 0 : return (EIO);
683 : }
684 0 : err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ed[UGL_ENDPT_INTR],
685 0 : USBD_EXCLUSIVE_USE, &sc->sc_ep[UGL_ENDPT_INTR], sc,
686 0 : sc->sc_ibuf, UGL_INTR_PKTLEN, ugl_intr,
687 : UGL_INTR_INTERVAL);
688 0 : if (err) {
689 0 : printf("%s: open intr pipe failed: %s\n",
690 0 : sc->sc_dev.dv_xname, usbd_errstr(err));
691 0 : return (EIO);
692 : }
693 :
694 : /* Start up the receive pipe. */
695 0 : for (i = 0; i < UGL_RX_LIST_CNT; i++) {
696 0 : c = &sc->sc_cdata.ugl_rx_chain[i];
697 0 : usbd_setup_xfer(c->ugl_xfer, sc->sc_ep[UGL_ENDPT_RX],
698 0 : c, c->ugl_buf, UGL_BUFSZ,
699 : USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT,
700 : ugl_rxeof);
701 0 : usbd_transfer(c->ugl_xfer);
702 : }
703 :
704 0 : return (0);
705 0 : }
706 :
707 : void
708 0 : ugl_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
709 : {
710 0 : struct ugl_softc *sc = priv;
711 0 : struct ifnet *ifp = GET_IFP(sc);
712 : int i;
713 :
714 : DPRINTFN(15,("%s: %s: enter\n", sc->sc_dev.dv_xname,__func__));
715 :
716 0 : if (usbd_is_dying(sc->sc_udev))
717 0 : return;
718 :
719 0 : if (!(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 : }
726 0 : sc->sc_intr_errs++;
727 0 : if (usbd_ratecheck(&sc->sc_rx_notice)) {
728 0 : printf("%s: %u usb errors on intr: %s\n",
729 0 : sc->sc_dev.dv_xname, sc->sc_rx_errs,
730 0 : usbd_errstr(status));
731 0 : sc->sc_intr_errs = 0;
732 0 : }
733 0 : if (status == USBD_STALLED)
734 0 : usbd_clear_endpoint_stall_async(sc->sc_ep[UGL_ENDPT_RX]);
735 0 : return;
736 : }
737 :
738 : DPRINTFN(10,("%s: %s:", sc->sc_dev.dv_xname, __func__));
739 0 : for (i = 0; i < UGL_INTR_PKTLEN; i++)
740 : DPRINTFN(10,(" 0x%02x", sc->sc_ibuf[i]));
741 : DPRINTFN(10,("\n"));
742 :
743 0 : }
744 :
745 : int
746 0 : ugl_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
747 : {
748 0 : struct ugl_softc *sc = ifp->if_softc;
749 : int s, error = 0;
750 :
751 0 : if (usbd_is_dying(sc->sc_udev))
752 0 : return (EIO);
753 :
754 : DPRINTFN(5,("%s: %s: cmd=0x%08lx\n",
755 : sc->sc_dev.dv_xname, __func__, command));
756 :
757 0 : s = splnet();
758 :
759 0 : switch(command) {
760 : case SIOCSIFADDR:
761 0 : ifp->if_flags |= IFF_UP;
762 0 : if (!(ifp->if_flags & IFF_RUNNING))
763 0 : ugl_init(sc);
764 : break;
765 :
766 : case SIOCSIFFLAGS:
767 0 : if (ifp->if_flags & IFF_UP) {
768 0 : if (ifp->if_flags & IFF_RUNNING)
769 0 : error = ENETRESET;
770 : else
771 0 : ugl_init(sc);
772 : } else {
773 0 : if (ifp->if_flags & IFF_RUNNING)
774 0 : ugl_stop(sc);
775 : }
776 : break;
777 :
778 : default:
779 0 : error = ether_ioctl(ifp, &sc->sc_arpcom, command, data);
780 0 : break;
781 : }
782 :
783 0 : if (error == ENETRESET)
784 0 : error = 0;
785 :
786 0 : splx(s);
787 0 : return (error);
788 0 : }
789 :
790 : void
791 0 : ugl_watchdog(struct ifnet *ifp)
792 : {
793 0 : struct ugl_softc *sc = ifp->if_softc;
794 :
795 0 : if (usbd_is_dying(sc->sc_udev))
796 0 : return;
797 :
798 0 : ifp->if_oerrors++;
799 0 : printf("%s: watchdog timeout\n", sc->sc_dev.dv_xname);
800 0 : }
801 :
802 : /*
803 : * Stop the adapter and free any mbufs allocated to the
804 : * RX and TX lists.
805 : */
806 : void
807 0 : ugl_stop(struct ugl_softc *sc)
808 : {
809 : struct ifnet *ifp;
810 : int i;
811 :
812 : DPRINTFN(10,("%s: %s: enter\n", sc->sc_dev.dv_xname,__func__));
813 :
814 0 : ifp = GET_IFP(sc);
815 0 : ifp->if_timer = 0;
816 0 : ifp->if_flags &= ~IFF_RUNNING;
817 0 : ifq_clr_oactive(&ifp->if_snd);
818 :
819 : /* Stop transfers. */
820 0 : if (sc->sc_ep[UGL_ENDPT_RX] != NULL) {
821 0 : usbd_abort_pipe(sc->sc_ep[UGL_ENDPT_RX]);
822 0 : usbd_close_pipe(sc->sc_ep[UGL_ENDPT_RX]);
823 0 : sc->sc_ep[UGL_ENDPT_RX] = NULL;
824 0 : }
825 :
826 0 : if (sc->sc_ep[UGL_ENDPT_TX] != NULL) {
827 0 : usbd_abort_pipe(sc->sc_ep[UGL_ENDPT_TX]);
828 0 : usbd_close_pipe(sc->sc_ep[UGL_ENDPT_TX]);
829 0 : sc->sc_ep[UGL_ENDPT_TX] = NULL;
830 0 : }
831 :
832 0 : if (sc->sc_ep[UGL_ENDPT_INTR] != NULL) {
833 0 : usbd_abort_pipe(sc->sc_ep[UGL_ENDPT_INTR]);
834 0 : usbd_close_pipe(sc->sc_ep[UGL_ENDPT_INTR]);
835 0 : sc->sc_ep[UGL_ENDPT_INTR] = NULL;
836 0 : }
837 :
838 : /* Free RX resources. */
839 0 : for (i = 0; i < UGL_RX_LIST_CNT; i++) {
840 0 : if (sc->sc_cdata.ugl_rx_chain[i].ugl_mbuf != NULL) {
841 0 : m_freem(sc->sc_cdata.ugl_rx_chain[i].ugl_mbuf);
842 0 : sc->sc_cdata.ugl_rx_chain[i].ugl_mbuf = NULL;
843 0 : }
844 0 : if (sc->sc_cdata.ugl_rx_chain[i].ugl_xfer != NULL) {
845 0 : usbd_free_xfer(sc->sc_cdata.ugl_rx_chain[i].ugl_xfer);
846 0 : sc->sc_cdata.ugl_rx_chain[i].ugl_xfer = NULL;
847 0 : }
848 : }
849 :
850 : /* Free TX resources. */
851 0 : for (i = 0; i < UGL_TX_LIST_CNT; i++) {
852 0 : if (sc->sc_cdata.ugl_tx_chain[i].ugl_mbuf != NULL) {
853 0 : m_freem(sc->sc_cdata.ugl_tx_chain[i].ugl_mbuf);
854 0 : sc->sc_cdata.ugl_tx_chain[i].ugl_mbuf = NULL;
855 0 : }
856 0 : if (sc->sc_cdata.ugl_tx_chain[i].ugl_xfer != NULL) {
857 0 : usbd_free_xfer(sc->sc_cdata.ugl_tx_chain[i].ugl_xfer);
858 0 : sc->sc_cdata.ugl_tx_chain[i].ugl_xfer = NULL;
859 0 : }
860 : }
861 0 : }
|