Merge branch 'vendor/OPENSSL'
[dragonfly.git] / sys / dev / usbmisc / ucom / ucom.c
1 /*
2  * (MPSAFE)
3  *
4  * $NetBSD: ucom.c,v 1.39 2001/08/16 22:31:24 augustss Exp $
5  * $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $
6  * $FreeBSD: src/sys/dev/usb/ucom.c,v 1.35 2003/11/16 11:58:21 akiyama Exp $
7  */
8 /*-
9  * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 /*
35  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
36  * All rights reserved.
37  *
38  * This code is derived from software contributed to The NetBSD Foundation
39  * by Lennart Augustsson (lennart@augustsson.net) at
40  * Carlstedt Research & Technology.
41  *
42  * Redistribution and use in source and binary forms, with or without
43  * modification, are permitted provided that the following conditions
44  * are met:
45  * 1. Redistributions of source code must retain the above copyright
46  *    notice, this list of conditions and the following disclaimer.
47  * 2. Redistributions in binary form must reproduce the above copyright
48  *    notice, this list of conditions and the following disclaimer in the
49  *    documentation and/or other materials provided with the distribution.
50  * 3. All advertising materials mentioning features or use of this software
51  *    must display the following acknowledgement:
52  *        This product includes software developed by the NetBSD
53  *        Foundation, Inc. and its contributors.
54  * 4. Neither the name of The NetBSD Foundation nor the names of its
55  *    contributors may be used to endorse or promote products derived
56  *    from this software without specific prior written permission.
57  *
58  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
59  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
60  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
61  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
62  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
63  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
64  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
65  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
66  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
67  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
68  * POSSIBILITY OF SUCH DAMAGE.
69  */
70
71 #include <sys/param.h>
72 #include <sys/systm.h>
73 #include <sys/kernel.h>
74 #include <sys/malloc.h>
75 #include <sys/bus.h>
76 #include <sys/fcntl.h>
77 #include <sys/conf.h>
78 #include <sys/tty.h>
79 #include <sys/clist.h>
80 #include <sys/file.h>
81 #include <sys/proc.h>
82 #include <sys/priv.h>
83 #include <sys/sysctl.h>
84 #include <sys/thread2.h>
85
86 #include <bus/usb/usb.h>
87 #include <bus/usb/usbcdc.h>
88
89 #include <bus/usb/usbdi.h>
90 #include <bus/usb/usbdi_util.h>
91 #include <bus/usb/usb_quirks.h>
92
93 #include "ucomvar.h"
94
95 #ifdef USB_DEBUG
96 static int      ucomdebug = 0;
97 SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
98 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
99            &ucomdebug, 0, "ucom debug level");
100 #define DPRINTF(x)      do { \
101                                 if (ucomdebug) \
102                                         kprintf x; \
103                         } while (0)
104
105 #define DPRINTFN(n, x)  do { \
106                                 if (ucomdebug > (n)) \
107                                         kprintf x; \
108                         } while (0)
109 #else
110 #define DPRINTF(x)
111 #define DPRINTFN(n, x)
112 #endif
113
114 static d_open_t  ucomopen;
115 static d_close_t ucomclose;
116 static d_read_t  ucomread;
117 static d_write_t ucomwrite;
118 static d_ioctl_t ucomioctl;
119
120 static struct dev_ops ucom_ops = {
121         { "ucom", 0, D_TTY },
122         .d_open =       ucomopen,
123         .d_close =      ucomclose,
124         .d_read =       ucomread,
125         .d_write =      ucomwrite,
126         .d_ioctl =      ucomioctl,
127         .d_kqfilter =   ttykqfilter,
128         .d_revoke =     ttyrevoke
129 };
130
131 static void ucom_cleanup(struct ucom_softc *);
132 static int ucomctl(struct ucom_softc *, int, int);
133 static int ucomparam(struct tty *, struct termios *);
134 static void ucomstart(struct tty *);
135 static void ucomstop(struct tty *, int);
136 static void ucom_shutdown(struct ucom_softc *);
137 static void ucom_dtr(struct ucom_softc *, int);
138 static void ucom_rts(struct ucom_softc *, int);
139 static void ucom_break(struct ucom_softc *, int);
140 static usbd_status ucomstartread(struct ucom_softc *);
141 static void ucomreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status);
142 static void ucomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status);
143 static void ucomstopread(struct ucom_softc *);
144 static void disc_optim(struct tty *, struct termios *, struct ucom_softc *);
145
146 devclass_t ucom_devclass;
147
148 static moduledata_t ucom_mod = {
149         "ucom",
150         NULL,
151         NULL
152 };
153
154 DECLARE_MODULE(ucom, ucom_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
155 MODULE_DEPEND(ucom, usb, 1, 1, 1);
156 MODULE_VERSION(ucom, UCOM_MODVER);
157
158 int
159 ucom_attach(struct ucom_softc *sc)
160 {
161         struct tty *tp;
162         int unit;
163         cdev_t dev;
164
165         lwkt_gettoken(&tty_token);
166         unit = device_get_unit(sc->sc_dev);
167
168         sc->sc_tty = tp = ttymalloc(sc->sc_tty);
169         tp->t_oproc = ucomstart;
170         tp->t_param = ucomparam;
171         tp->t_stop = ucomstop;
172
173         DPRINTF(("ucom_attach: tty_attach tp = %p\n", tp));
174
175         DPRINTF(("ucom_attach: make_dev: ucom%d\n", unit));
176
177         dev = make_dev(&ucom_ops, unit | UCOM_CALLOUT_MASK,
178                         UID_UUCP, GID_DIALER, 0660,
179                         "ucom%d", unit);
180         dev->si_tty = tp;
181         sc->dev = dev;
182         lwkt_reltoken(&tty_token);
183
184         return (0);
185 }
186
187 int
188 ucom_detach(struct ucom_softc *sc)
189 {
190         struct tty *tp = sc->sc_tty;
191         int unit;
192
193         DPRINTF(("ucom_detach: sc = %p, tp = %p\n", sc, sc->sc_tty));
194
195         destroy_dev(sc->dev);
196         lwkt_gettoken(&tty_token);
197         sc->sc_dying = 1;
198
199         if (sc->sc_bulkin_pipe != NULL)
200                 usbd_abort_pipe(sc->sc_bulkin_pipe);
201         if (sc->sc_bulkout_pipe != NULL)
202                 usbd_abort_pipe(sc->sc_bulkout_pipe);
203
204         if (tp != NULL) {
205                 if (tp->t_state & TS_ISOPEN) {
206                         device_printf(sc->sc_dev,
207                                       "still open, forcing close\n");
208                         (*linesw[tp->t_line].l_close)(tp, 0);
209                         ttyclose(tp);
210                 }
211         } else {
212                 DPRINTF(("ucom_detach: no tty\n"));
213                 lwkt_reltoken(&tty_token);
214                 return (0);
215         }
216
217         crit_enter();
218         if (--sc->sc_refcnt >= 0) {
219                 /* Wait for processes to go away. */
220                 usb_detach_wait(sc->sc_dev);
221         }
222         crit_exit();
223
224         unit = device_get_unit(sc->sc_dev);
225         dev_ops_remove_minor(&ucom_ops, /*UCOMUNIT_MASK, */unit);
226
227         lwkt_reltoken(&tty_token);
228         return (0);
229 }
230
231 static void
232 ucom_shutdown(struct ucom_softc *sc)
233 {
234         struct tty *tp = sc->sc_tty;
235
236         lwkt_gettoken(&tty_token);
237         DPRINTF(("ucom_shutdown\n"));
238         /*
239          * Hang up if necessary.  Wait a bit, so the other side has time to
240          * notice even if we immediately open the port again.
241          */
242         if (ISSET(tp->t_cflag, HUPCL)) {
243                 (void)ucomctl(sc, TIOCM_DTR, DMBIC);
244                 (void)tsleep(sc, 0, "ucomsd", hz);
245         }
246         lwkt_reltoken(&tty_token);
247 }
248
249 static int
250 ucomopen(struct dev_open_args *ap)
251 {
252         cdev_t dev = ap->a_head.a_dev;
253         int unit = UCOMUNIT(dev);
254         struct ucom_softc *sc;
255         usbd_status err;
256         struct tty *tp;
257         int error;
258
259         sc = devclass_get_softc(ucom_devclass, unit);
260         if (sc == NULL)
261                 return (ENXIO);
262
263         if (sc->sc_dying)
264                 return (ENXIO);
265
266         lwkt_gettoken(&tty_token);
267         tp = sc->sc_tty;
268
269         DPRINTF(("%s: ucomopen: tp = %p\n", device_get_nameunit(sc->sc_dev), tp));
270
271         if (ISSET(tp->t_state, TS_ISOPEN) &&
272             ISSET(tp->t_state, TS_XCLUDE) &&
273             priv_check_cred(ap->a_cred, PRIV_ROOT, 0)
274         ) {
275                 lwkt_reltoken(&tty_token);
276                 return (EBUSY);
277         }
278
279         /*
280          * Do the following iff this is a first open.
281          */
282         crit_enter();
283         while (sc->sc_opening)
284                 tsleep(&sc->sc_opening, 0, "ucomop", 0);
285         sc->sc_opening = 1;
286
287         if (!ISSET(tp->t_state, TS_ISOPEN)) {
288                 struct termios t;
289
290                 sc->sc_poll = 0;
291                 sc->sc_lsr = sc->sc_msr = sc->sc_mcr = 0;
292
293                 tp->t_dev = reference_dev(dev);
294
295                 /*
296                  * Initialize the termios status to the defaults.  Add in the
297                  * sticky bits from TIOCSFLAGS.
298                  */
299                 t.c_ispeed = 0;
300                 t.c_ospeed = TTYDEF_SPEED;
301                 t.c_cflag = TTYDEF_CFLAG;
302                 /* Make sure ucomparam() will do something. */
303                 tp->t_ospeed = 0;
304                 (void)ucomparam(tp, &t);
305                 tp->t_iflag = TTYDEF_IFLAG;
306                 tp->t_oflag = TTYDEF_OFLAG;
307                 tp->t_lflag = TTYDEF_LFLAG;
308                 ttychars(tp);
309                 ttsetwater(tp);
310
311                 /*
312                  * Turn on DTR.  We must always do this, even if carrier is not
313                  * present, because otherwise we'd have to use TIOCSDTR
314                  * immediately after setting CLOCAL, which applications do not
315                  * expect.  We always assert DTR while the device is open
316                  * unless explicitly requested to deassert it.
317                  */
318                 (void)ucomctl(sc, TIOCM_DTR | TIOCM_RTS, DMBIS);
319
320                 /* Device specific open */
321                 if (sc->sc_callback->ucom_open != NULL) {
322                         error = sc->sc_callback->ucom_open(sc->sc_parent,
323                                                            sc->sc_portno);
324                         if (error) {
325                                 ucom_cleanup(sc);
326                                 sc->sc_opening = 0;
327                                 wakeup(&sc->sc_opening);
328                                 crit_exit();
329                                 lwkt_reltoken(&tty_token);
330                                 return (error);
331                         }
332                 }
333
334                 DPRINTF(("ucomopen: open pipes in = %d out = %d\n",
335                          sc->sc_bulkin_no, sc->sc_bulkout_no));
336
337                 /* Open the bulk pipes */
338                 /* Bulk-in pipe */
339                 err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no, 0,
340                                      &sc->sc_bulkin_pipe);
341                 if (err) {
342                         kprintf("%s: open bulk in error (addr %d): %s\n",
343                                device_get_nameunit(sc->sc_dev), sc->sc_bulkin_no,
344                                usbd_errstr(err));
345                         error = EIO;
346                         goto fail_0;
347                 }
348                 /* Bulk-out pipe */
349                 err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
350                                      USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
351                 if (err) {
352                         kprintf("%s: open bulk out error (addr %d): %s\n",
353                                device_get_nameunit(sc->sc_dev), sc->sc_bulkout_no,
354                                usbd_errstr(err));
355                         error = EIO;
356                         goto fail_1;
357                 }
358
359                 /* Allocate a request and an input buffer and start reading. */
360                 sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
361                 if (sc->sc_ixfer == NULL) {
362                         error = ENOMEM;
363                         goto fail_2;
364                 }
365
366                 sc->sc_ibuf = usbd_alloc_buffer(sc->sc_ixfer,
367                                                 sc->sc_ibufsizepad);
368                 if (sc->sc_ibuf == NULL) {
369                         error = ENOMEM;
370                         goto fail_3;
371                 }
372
373                 sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
374                 if (sc->sc_oxfer == NULL) {
375                         error = ENOMEM;
376                         goto fail_3;
377                 }
378
379                 sc->sc_obuf = usbd_alloc_buffer(sc->sc_oxfer,
380                                                 sc->sc_obufsize +
381                                                 sc->sc_opkthdrlen);
382                 if (sc->sc_obuf == NULL) {
383                         error = ENOMEM;
384                         goto fail_4;
385                 }
386
387                 /*
388                  * Handle initial DCD.
389                  */
390                 if (ISSET(sc->sc_msr, UMSR_DCD) ||
391                     (minor(dev) & UCOM_CALLOUT_MASK))
392                         (*linesw[tp->t_line].l_modem)(tp, 1);
393
394                 ucomstartread(sc);
395         }
396
397         sc->sc_opening = 0;
398         wakeup(&sc->sc_opening);
399         crit_exit();
400
401         error = ttyopen(dev, tp);
402         if (error)
403                 goto bad;
404
405         error = (*linesw[tp->t_line].l_open)(dev, tp);
406         if (error)
407                 goto bad;
408
409         disc_optim(tp, &tp->t_termios, sc);
410
411         DPRINTF(("%s: ucomopen: success\n", device_get_nameunit(sc->sc_dev)));
412
413         sc->sc_poll = 1;
414         sc->sc_refcnt++;
415
416         lwkt_reltoken(&tty_token);
417         return (0);
418
419 fail_4:
420         usbd_free_xfer(sc->sc_oxfer);
421         sc->sc_oxfer = NULL;
422 fail_3:
423         usbd_free_xfer(sc->sc_ixfer);
424         sc->sc_ixfer = NULL;
425 fail_2:
426         usbd_close_pipe(sc->sc_bulkout_pipe);
427         sc->sc_bulkout_pipe = NULL;
428 fail_1:
429         usbd_close_pipe(sc->sc_bulkin_pipe);
430         sc->sc_bulkin_pipe = NULL;
431 fail_0:
432         sc->sc_opening = 0;
433         wakeup(&sc->sc_opening);
434         crit_exit();
435         lwkt_reltoken(&tty_token);
436         return (error);
437
438 bad:
439         if (!ISSET(tp->t_state, TS_ISOPEN)) {
440                 /*
441                  * We failed to open the device, and nobody else had it opened.
442                  * Clean up the state as appropriate.
443                  */
444                 ucom_cleanup(sc);
445         }
446
447         DPRINTF(("%s: ucomopen: failed\n", device_get_nameunit(sc->sc_dev)));
448
449         lwkt_reltoken(&tty_token);
450         return (error);
451 }
452
453 static int
454 ucomclose(struct dev_close_args *ap)
455 {
456         cdev_t dev = ap->a_head.a_dev;
457         struct ucom_softc *sc;
458         struct tty *tp;
459
460         sc = devclass_get_softc(ucom_devclass, UCOMUNIT(dev));
461
462         lwkt_gettoken(&tty_token);
463         tp = sc->sc_tty;
464
465         DPRINTF(("%s: ucomclose: unit = %d\n",
466                 device_get_nameunit(sc->sc_dev), UCOMUNIT(dev)));
467
468         if (!ISSET(tp->t_state, TS_ISOPEN))
469                 goto quit;
470
471         crit_enter();
472         (*linesw[tp->t_line].l_close)(tp, ap->a_fflag);
473         disc_optim(tp, &tp->t_termios, sc);
474         ttyclose(tp);
475         crit_exit();
476
477         if (sc->sc_dying)
478                 goto quit;
479
480         if (!ISSET(tp->t_state, TS_ISOPEN)) {
481                 /*
482                  * Although we got a last close, the device may still be in
483                  * use; e.g. if this was the dialout node, and there are still
484                  * processes waiting for carrier on the non-dialout node.
485                  */
486                 ucom_cleanup(sc);
487         }
488
489         if (sc->sc_callback->ucom_close != NULL)
490                 sc->sc_callback->ucom_close(sc->sc_parent, sc->sc_portno);
491
492 quit:
493         if (tp->t_dev) {
494                 release_dev(tp->t_dev);
495                 tp->t_dev = NULL;
496         }
497
498         if (--sc->sc_refcnt < 0)
499                 usb_detach_wakeup(sc->sc_dev);
500
501         lwkt_reltoken(&tty_token);
502         return (0);
503 }
504
505 static int
506 ucomread(struct dev_read_args *ap)
507 {
508         cdev_t dev = ap->a_head.a_dev;
509         struct ucom_softc *sc;
510         struct tty *tp;
511         int error;
512
513         sc = devclass_get_softc(ucom_devclass, UCOMUNIT(dev));
514         lwkt_gettoken(&tty_token);
515         tp = sc->sc_tty;
516
517         DPRINTF(("ucomread: tp = %p, flag = 0x%x\n", tp, ap->a_ioflag));
518
519         if (sc->sc_dying) {
520                 lwkt_reltoken(&tty_token);
521                 return (EIO);
522         }
523
524         error = (*linesw[tp->t_line].l_read)(tp, ap->a_uio, ap->a_ioflag);
525
526         DPRINTF(("ucomread: error = %d\n", error));
527
528         lwkt_reltoken(&tty_token);
529         return (error);
530 }
531
532 static int
533 ucomwrite(struct dev_write_args *ap)
534 {
535         cdev_t dev = ap->a_head.a_dev;
536         struct ucom_softc *sc;
537         struct tty *tp;
538         int error;
539
540         sc = devclass_get_softc(ucom_devclass, UCOMUNIT(dev));
541         lwkt_gettoken(&tty_token);
542         tp = sc->sc_tty;
543
544         DPRINTF(("ucomwrite: tp = %p, flag = 0x%x\n", tp, ap->a_ioflag));
545
546         if (sc->sc_dying) {
547                 lwkt_reltoken(&tty_token);
548                 return (EIO);
549         }
550
551         error = (*linesw[tp->t_line].l_write)(tp, ap->a_uio, ap->a_ioflag);
552
553         DPRINTF(("ucomwrite: error = %d\n", error));
554
555         lwkt_reltoken(&tty_token);
556         return (error);
557 }
558
559 static int
560 ucomioctl(struct dev_ioctl_args *ap)
561 {
562         cdev_t dev = ap->a_head.a_dev;
563         struct ucom_softc *sc;
564         struct tty *tp;
565         int error;
566         int d;
567
568         sc = devclass_get_softc(ucom_devclass, UCOMUNIT(dev));
569         lwkt_gettoken(&tty_token);
570         tp = sc->sc_tty;
571
572         if (sc->sc_dying) {
573                 lwkt_reltoken(&tty_token);
574                 return (EIO);
575         }
576
577         DPRINTF(("ucomioctl: cmd = 0x%08lx\n", ap->a_cmd));
578
579         error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data,
580                                               ap->a_fflag, ap->a_cred);
581         if (error != ENOIOCTL) {
582                 DPRINTF(("ucomioctl: l_ioctl: error = %d\n", error));
583                 lwkt_reltoken(&tty_token);
584                 return (error);
585         }
586
587         crit_enter();
588
589         error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag);
590         disc_optim(tp, &tp->t_termios, sc);
591         if (error != ENOIOCTL) {
592                 crit_exit();
593                 DPRINTF(("ucomioctl: ttioctl: error = %d\n", error));
594                 lwkt_reltoken(&tty_token);
595                 return (error);
596         }
597
598         if (sc->sc_callback->ucom_ioctl != NULL) {
599                 error = sc->sc_callback->ucom_ioctl(sc->sc_parent,
600                                                     sc->sc_portno,
601                                                     ap->a_cmd, ap->a_data,
602                                                     ap->a_fflag, curthread);
603                 if (error >= 0) {
604                         crit_exit();
605                         lwkt_reltoken(&tty_token);
606                         return (error);
607                 }
608         }
609
610         error = 0;
611
612         DPRINTF(("ucomioctl: our cmd = 0x%08lx\n", ap->a_cmd));
613
614         switch (ap->a_cmd) {
615         case TIOCSBRK:
616                 DPRINTF(("ucomioctl: TIOCSBRK\n"));
617                 ucom_break(sc, 1);
618                 break;
619         case TIOCCBRK:
620                 DPRINTF(("ucomioctl: TIOCCBRK\n"));
621                 ucom_break(sc, 0);
622                 break;
623
624         case TIOCSDTR:
625                 DPRINTF(("ucomioctl: TIOCSDTR\n"));
626                 (void)ucomctl(sc, TIOCM_DTR, DMBIS);
627                 break;
628         case TIOCCDTR:
629                 DPRINTF(("ucomioctl: TIOCCDTR\n"));
630                 (void)ucomctl(sc, TIOCM_DTR, DMBIC);
631                 break;
632
633         case TIOCMSET:
634                 d = *(int *)ap->a_data;
635                 DPRINTF(("ucomioctl: TIOCMSET, 0x%x\n", d));
636                 (void)ucomctl(sc, d, DMSET);
637                 break;
638         case TIOCMBIS:
639                 d = *(int *)ap->a_data;
640                 DPRINTF(("ucomioctl: TIOCMBIS, 0x%x\n", d));
641                 (void)ucomctl(sc, d, DMBIS);
642                 break;
643         case TIOCMBIC:
644                 d = *(int *)ap->a_data;
645                 DPRINTF(("ucomioctl: TIOCMBIC, 0x%x\n", d));
646                 (void)ucomctl(sc, d, DMBIC);
647                 break;
648         case TIOCMGET:
649                 d = ucomctl(sc, 0, DMGET);
650                 DPRINTF(("ucomioctl: TIOCMGET, 0x%x\n", d));
651                 *(int *)ap->a_data = d;
652                 break;
653
654         default:
655                 DPRINTF(("ucomioctl: error: our cmd = 0x%08lx\n", ap->a_cmd));
656                 error = ENOTTY;
657                 break;
658         }
659
660         crit_exit();
661
662         lwkt_reltoken(&tty_token);
663         return (error);
664 }
665
666 /*
667  * NOTE: Must be called with tty_token held.
668  */
669 static int
670 ucomctl(struct ucom_softc *sc, int bits, int how)
671 {
672         int     mcr;
673         int     msr;
674         int     onoff;
675
676         ASSERT_LWKT_TOKEN_HELD(&tty_token);
677         DPRINTF(("ucomctl: bits = 0x%x, how = %d\n", bits, how));
678
679         if (how == DMGET) {
680                 SET(bits, TIOCM_LE);            /* always set TIOCM_LE bit */
681                 DPRINTF(("ucomctl: DMGET: LE"));
682
683                 mcr = sc->sc_mcr;
684                 if (ISSET(mcr, UMCR_DTR)) {
685                         SET(bits, TIOCM_DTR);
686                         DPRINTF((" DTR"));
687                 }
688                 if (ISSET(mcr, UMCR_RTS)) {
689                         SET(bits, TIOCM_RTS);
690                         DPRINTF((" RTS"));
691                 }
692
693                 msr = sc->sc_msr;
694                 if (ISSET(msr, UMSR_CTS)) {
695                         SET(bits, TIOCM_CTS);
696                         DPRINTF((" CTS"));
697                 }
698                 if (ISSET(msr, UMSR_DCD)) {
699                         SET(bits, TIOCM_CD);
700                         DPRINTF((" CD"));
701                 }
702                 if (ISSET(msr, UMSR_DSR)) {
703                         SET(bits, TIOCM_DSR);
704                         DPRINTF((" DSR"));
705                 }
706                 if (ISSET(msr, UMSR_RI)) {
707                         SET(bits, TIOCM_RI);
708                         DPRINTF((" RI"));
709                 }
710
711                 DPRINTF(("\n"));
712
713                 return (bits);
714         }
715
716         mcr = 0;
717         if (ISSET(bits, TIOCM_DTR))
718                 SET(mcr, UMCR_DTR);
719         if (ISSET(bits, TIOCM_RTS))
720                 SET(mcr, UMCR_RTS);
721
722         switch (how) {
723         case DMSET:
724                 sc->sc_mcr = mcr;
725                 break;
726         case DMBIS:
727                 sc->sc_mcr |= mcr;
728                 break;
729         case DMBIC:
730                 sc->sc_mcr &= ~mcr;
731                 break;
732         }
733
734         onoff = ISSET(sc->sc_mcr, UMCR_DTR) ? 1 : 0;
735         ucom_dtr(sc, onoff);
736
737         onoff = ISSET(sc->sc_mcr, UMCR_RTS) ? 1 : 0;
738         ucom_rts(sc, onoff);
739
740         return (0);
741 }
742
743 /*
744  * NOTE: Must be called with tty_token held.
745  */
746 static void
747 ucom_break(struct ucom_softc *sc, int onoff)
748 {
749         ASSERT_LWKT_TOKEN_HELD(&tty_token);
750         DPRINTF(("ucom_break: onoff = %d\n", onoff));
751
752         if (sc->sc_callback->ucom_set == NULL)
753                 return;
754         sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
755                                   UCOM_SET_BREAK, onoff);
756 }
757
758 /*
759  * NOTE: Must be called with tty_token held.
760  */
761 static void
762 ucom_dtr(struct ucom_softc *sc, int onoff)
763 {
764         ASSERT_LWKT_TOKEN_HELD(&tty_token);
765         DPRINTF(("ucom_dtr: onoff = %d\n", onoff));
766
767         if (sc->sc_callback->ucom_set == NULL)
768                 return;
769         sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
770                                   UCOM_SET_DTR, onoff);
771 }
772
773 /*
774  * NOTE: Must be called with tty_token held.
775  */
776 static void
777 ucom_rts(struct ucom_softc *sc, int onoff)
778 {
779         ASSERT_LWKT_TOKEN_HELD(&tty_token);
780         DPRINTF(("ucom_rts: onoff = %d\n", onoff));
781
782         if (sc->sc_callback->ucom_set == NULL)
783                 return;
784         sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
785                                   UCOM_SET_RTS, onoff);
786 }
787
788 void
789 ucom_status_change(struct ucom_softc *sc)
790 {
791         struct tty *tp = sc->sc_tty;
792         u_char old_msr;
793         int onoff;
794
795         lwkt_gettoken(&tty_token);
796         if (sc->sc_callback->ucom_get_status == NULL) {
797                 sc->sc_lsr = 0;
798                 sc->sc_msr = 0;
799                 lwkt_reltoken(&tty_token);
800                 return;
801         }
802
803         old_msr = sc->sc_msr;
804         sc->sc_callback->ucom_get_status(sc->sc_parent, sc->sc_portno,
805                                          &sc->sc_lsr, &sc->sc_msr);
806         if (ISSET((sc->sc_msr ^ old_msr), UMSR_DCD)) {
807                 if (sc->sc_poll == 0) {
808                         lwkt_reltoken(&tty_token);
809                         return;
810                 }
811                 onoff = ISSET(sc->sc_msr, UMSR_DCD) ? 1 : 0;
812                 DPRINTF(("ucom_status_change: DCD changed to %d\n", onoff));
813                 (*linesw[tp->t_line].l_modem)(tp, onoff);
814         }
815         lwkt_reltoken(&tty_token);
816 }
817
818 static int
819 ucomparam(struct tty *tp, struct termios *t)
820 {
821         struct ucom_softc *sc;
822         int error;
823         usbd_status uerr;
824
825         sc = devclass_get_softc(ucom_devclass, UCOMUNIT(tp->t_dev));
826
827         lwkt_gettoken(&tty_token);
828         if (sc->sc_dying) {
829                 lwkt_reltoken(&tty_token);
830                 return (EIO);
831         }
832
833         DPRINTF(("ucomparam: sc = %p\n", sc));
834
835         /* Check requested parameters. */
836         if (t->c_ospeed < 0) {
837                 DPRINTF(("ucomparam: negative ospeed\n"));
838                 lwkt_reltoken(&tty_token);
839                 return (EINVAL);
840         }
841         if (t->c_ispeed && t->c_ispeed != t->c_ospeed) {
842                 DPRINTF(("ucomparam: mismatch ispeed and ospeed\n"));
843                 lwkt_reltoken(&tty_token);
844                 return (EINVAL);
845         }
846
847         /*
848          * If there were no changes, don't do anything.  This avoids dropping
849          * input and improves performance when all we did was frob things like
850          * VMIN and VTIME.
851          */
852         if (tp->t_ospeed == t->c_ospeed &&
853             tp->t_cflag == t->c_cflag) {
854                 lwkt_reltoken(&tty_token);
855                 return (0);
856         }
857
858         /* And copy to tty. */
859         tp->t_ispeed = 0;
860         tp->t_ospeed = t->c_ospeed;
861         tp->t_cflag = t->c_cflag;
862
863         if (sc->sc_callback->ucom_param == NULL) {
864                 lwkt_reltoken(&tty_token);
865                 return (0);
866         }
867
868         ucomstopread(sc);
869
870         error = sc->sc_callback->ucom_param(sc->sc_parent, sc->sc_portno, t);
871         if (error) {
872                 DPRINTF(("ucomparam: callback: error = %d\n", error));
873                 lwkt_reltoken(&tty_token);
874                 return (error);
875         }
876
877         ttsetwater(tp);
878
879         if (t->c_cflag & CRTS_IFLOW) {
880                 sc->sc_state |= UCS_RTS_IFLOW;
881         } else if (sc->sc_state & UCS_RTS_IFLOW) {
882                 sc->sc_state &= ~UCS_RTS_IFLOW;
883                 (void)ucomctl(sc, UMCR_RTS, DMBIS);
884         }
885
886         disc_optim(tp, t, sc);
887
888         uerr = ucomstartread(sc);
889         if (uerr != USBD_NORMAL_COMPLETION) {
890                 lwkt_reltoken(&tty_token);
891                 return (EIO);
892         }
893
894         lwkt_reltoken(&tty_token);
895         return (0);
896 }
897
898 static void
899 ucomstart(struct tty *tp)
900 {
901         struct ucom_softc *sc;
902         struct cblock *cbp;
903         usbd_status err;
904         u_char *data;
905         int cnt;
906
907         sc = devclass_get_softc(ucom_devclass, UCOMUNIT(tp->t_dev));
908         DPRINTF(("ucomstart: sc = %p\n", sc));
909
910         lwkt_gettoken(&tty_token);
911
912         if (sc->sc_dying) {
913                 lwkt_reltoken(&tty_token);
914                 return;
915         }
916
917         crit_enter();
918
919         if (tp->t_state & TS_TBLOCK) {
920                 if (ISSET(sc->sc_mcr, UMCR_RTS) &&
921                     ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
922                         DPRINTF(("ucomstart: clear RTS\n"));
923                         (void)ucomctl(sc, UMCR_RTS, DMBIC);
924                 }
925         } else {
926                 if (!ISSET(sc->sc_mcr, UMCR_RTS) &&
927                     tp->t_rawq.c_cc <= tp->t_ilowat &&
928                     ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
929                         DPRINTF(("ucomstart: set RTS\n"));
930                         (void)ucomctl(sc, UMCR_RTS, DMBIS);
931                 }
932         }
933
934         if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
935                 ttwwakeup(tp);
936                 DPRINTF(("ucomstart: stopped\n"));
937                 goto out;
938         }
939
940         if (tp->t_outq.c_cc <= tp->t_olowat) {
941                 if (ISSET(tp->t_state, TS_SO_OLOWAT)) {
942                         CLR(tp->t_state, TS_SO_OLOWAT);
943                         wakeup(TSA_OLOWAT(tp));
944                 }
945                 KNOTE(&tp->t_wkq.ki_note, 0);
946                 if (tp->t_outq.c_cc == 0) {
947                         if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
948                             TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
949                                 CLR(tp->t_state, TS_SO_OCOMPLETE);
950                                 wakeup(TSA_OCOMPLETE(tp));
951                         }
952                         goto out;
953                 }
954         }
955
956         /* Grab the first contiguous region of buffer space. */
957         data = tp->t_outq.c_cf;
958         cbp = (struct cblock *) ((intptr_t) tp->t_outq.c_cf & ~CROUND);
959         cnt = min((char *) (cbp+1) - tp->t_outq.c_cf, tp->t_outq.c_cc);
960
961         if (cnt == 0) {
962                 DPRINTF(("ucomstart: cnt == 0\n"));
963                 goto out;
964         }
965
966         SET(tp->t_state, TS_BUSY);
967
968         if (cnt > sc->sc_obufsize) {
969                 DPRINTF(("ucomstart: big buffer %d chars\n", cnt));
970                 cnt = sc->sc_obufsize;
971         }
972         if (sc->sc_callback->ucom_write != NULL)
973                 sc->sc_callback->ucom_write(sc->sc_parent, sc->sc_portno,
974                                             sc->sc_obuf, data, &cnt);
975         else
976                 memcpy(sc->sc_obuf, data, cnt);
977
978         DPRINTF(("ucomstart: %d chars\n", cnt));
979         usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe,
980                         (usbd_private_handle)sc, sc->sc_obuf, cnt,
981                         USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb);
982         /* What can we do on error? */
983         err = usbd_transfer(sc->sc_oxfer);
984         if (err != USBD_IN_PROGRESS)
985                 kprintf("ucomstart: err=%s\n", usbd_errstr(err));
986
987         ttwwakeup(tp);
988
989     out:
990         crit_exit();
991         lwkt_reltoken(&tty_token);
992 }
993
994 static void
995 ucomstop(struct tty *tp, int flag)
996 {
997         struct ucom_softc *sc;
998
999         sc = devclass_get_softc(ucom_devclass, UCOMUNIT(tp->t_dev));
1000
1001         DPRINTF(("ucomstop: %d\n", flag));
1002
1003         lwkt_gettoken(&tty_token);
1004         if (flag & FREAD) {
1005                 /*
1006                  * This is just supposed to flush pending receive data,
1007                  * not stop the reception of data entirely!
1008                  */
1009                 DPRINTF(("ucomstop: read\n"));
1010                 ucomstopread(sc);
1011                 ucomstartread(sc);
1012         }
1013
1014         if (flag & FWRITE) {
1015                 DPRINTF(("ucomstop: write\n"));
1016                 crit_enter();
1017                 if (ISSET(tp->t_state, TS_BUSY)) {
1018                         /* XXX do what? */
1019                         if (!ISSET(tp->t_state, TS_TTSTOP))
1020                                 SET(tp->t_state, TS_FLUSH);
1021                 }
1022                 crit_exit();
1023         }
1024
1025         DPRINTF(("ucomstop: done\n"));
1026         lwkt_reltoken(&tty_token);
1027 }
1028
1029 static void
1030 ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
1031 {
1032         struct ucom_softc *sc = (struct ucom_softc *)p;
1033         struct tty *tp = sc->sc_tty;
1034         u_int32_t cc;
1035
1036         lwkt_gettoken(&tty_token);
1037         DPRINTF(("ucomwritecb: status = %d\n", status));
1038
1039         if (status == USBD_CANCELLED || sc->sc_dying)
1040                 goto error;
1041
1042         if (status != USBD_NORMAL_COMPLETION) {
1043                 kprintf("%s: ucomwritecb: %s\n",
1044                        device_get_nameunit(sc->sc_dev), usbd_errstr(status));
1045                 if (status == USBD_STALLED)
1046                         usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
1047                 /*
1048                  * XXX.  We may need a flag to sequence ucomstopread() and
1049                  * ucomstartread() to handle the case where ucomstartread()
1050                  * is called after ucomstopread() but before the request has
1051                  * been properly canceled?
1052                  */
1053                 goto error;
1054         }
1055
1056         usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
1057         DPRINTF(("ucomwritecb: cc = %d\n", cc));
1058         if (cc <= sc->sc_opkthdrlen) {
1059                 kprintf("%s: sent size too small, cc = %d\n",
1060                         device_get_nameunit(sc->sc_dev), cc);
1061                 goto error;
1062         }
1063
1064         /* convert from USB bytes to tty bytes */
1065         cc -= sc->sc_opkthdrlen;
1066
1067         crit_enter();
1068         CLR(tp->t_state, TS_BUSY);
1069         if (ISSET(tp->t_state, TS_FLUSH))
1070                 CLR(tp->t_state, TS_FLUSH);
1071         else
1072                 ndflush(&tp->t_outq, cc);
1073         (*linesw[tp->t_line].l_start)(tp);
1074         crit_exit();
1075
1076         lwkt_reltoken(&tty_token);
1077         return;
1078
1079   error:
1080         crit_enter();
1081         CLR(tp->t_state, TS_BUSY);
1082         crit_exit();
1083         lwkt_reltoken(&tty_token);
1084         return;
1085 }
1086
1087 /*
1088  * NOTE: Must be called with tty_token held
1089  */
1090 static usbd_status
1091 ucomstartread(struct ucom_softc *sc)
1092 {
1093         usbd_status err;
1094
1095         ASSERT_LWKT_TOKEN_HELD(&tty_token);
1096         DPRINTF(("ucomstartread: start\n"));
1097
1098         sc->sc_state &= ~UCS_RXSTOP;
1099
1100         if (sc->sc_bulkin_pipe == NULL)
1101                 return (USBD_NORMAL_COMPLETION);
1102
1103         usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe,
1104                         (usbd_private_handle)sc,
1105                         sc->sc_ibuf, sc->sc_ibufsize,
1106                         USBD_SHORT_XFER_OK | USBD_NO_COPY,
1107                         USBD_NO_TIMEOUT, ucomreadcb);
1108
1109         err = usbd_transfer(sc->sc_ixfer);
1110         if (err != USBD_IN_PROGRESS) {
1111                 DPRINTF(("ucomstartread: err = %s\n", usbd_errstr(err)));
1112                 return (err);
1113         }
1114
1115         return (USBD_NORMAL_COMPLETION);
1116 }
1117
1118 static void
1119 ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
1120 {
1121         lwkt_gettoken(&tty_token);
1122
1123         struct ucom_softc *sc = (struct ucom_softc *)p;
1124         struct tty *tp = sc->sc_tty;
1125         int (*rint) (int c, struct tty *tp) = linesw[tp->t_line].l_rint;
1126         usbd_status err;
1127         u_int32_t cc;
1128         u_char *cp;
1129         int lostcc;
1130
1131         DPRINTF(("ucomreadcb: status = %d\n", status));
1132
1133         if (status != USBD_NORMAL_COMPLETION) {
1134                 if (!(sc->sc_state & UCS_RXSTOP))
1135                         kprintf("%s: ucomreadcb: %s\n",
1136                                device_get_nameunit(sc->sc_dev), usbd_errstr(status));
1137                 if (status == USBD_STALLED)
1138                         usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
1139                 /* XXX we should restart after some delay. */
1140                 lwkt_reltoken(&tty_token);
1141                 return;
1142         }
1143
1144         usbd_get_xfer_status(xfer, NULL, (void **)&cp, &cc, NULL);
1145         DPRINTF(("ucomreadcb: got %d chars, tp = %p\n", cc, tp));
1146         if (cc == 0)
1147                 goto resubmit;
1148
1149         if (sc->sc_callback->ucom_read != NULL) {
1150                 sc->sc_callback->ucom_read(sc->sc_parent, sc->sc_portno,
1151                                            &cp, &cc);
1152         }
1153
1154         if (cc > sc->sc_ibufsize) {
1155                 kprintf("%s: invalid receive data size, %d chars\n",
1156                         device_get_nameunit(sc->sc_dev), cc);
1157                 goto resubmit;
1158         }
1159         if (cc < 1)
1160                 goto resubmit;
1161
1162         crit_enter();
1163         if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
1164                 if (tp->t_rawq.c_cc + cc > tp->t_ihiwat
1165                     && (sc->sc_state & UCS_RTS_IFLOW
1166                         || tp->t_iflag & IXOFF)
1167                     && !(tp->t_state & TS_TBLOCK))
1168                         ttyblock(tp);
1169                 lostcc = b_to_q((char *)cp, cc, &tp->t_rawq);
1170                 tp->t_rawcc += cc;
1171                 if (sc->hotchar) {
1172                         while (cc) {
1173                                 if (*cp == sc->hotchar)
1174                                         break;
1175                                 --cc;
1176                                 ++cp;
1177                         }
1178                         if (cc)
1179                                 setsofttty();
1180                 }
1181                 ttwakeup(tp);
1182                 if (tp->t_state & TS_TTSTOP
1183                     && (tp->t_iflag & IXANY
1184                         || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
1185                         tp->t_state &= ~TS_TTSTOP;
1186                         tp->t_lflag &= ~FLUSHO;
1187                         ucomstart(tp);
1188                 }
1189                 if (lostcc > 0)
1190                         kprintf("%s: lost %d chars\n", device_get_nameunit(sc->sc_dev),
1191                                lostcc);
1192         } else {
1193                 /* Give characters to tty layer. */
1194                 while (cc > 0) {
1195                         DPRINTFN(7, ("ucomreadcb: char = 0x%02x\n", *cp));
1196                         if ((*rint)(*cp, tp) == -1) {
1197                                 /* XXX what should we do? */
1198                                 kprintf("%s: lost %d chars\n",
1199                                        device_get_nameunit(sc->sc_dev), cc);
1200                                 break;
1201                         }
1202                         cc--;
1203                         cp++;
1204                 }
1205         }
1206         crit_exit();
1207
1208 resubmit:
1209         err = ucomstartread(sc);
1210         if (err) {
1211                 kprintf("%s: read start failed\n", device_get_nameunit(sc->sc_dev));
1212                 /* XXX what should we dow now? */
1213         }
1214
1215         if ((sc->sc_state & UCS_RTS_IFLOW) && !ISSET(sc->sc_mcr, UMCR_RTS)
1216             && !(tp->t_state & TS_TBLOCK))
1217                 ucomctl(sc, UMCR_RTS, DMBIS);
1218
1219         lwkt_reltoken(&tty_token);
1220 }
1221
1222 /*
1223  * NOTE: Must be called with tty_token held.
1224  */
1225 static void
1226 ucom_cleanup(struct ucom_softc *sc)
1227 {
1228         ASSERT_LWKT_TOKEN_HELD(&tty_token);
1229         DPRINTF(("ucom_cleanup: closing pipes\n"));
1230
1231         ucom_shutdown(sc);
1232         if (sc->sc_bulkin_pipe != NULL) {
1233                 usbd_abort_pipe(sc->sc_bulkin_pipe);
1234                 usbd_close_pipe(sc->sc_bulkin_pipe);
1235                 sc->sc_bulkin_pipe = NULL;
1236         }
1237         if (sc->sc_bulkout_pipe != NULL) {
1238                 usbd_abort_pipe(sc->sc_bulkout_pipe);
1239                 usbd_close_pipe(sc->sc_bulkout_pipe);
1240                 sc->sc_bulkout_pipe = NULL;
1241         }
1242         if (sc->sc_ixfer != NULL) {
1243                 usbd_free_xfer(sc->sc_ixfer);
1244                 sc->sc_ixfer = NULL;
1245         }
1246         if (sc->sc_oxfer != NULL) {
1247                 usbd_free_xfer(sc->sc_oxfer);
1248                 sc->sc_oxfer = NULL;
1249         }
1250 }
1251
1252 /*
1253  * NOTE: Must be called with tty_token held.
1254  */
1255 static void
1256 ucomstopread(struct ucom_softc *sc)
1257 {
1258         usbd_status err;
1259
1260         ASSERT_LWKT_TOKEN_HELD(&tty_token);
1261         DPRINTF(("ucomstopread: enter\n"));
1262
1263         if (!(sc->sc_state & UCS_RXSTOP)) {
1264                 sc->sc_state |= UCS_RXSTOP;
1265                 if (sc->sc_bulkin_pipe == NULL) {
1266                         DPRINTF(("ucomstopread: bulkin pipe NULL\n"));
1267                         return;
1268                 }
1269                 err = usbd_abort_pipe(sc->sc_bulkin_pipe);
1270                 if (err) {
1271                         DPRINTF(("ucomstopread: err = %s\n",
1272                                  usbd_errstr(err)));
1273                 }
1274         }
1275
1276         DPRINTF(("ucomstopread: leave\n"));
1277 }
1278
1279 /*
1280  * NOTE: Must be called with tty_token held.
1281  */
1282 static void
1283 disc_optim(struct tty *tp, struct termios *t, struct ucom_softc *sc)
1284 {
1285         ASSERT_LWKT_TOKEN_HELD(&tty_token);
1286         if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
1287             && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
1288             && (!(t->c_iflag & PARMRK)
1289                 || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
1290             && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
1291             && linesw[tp->t_line].l_rint == ttyinput) {
1292                 DPRINTF(("disc_optim: bypass l_rint\n"));
1293                 tp->t_state |= TS_CAN_BYPASS_L_RINT;
1294         } else {
1295                 DPRINTF(("disc_optim: can't bypass l_rint\n"));
1296                 tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
1297         }
1298         sc->hotchar = linesw[tp->t_line].l_hotchar;
1299 }