]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/usb/ucom.c
This commit was generated by cvs2svn to compensate for changes in r178476,
[FreeBSD/FreeBSD.git] / sys / dev / usb / ucom.c
1 /*      $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $  */
2
3 /*-
4  * Copyright (c) 2001-2003, 2005, 2008
5  *      Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 /*-
34  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
35  * All rights reserved.
36  *
37  * This code is derived from software contributed to The NetBSD Foundation
38  * by Lennart Augustsson (lennart@augustsson.net) at
39  * Carlstedt Research & Technology.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. All advertising materials mentioning features or use of this software
50  *    must display the following acknowledgement:
51  *        This product includes software developed by the NetBSD
52  *        Foundation, Inc. and its contributors.
53  * 4. Neither the name of The NetBSD Foundation nor the names of its
54  *    contributors may be used to endorse or promote products derived
55  *    from this software without specific prior written permission.
56  *
57  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
58  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
59  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
60  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
61  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
62  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
63  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
64  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
66  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
67  * POSSIBILITY OF SUCH DAMAGE.
68  */
69
70 #include <sys/param.h>
71 #include <sys/systm.h>
72 #include <sys/kernel.h>
73 #include <sys/malloc.h>
74 #include <sys/module.h>
75 #include <sys/bus.h>
76 #include <sys/ioccom.h>
77 #include <sys/fcntl.h>
78 #include <sys/conf.h>
79 #include <sys/serial.h>
80 #include <sys/tty.h>
81 #include <sys/clist.h>
82 #include <sys/file.h>
83 #include <sys/selinfo.h>
84 #include <sys/proc.h>
85 #include <sys/poll.h>
86 #include <sys/sysctl.h>
87
88 #include <dev/usb/usb.h>
89 #include <dev/usb/usbcdc.h>
90
91 #include <dev/usb/usbdi.h>
92 #include <dev/usb/usbdi_util.h>
93 #include "usbdevs.h"
94 #include <dev/usb/usb_quirks.h>
95
96 #include <dev/usb/ucomvar.h>
97
98 #ifdef USB_DEBUG
99 static int      ucomdebug = 0;
100 SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
101 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
102            &ucomdebug, 0, "ucom debug level");
103 #define DPRINTF(x)      do { \
104                                 if (ucomdebug) \
105                                         printf x; \
106                         } while (0)
107
108 #define DPRINTFN(n, x)  do { \
109                                 if (ucomdebug > (n)) \
110                                         printf x; \
111                         } while (0)
112 #else
113 #define DPRINTF(x)
114 #define DPRINTFN(n, x)
115 #endif
116
117 static int ucom_modevent(module_t, int, void *);
118 static void ucom_cleanup(struct ucom_softc *);
119 static int ucomparam(struct tty *, struct termios *);
120 static void ucomstart(struct tty *);
121 static void ucomstop(struct tty *, int);
122 static void ucom_shutdown(struct ucom_softc *);
123 static void ucom_dtr(struct ucom_softc *, int);
124 static void ucom_rts(struct ucom_softc *, int);
125 static void ucombreak(struct tty *, int);
126 static usbd_status ucomstartread(struct ucom_softc *);
127 static void ucomreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status);
128 static void ucomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status);
129 static void ucomstopread(struct ucom_softc *);
130
131 static t_open_t  ucomopen;
132 static t_close_t ucomclose;
133 static t_modem_t ucommodem;
134 static t_ioctl_t ucomioctl;
135
136 devclass_t ucom_devclass;
137
138 static moduledata_t ucom_mod = {
139         "ucom",
140         ucom_modevent,
141         NULL
142 };
143
144 DECLARE_MODULE(ucom, ucom_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
145 MODULE_DEPEND(ucom, usb, 1, 1, 1);
146 MODULE_VERSION(ucom, UCOM_MODVER);
147
148 static int
149 ucom_modevent(module_t mod, int type, void *data)
150 {
151         switch (type) {
152         case MOD_LOAD:
153                 break;
154         case MOD_UNLOAD:
155                 break;
156         default:
157                 return (EOPNOTSUPP);
158                 break;
159         }
160         return (0);
161 }
162
163 int
164 ucom_attach_tty(struct ucom_softc *sc, int flags, char* fmt, int unit)
165 {
166         struct tty *tp;
167
168         sc->sc_tty = tp = ttyalloc();
169         tp->t_sc = sc;
170         tp->t_oproc = ucomstart;
171         tp->t_param = ucomparam;
172         tp->t_stop = ucomstop;
173         tp->t_break = ucombreak;
174         tp->t_open = ucomopen;
175         tp->t_close = ucomclose;
176         tp->t_modem = ucommodem;
177         tp->t_ioctl = ucomioctl;
178
179         return ttycreate(tp, flags, fmt, unit);
180 }
181
182 int
183 ucom_attach(struct ucom_softc *sc)
184 {
185
186         ucom_attach_tty(sc, TS_CALLOUT,
187             "U%d", device_get_unit(sc->sc_dev));
188
189         DPRINTF(("ucom_attach: ttycreate: tp = %p, %s\n",
190             sc->sc_tty, sc->sc_tty->t_dev->si_name));
191
192         return (0);
193 }
194
195 int
196 ucom_detach(struct ucom_softc *sc)
197 {
198         int s;
199
200         DPRINTF(("ucom_detach: sc = %p, tp = %p\n", sc, sc->sc_tty));
201
202         sc->sc_dying = 1;
203         ttygone(sc->sc_tty);
204         if (sc->sc_tty->t_state & TS_ISOPEN)
205                 ucomclose(sc->sc_tty);
206
207         if (sc->sc_bulkin_pipe != NULL)
208                 usbd_abort_pipe(sc->sc_bulkin_pipe);
209         if (sc->sc_bulkout_pipe != NULL)
210                 usbd_abort_pipe(sc->sc_bulkout_pipe);
211
212         ttyfree(sc->sc_tty);
213
214         s = splusb();
215         splx(s);
216
217         return (0);
218 }
219
220 static void
221 ucom_shutdown(struct ucom_softc *sc)
222 {
223         struct tty *tp = sc->sc_tty;
224
225         DPRINTF(("ucom_shutdown\n"));
226         /*
227          * Hang up if necessary.  Wait a bit, so the other side has time to
228          * notice even if we immediately open the port again.
229          */
230         if (ISSET(tp->t_cflag, HUPCL)) {
231                 (void)ucommodem(tp, 0, SER_DTR);
232                 (void)tsleep(sc, TTIPRI, "ucomsd", hz);
233         }
234 }
235
236 static int
237 ucomopen(struct tty *tp, struct cdev *dev)
238 {
239         struct ucom_softc *sc;
240         usbd_status err;
241         int error;
242
243         sc = tp->t_sc;
244
245         if (sc->sc_dying)
246                 return (ENXIO);
247
248         DPRINTF(("%s: ucomopen: tp = %p\n", device_get_nameunit(sc->sc_dev), tp));
249
250         sc->sc_poll = 0;
251         sc->sc_lsr = sc->sc_msr = sc->sc_mcr = 0;
252
253         (void)ucommodem(tp, SER_DTR | SER_RTS, 0);
254
255         /* Device specific open */
256         if (sc->sc_callback->ucom_open != NULL) {
257                 error = sc->sc_callback->ucom_open(sc->sc_parent,
258                                                    sc->sc_portno);
259                 if (error) {
260                         ucom_cleanup(sc);
261                         return (error);
262                 }
263         }
264
265         DPRINTF(("ucomopen: open pipes in = %d out = %d\n",
266                  sc->sc_bulkin_no, sc->sc_bulkout_no));
267
268         /* Open the bulk pipes */
269         /* Bulk-in pipe */
270         err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no, 0,
271                              &sc->sc_bulkin_pipe);
272         if (err) {
273                 printf("%s: open bulk in error (addr %d): %s\n",
274                        device_get_nameunit(sc->sc_dev), sc->sc_bulkin_no,
275                        usbd_errstr(err));
276                 error = EIO;
277                 goto fail;
278         }
279         /* Bulk-out pipe */
280         err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
281                              USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
282         if (err) {
283                 printf("%s: open bulk out error (addr %d): %s\n",
284                        device_get_nameunit(sc->sc_dev), sc->sc_bulkout_no,
285                        usbd_errstr(err));
286                 error = EIO;
287                 goto fail;
288         }
289
290         /* Allocate a request and an input buffer and start reading. */
291         sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
292         if (sc->sc_ixfer == NULL) {
293                 error = ENOMEM;
294                 goto fail;
295         }
296
297         sc->sc_ibuf = usbd_alloc_buffer(sc->sc_ixfer,
298                                         sc->sc_ibufsizepad);
299         if (sc->sc_ibuf == NULL) {
300                 error = ENOMEM;
301                 goto fail;
302         }
303
304         sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
305         if (sc->sc_oxfer == NULL) {
306                 error = ENOMEM;
307                 goto fail;
308         }
309
310         sc->sc_obuf = usbd_alloc_buffer(sc->sc_oxfer,
311                                         sc->sc_obufsize +
312                                         sc->sc_opkthdrlen);
313         if (sc->sc_obuf == NULL) {
314                 error = ENOMEM;
315                 goto fail;
316         }
317
318         sc->sc_state |= UCS_RXSTOP;
319         ucomstartread(sc);
320
321         sc->sc_poll = 1;
322
323         return (0);
324
325 fail:
326         ucom_cleanup(sc);
327         return (error);
328 }
329
330 static void
331 ucomclose(struct tty *tp)
332 {
333         struct ucom_softc *sc;
334
335         sc = tp->t_sc;
336
337         DPRINTF(("%s: ucomclose \n", device_get_nameunit(sc->sc_dev)));
338
339         ucom_cleanup(sc);
340
341         if (sc->sc_callback->ucom_close != NULL)
342                 sc->sc_callback->ucom_close(sc->sc_parent, sc->sc_portno);
343 }
344
345 static int
346 ucomioctl(struct tty *tp, u_long cmd, void *data, int flag, struct thread *p)
347 {
348         struct ucom_softc *sc;
349         int error;
350
351         sc = tp->t_sc;;
352         if (sc->sc_dying)
353                 return (EIO);
354
355         DPRINTF(("ucomioctl: cmd = 0x%08lx\n", cmd));
356
357         error = ENOTTY;
358         if (sc->sc_callback->ucom_ioctl != NULL)
359                 error = sc->sc_callback->ucom_ioctl(sc->sc_parent,
360                                                     sc->sc_portno,
361                                                     cmd, data, flag, p);
362         return (error);
363 }
364
365 static int
366 ucommodem(struct tty *tp, int sigon, int sigoff)
367 {
368         struct ucom_softc *sc;
369         int     mcr;
370         int     msr;
371         int     onoff;
372
373         sc = tp->t_sc;
374
375         if (sigon == 0 && sigoff == 0) {
376                 mcr = sc->sc_mcr;
377                 if (ISSET(mcr, SER_DTR))
378                         sigon |= SER_DTR;
379                 if (ISSET(mcr, SER_RTS))
380                         sigon |= SER_RTS;
381
382                 msr = sc->sc_msr;
383                 if (ISSET(msr, SER_CTS))
384                         sigon |= SER_CTS;
385                 if (ISSET(msr, SER_DCD))
386                         sigon |= SER_DCD;
387                 if (ISSET(msr, SER_DSR))
388                         sigon |= SER_DSR;
389                 if (ISSET(msr, SER_RI))
390                         sigon |= SER_RI;
391                 return (sigon);
392         }
393
394         mcr = sc->sc_mcr;
395         if (ISSET(sigon, SER_DTR))
396                 mcr |= SER_DTR;
397         if (ISSET(sigoff, SER_DTR))
398                 mcr &= ~SER_DTR;
399         if (ISSET(sigon, SER_RTS))
400                 mcr |= SER_RTS;
401         if (ISSET(sigoff, SER_RTS))
402                 mcr &= ~SER_RTS;
403         sc->sc_mcr = mcr;
404
405         onoff = ISSET(sc->sc_mcr, SER_DTR) ? 1 : 0;
406         ucom_dtr(sc, onoff);
407
408         onoff = ISSET(sc->sc_mcr, SER_RTS) ? 1 : 0;
409         ucom_rts(sc, onoff);
410
411         return (0);
412 }
413
414 static void
415 ucombreak(struct tty *tp, int onoff)
416 {
417         struct ucom_softc *sc;
418
419         sc = tp->t_sc;
420         DPRINTF(("ucombreak: onoff = %d\n", onoff));
421         if (sc->sc_callback->ucom_set == NULL)
422                 return;
423         sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
424                                   UCOM_SET_BREAK, onoff);
425 }
426
427 static void
428 ucom_dtr(struct ucom_softc *sc, int onoff)
429 {
430         DPRINTF(("ucom_dtr: onoff = %d\n", onoff));
431
432         if (sc->sc_callback->ucom_set == NULL)
433                 return;
434         sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
435                                   UCOM_SET_DTR, onoff);
436 }
437
438 static void
439 ucom_rts(struct ucom_softc *sc, int onoff)
440 {
441         DPRINTF(("ucom_rts: onoff = %d\n", onoff));
442
443         if (sc->sc_callback->ucom_set == NULL)
444                 return;
445         sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
446                                   UCOM_SET_RTS, onoff);
447 }
448
449 void
450 ucom_status_change(struct ucom_softc *sc)
451 {
452         struct tty *tp = sc->sc_tty;
453         u_char old_msr;
454         int onoff;
455
456         if (sc->sc_callback->ucom_get_status == NULL) {
457                 sc->sc_lsr = 0;
458                 sc->sc_msr = 0;
459                 return;
460         }
461
462         old_msr = sc->sc_msr;
463         sc->sc_callback->ucom_get_status(sc->sc_parent, sc->sc_portno,
464                                          &sc->sc_lsr, &sc->sc_msr);
465         if (ISSET((sc->sc_msr ^ old_msr), SER_DCD)) {
466                 if (sc->sc_poll == 0)
467                         return;
468                 onoff = ISSET(sc->sc_msr, SER_DCD) ? 1 : 0;
469                 DPRINTF(("ucom_status_change: DCD changed to %d\n", onoff));
470                 ttyld_modem(tp, onoff);
471         }
472 }
473
474 static int
475 ucomparam(struct tty *tp, struct termios *t)
476 {
477         struct ucom_softc *sc;
478         int error;
479         usbd_status uerr;
480
481         sc = tp->t_sc;
482
483         if (sc->sc_dying)
484                 return (EIO);
485
486         DPRINTF(("ucomparam: sc = %p\n", sc));
487
488         /* Check requested parameters. */
489         if (t->c_ospeed < 0) {
490                 DPRINTF(("ucomparam: negative ospeed\n"));
491                 return (EINVAL);
492         }
493         if (t->c_ispeed && t->c_ispeed != t->c_ospeed) {
494                 DPRINTF(("ucomparam: mismatch ispeed and ospeed\n"));
495                 return (EINVAL);
496         }
497
498         /*
499          * If there were no changes, don't do anything.  This avoids dropping
500          * input and improves performance when all we did was frob things like
501          * VMIN and VTIME.
502          */
503         if (tp->t_ospeed == t->c_ospeed &&
504             tp->t_cflag == t->c_cflag)
505                 return (0);
506
507         /* And copy to tty. */
508         tp->t_ispeed = 0;
509         tp->t_ospeed = t->c_ospeed;
510         tp->t_cflag = t->c_cflag;
511
512         if (sc->sc_callback->ucom_param == NULL)
513                 return (0);
514
515         ucomstopread(sc);
516
517         error = sc->sc_callback->ucom_param(sc->sc_parent, sc->sc_portno, t);
518         if (error) {
519                 DPRINTF(("ucomparam: callback: error = %d\n", error));
520                 return (error);
521         }
522
523         ttsetwater(tp);
524
525         if (t->c_cflag & CRTS_IFLOW) {
526                 sc->sc_state |= UCS_RTS_IFLOW;
527         } else if (sc->sc_state & UCS_RTS_IFLOW) {
528                 sc->sc_state &= ~UCS_RTS_IFLOW;
529                 (void)ucommodem(tp, SER_RTS, 0);
530         }
531
532         ttyldoptim(tp);
533
534         uerr = ucomstartread(sc);
535         if (uerr != USBD_NORMAL_COMPLETION)
536                 return (EIO);
537
538         return (0);
539 }
540
541 static void
542 ucomstart(struct tty *tp)
543 {
544         struct ucom_softc *sc;
545         struct cblock *cbp;
546         usbd_status err;
547         int s;
548         u_char *data;
549         int cnt;
550
551         sc = tp->t_sc;
552         DPRINTF(("ucomstart: sc = %p\n", sc));
553
554         if (sc->sc_dying)
555                 return;
556
557         /*
558          * If there's no sc_oxfer, then ucomclose has removed it.  The buffer
559          * has just been flushed in the ttyflush() in ttyclose().  ttyflush()
560          * then calls tt_stop().  ucomstop calls ucomstart, so the right thing
561          * to do here is just abort if sc_oxfer is NULL, as everything else
562          * is cleaned up elsewhere.
563          */
564         if (sc->sc_oxfer == NULL)
565                 return;
566
567         s = spltty();
568
569         if (tp->t_state & TS_TBLOCK) {
570                 if (ISSET(sc->sc_mcr, SER_RTS) &&
571                     ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
572                         DPRINTF(("ucomstart: clear RTS\n"));
573                         (void)ucommodem(tp, 0, SER_RTS);
574                 }
575         } else {
576                 if (!ISSET(sc->sc_mcr, SER_RTS) &&
577                     tp->t_rawq.c_cc <= tp->t_ilowat &&
578                     ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
579                         DPRINTF(("ucomstart: set RTS\n"));
580                         (void)ucommodem(tp, SER_RTS, 0);
581                 }
582         }
583
584         if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
585                 ttwwakeup(tp);
586                 DPRINTF(("ucomstart: stopped\n"));
587                 goto out;
588         }
589
590         if (tp->t_outq.c_cc <= tp->t_olowat) {
591                 if (ISSET(tp->t_state, TS_SO_OLOWAT)) {
592                         CLR(tp->t_state, TS_SO_OLOWAT);
593                         wakeup(TSA_OLOWAT(tp));
594                 }
595                 selwakeuppri(&tp->t_wsel, TTIPRI);
596                 if (tp->t_outq.c_cc == 0) {
597                         if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
598                             TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
599                                 CLR(tp->t_state, TS_SO_OCOMPLETE);
600                                 wakeup(TSA_OCOMPLETE(tp));
601                         }
602                         goto out;
603                 }
604         }
605
606         /* Grab the first contiguous region of buffer space. */
607         data = tp->t_outq.c_cf;
608         cbp = (struct cblock *) ((intptr_t) tp->t_outq.c_cf & ~CROUND);
609         cnt = min((char *) (cbp+1) - tp->t_outq.c_cf, tp->t_outq.c_cc);
610
611         if (cnt == 0) {
612                 DPRINTF(("ucomstart: cnt == 0\n"));
613                 goto out;
614         }
615
616         SET(tp->t_state, TS_BUSY);
617
618         if (cnt > sc->sc_obufsize) {
619                 DPRINTF(("ucomstart: big buffer %d chars\n", cnt));
620                 cnt = sc->sc_obufsize;
621         }
622         if (sc->sc_callback->ucom_write != NULL)
623                 sc->sc_callback->ucom_write(sc->sc_parent, sc->sc_portno,
624                                             sc->sc_obuf, data, &cnt);
625         else
626                 memcpy(sc->sc_obuf, data, cnt);
627
628         DPRINTF(("ucomstart: %d chars\n", cnt));
629         usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe,
630                         (usbd_private_handle)sc, sc->sc_obuf, cnt,
631                         USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb);
632         /* What can we do on error? */
633         err = usbd_transfer(sc->sc_oxfer);
634         if (err != USBD_IN_PROGRESS)
635                 printf("ucomstart: err=%s\n", usbd_errstr(err));
636
637         ttwwakeup(tp);
638
639     out:
640         splx(s);
641 }
642
643 static void
644 ucomstop(struct tty *tp, int flag)
645 {
646         struct ucom_softc *sc;
647         int s;
648
649         sc = tp->t_sc;
650
651         DPRINTF(("ucomstop: %d\n", flag));
652
653         if ((flag & FREAD) && (sc->sc_state & UCS_RXSTOP) == 0) {
654                 DPRINTF(("ucomstop: read\n"));
655                 ucomstopread(sc);
656                 ucomstartread(sc);
657         }
658
659         if (flag & FWRITE) {
660                 DPRINTF(("ucomstop: write\n"));
661                 s = spltty();
662                 if (ISSET(tp->t_state, TS_BUSY)) {
663                         /* XXX do what? */
664                         if (!ISSET(tp->t_state, TS_TTSTOP))
665                                 SET(tp->t_state, TS_FLUSH);
666                 }
667                 splx(s);
668         }
669
670         ucomstart(tp);
671
672         DPRINTF(("ucomstop: done\n"));
673 }
674
675 static void
676 ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
677 {
678         struct ucom_softc *sc = (struct ucom_softc *)p;
679         struct tty *tp = sc->sc_tty;
680         u_int32_t cc;
681         int s;
682
683         DPRINTF(("ucomwritecb: status = %d\n", status));
684
685         if (status == USBD_CANCELLED || sc->sc_dying)
686                 goto error;
687
688         if (status != USBD_NORMAL_COMPLETION) {
689                 printf("%s: ucomwritecb: %s\n",
690                        device_get_nameunit(sc->sc_dev), usbd_errstr(status));
691                 if (status == USBD_STALLED)
692                         usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
693                 /* XXX we should restart after some delay. */
694                 goto error;
695         }
696
697         usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
698         DPRINTF(("ucomwritecb: cc = %d\n", cc));
699         if (cc <= sc->sc_opkthdrlen) {
700                 printf("%s: sent size too small, cc = %d\n",
701                        device_get_nameunit(sc->sc_dev), cc);
702                 goto error;
703         }
704
705         /* convert from USB bytes to tty bytes */
706         cc -= sc->sc_opkthdrlen;
707
708         s = spltty();
709         CLR(tp->t_state, TS_BUSY);
710         if (ISSET(tp->t_state, TS_FLUSH))
711                 CLR(tp->t_state, TS_FLUSH);
712         else
713                 ndflush(&tp->t_outq, cc);
714         ttyld_start(tp);
715         splx(s);
716
717         return;
718
719   error:
720         s = spltty();
721         CLR(tp->t_state, TS_BUSY);
722         splx(s);
723         return;
724 }
725
726 static usbd_status
727 ucomstartread(struct ucom_softc *sc)
728 {
729         usbd_status err;
730
731         DPRINTF(("ucomstartread: start\n"));
732
733         if (sc->sc_bulkin_pipe == NULL || (sc->sc_state & UCS_RXSTOP) == 0)
734                 return (USBD_NORMAL_COMPLETION);
735         sc->sc_state &= ~UCS_RXSTOP;
736
737         usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe,
738                         (usbd_private_handle)sc,
739                         sc->sc_ibuf, sc->sc_ibufsize,
740                         USBD_SHORT_XFER_OK | USBD_NO_COPY,
741                         USBD_NO_TIMEOUT, ucomreadcb);
742
743         err = usbd_transfer(sc->sc_ixfer);
744         if (err && err != USBD_IN_PROGRESS) {
745                 sc->sc_state |= UCS_RXSTOP;
746                 DPRINTF(("ucomstartread: err = %s\n", usbd_errstr(err)));
747                 return (err);
748         }
749
750         return (USBD_NORMAL_COMPLETION);
751 }
752
753 static void
754 ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
755 {
756         struct ucom_softc *sc = (struct ucom_softc *)p;
757         struct tty *tp = sc->sc_tty;
758         usbd_status err;
759         u_int32_t cc;
760         u_char *cp;
761         int lostcc;
762         int s;
763
764         DPRINTF(("ucomreadcb: status = %d\n", status));
765
766         if (status != USBD_NORMAL_COMPLETION) {
767                 if (!(sc->sc_state & UCS_RXSTOP))
768                         printf("%s: ucomreadcb: %s\n",
769                                device_get_nameunit(sc->sc_dev), usbd_errstr(status));
770                 sc->sc_state |= UCS_RXSTOP;
771                 if (status == USBD_STALLED)
772                         usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
773                 /* XXX we should restart after some delay. */
774                 return;
775         }
776         sc->sc_state |= UCS_RXSTOP;
777
778         usbd_get_xfer_status(xfer, NULL, (void **)&cp, &cc, NULL);
779         DPRINTF(("ucomreadcb: got %d chars, tp = %p\n", cc, tp));
780         if (cc == 0)
781                 goto resubmit;
782
783         if (sc->sc_callback->ucom_read != NULL)
784                 sc->sc_callback->ucom_read(sc->sc_parent, sc->sc_portno,
785                                            &cp, &cc);
786
787         if (cc > sc->sc_ibufsize) {
788                 printf("%s: invalid receive data size, %d chars\n",
789                        device_get_nameunit(sc->sc_dev), cc);
790                 goto resubmit;
791         }
792         if (cc < 1)
793                 goto resubmit;
794
795         s = spltty();
796         if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
797                 if (tp->t_rawq.c_cc + cc > tp->t_ihiwat
798                     && (sc->sc_state & UCS_RTS_IFLOW
799                         || tp->t_iflag & IXOFF)
800                     && !(tp->t_state & TS_TBLOCK))
801                         ttyblock(tp);
802                 lostcc = b_to_q((char *)cp, cc, &tp->t_rawq);
803                 tp->t_rawcc += cc;
804                 ttwakeup(tp);
805                 if (tp->t_state & TS_TTSTOP
806                     && (tp->t_iflag & IXANY
807                         || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
808                         tp->t_state &= ~TS_TTSTOP;
809                         tp->t_lflag &= ~FLUSHO;
810                         ucomstart(tp);
811                 }
812                 if (lostcc > 0)
813                         printf("%s: lost %d chars\n", device_get_nameunit(sc->sc_dev),
814                                lostcc);
815         } else {
816                 /* Give characters to tty layer. */
817                 while (cc > 0) {
818                         DPRINTFN(7, ("ucomreadcb: char = 0x%02x\n", *cp));
819                         if (ttyld_rint(tp, *cp) == -1) {
820                                 /* XXX what should we do? */
821                                 printf("%s: lost %d chars\n",
822                                        device_get_nameunit(sc->sc_dev), cc);
823                                 break;
824                         }
825                         cc--;
826                         cp++;
827                 }
828         }
829         splx(s);
830
831     resubmit:
832         err = ucomstartread(sc);
833         if (err) {
834                 printf("%s: read start failed\n", device_get_nameunit(sc->sc_dev));
835                 /* XXX what should we dow now? */
836         }
837
838         if ((sc->sc_state & UCS_RTS_IFLOW) && !ISSET(sc->sc_mcr, SER_RTS)
839             && !(tp->t_state & TS_TBLOCK))
840                 ucommodem(tp, SER_RTS, 0);
841 }
842
843 static void
844 ucom_cleanup(struct ucom_softc *sc)
845 {
846         DPRINTF(("ucom_cleanup: closing pipes\n"));
847
848         ucom_shutdown(sc);
849         if (sc->sc_bulkin_pipe != NULL) {
850                 sc->sc_state |= UCS_RXSTOP;
851                 usbd_abort_pipe(sc->sc_bulkin_pipe);
852                 usbd_close_pipe(sc->sc_bulkin_pipe);
853                 sc->sc_bulkin_pipe = NULL;
854         }
855         if (sc->sc_bulkout_pipe != NULL) {
856                 usbd_abort_pipe(sc->sc_bulkout_pipe);
857                 usbd_close_pipe(sc->sc_bulkout_pipe);
858                 sc->sc_bulkout_pipe = NULL;
859         }
860         if (sc->sc_ixfer != NULL) {
861                 usbd_free_xfer(sc->sc_ixfer);
862                 sc->sc_ixfer = NULL;
863         }
864         if (sc->sc_oxfer != NULL) {
865                 usbd_free_xfer(sc->sc_oxfer);
866                 sc->sc_oxfer = NULL;
867         }
868 }
869
870 static void
871 ucomstopread(struct ucom_softc *sc)
872 {
873         usbd_status err;
874
875         DPRINTF(("ucomstopread: enter\n"));
876
877         if (!(sc->sc_state & UCS_RXSTOP)) {
878                 sc->sc_state |= UCS_RXSTOP;
879                 if (sc->sc_bulkin_pipe == NULL) {
880                         DPRINTF(("ucomstopread: bulkin pipe NULL\n"));
881                         return;
882                 }
883                 err = usbd_abort_pipe(sc->sc_bulkin_pipe);
884                 if (err) {
885                         DPRINTF(("ucomstopread: err = %s\n",
886                                  usbd_errstr(err)));
887                 }
888         }
889
890         DPRINTF(("ucomstopread: leave\n"));
891 }