Nuke SIMPLEQ_* and logprintf.
[dragonfly/vkernel-mp.git] / sys / dev / usbmisc / ucom / ucom.c
blob8499f4ef2c20b5270890893c228a6f43bfa75f69
1 /*
2 * $NetBSD: ucom.c,v 1.39 2001/08/16 22:31:24 augustss Exp $
3 * $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $
4 * $FreeBSD: src/sys/dev/usb/ucom.c,v 1.35 2003/11/16 11:58:21 akiyama Exp $
5 * $DragonFly: src/sys/dev/usbmisc/ucom/ucom.c,v 1.26 2007/06/28 13:55:12 hasso Exp $
6 */
7 /*-
8 * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
9 * All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
34 * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
35 * All rights reserved.
37 * This code is derived from software contributed to The NetBSD Foundation
38 * by Lennart Augustsson (lennart@augustsson.net) at
39 * Carlstedt Research & Technology.
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.
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.
70 #include <sys/param.h>
71 #include <sys/systm.h>
72 #include <sys/kernel.h>
73 #include <sys/malloc.h>
74 #include <sys/bus.h>
75 #include <sys/ioccom.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/select.h>
82 #include <sys/proc.h>
83 #include <sys/vnode.h>
84 #include <sys/poll.h>
85 #include <sys/sysctl.h>
86 #include <sys/thread2.h>
88 #include <bus/usb/usb.h>
89 #include <bus/usb/usbcdc.h>
91 #include <bus/usb/usbdi.h>
92 #include <bus/usb/usbdi_util.h>
93 #include <bus/usb/usbdevs.h>
94 #include <bus/usb/usb_quirks.h>
96 #include "ucomvar.h"
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 kprintf x; \
106 } while (0)
108 #define DPRINTFN(n, x) do { \
109 if (ucomdebug > (n)) \
110 kprintf x; \
111 } while (0)
112 #else
113 #define DPRINTF(x)
114 #define DPRINTFN(n, x)
115 #endif
117 static d_open_t ucomopen;
118 static d_close_t ucomclose;
119 static d_read_t ucomread;
120 static d_write_t ucomwrite;
121 static d_ioctl_t ucomioctl;
123 #define UCOM_CDEV_MAJOR 138
125 static struct dev_ops ucom_ops = {
126 { "ucom", UCOM_CDEV_MAJOR, D_TTY | D_KQFILTER },
127 .d_open = ucomopen,
128 .d_close = ucomclose,
129 .d_read = ucomread,
130 .d_write = ucomwrite,
131 .d_ioctl = ucomioctl,
132 .d_poll = ttypoll,
133 .d_kqfilter = ttykqfilter
136 static void ucom_cleanup(struct ucom_softc *);
137 static int ucomctl(struct ucom_softc *, int, int);
138 static int ucomparam(struct tty *, struct termios *);
139 static void ucomstart(struct tty *);
140 static void ucomstop(struct tty *, int);
141 static void ucom_shutdown(struct ucom_softc *);
142 static void ucom_dtr(struct ucom_softc *, int);
143 static void ucom_rts(struct ucom_softc *, int);
144 static void ucom_break(struct ucom_softc *, int);
145 static usbd_status ucomstartread(struct ucom_softc *);
146 static void ucomreadcb(usbd_xfer_handle, usbd_private_handle, usbd_status);
147 static void ucomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status);
148 static void ucomstopread(struct ucom_softc *);
149 static void disc_optim(struct tty *, struct termios *, struct ucom_softc *);
151 devclass_t ucom_devclass;
153 static moduledata_t ucom_mod = {
154 "ucom",
155 NULL,
156 NULL
159 DECLARE_MODULE(ucom, ucom_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
160 MODULE_DEPEND(ucom, usb, 1, 1, 1);
161 MODULE_VERSION(ucom, UCOM_MODVER);
164 ucom_attach(struct ucom_softc *sc)
166 struct tty *tp;
167 int unit;
168 cdev_t dev;
170 unit = device_get_unit(sc->sc_dev);
172 sc->sc_tty = tp = ttymalloc(sc->sc_tty);
173 tp->t_oproc = ucomstart;
174 tp->t_param = ucomparam;
175 tp->t_stop = ucomstop;
177 DPRINTF(("ucom_attach: tty_attach tp = %p\n", tp));
179 DPRINTF(("ucom_attach: make_dev: ucom%d\n", unit));
181 dev_ops_add(&ucom_ops, UCOMUNIT_MASK, unit);
182 dev = make_dev(&ucom_ops, unit | UCOM_CALLOUT_MASK,
183 UID_UUCP, GID_DIALER, 0660,
184 "ucom%d", unit);
185 dev->si_tty = tp;
187 return (0);
191 ucom_detach(struct ucom_softc *sc)
193 struct tty *tp = sc->sc_tty;
194 int unit;
196 DPRINTF(("ucom_detach: sc = %p, tp = %p\n", sc, sc->sc_tty));
198 sc->sc_dying = 1;
200 if (sc->sc_bulkin_pipe != NULL)
201 usbd_abort_pipe(sc->sc_bulkin_pipe);
202 if (sc->sc_bulkout_pipe != NULL)
203 usbd_abort_pipe(sc->sc_bulkout_pipe);
205 if (tp != NULL) {
206 if (tp->t_state & TS_ISOPEN) {
207 device_printf(sc->sc_dev,
208 "still open, forcing close\n");
209 (*linesw[tp->t_line].l_close)(tp, 0);
210 tp->t_gen++;
211 ttyclose(tp);
212 ttwakeup(tp);
213 ttwwakeup(tp);
215 } else {
216 DPRINTF(("ucom_detach: no tty\n"));
217 return (0);
220 crit_enter();
221 if (--sc->sc_refcnt >= 0) {
222 /* Wait for processes to go away. */
223 usb_detach_wait(sc->sc_dev);
225 crit_exit();
227 unit = device_get_unit(sc->sc_dev);
228 dev_ops_remove(&ucom_ops, UCOMUNIT_MASK, unit);
230 return (0);
233 static void
234 ucom_shutdown(struct ucom_softc *sc)
236 struct tty *tp = sc->sc_tty;
238 DPRINTF(("ucom_shutdown\n"));
240 * Hang up if necessary. Wait a bit, so the other side has time to
241 * notice even if we immediately open the port again.
243 if (ISSET(tp->t_cflag, HUPCL)) {
244 (void)ucomctl(sc, TIOCM_DTR, DMBIC);
245 (void)tsleep(sc, 0, "ucomsd", hz);
249 static int
250 ucomopen(struct dev_open_args *ap)
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;
259 USB_GET_SC_OPEN(ucom, unit, sc);
261 if (sc->sc_dying)
262 return (ENXIO);
264 tp = sc->sc_tty;
266 DPRINTF(("%s: ucomopen: tp = %p\n", device_get_nameunit(sc->sc_dev), tp));
268 if (ISSET(tp->t_state, TS_ISOPEN) &&
269 ISSET(tp->t_state, TS_XCLUDE) &&
270 suser_cred(ap->a_cred, 0)
272 return (EBUSY);
276 * Do the following iff this is a first open.
278 crit_enter();
279 while (sc->sc_opening)
280 tsleep(&sc->sc_opening, 0, "ucomop", 0);
281 sc->sc_opening = 1;
283 if (!ISSET(tp->t_state, TS_ISOPEN)) {
284 struct termios t;
286 sc->sc_poll = 0;
287 sc->sc_lsr = sc->sc_msr = sc->sc_mcr = 0;
289 tp->t_dev = reference_dev(dev);
292 * Initialize the termios status to the defaults. Add in the
293 * sticky bits from TIOCSFLAGS.
295 t.c_ispeed = 0;
296 t.c_ospeed = TTYDEF_SPEED;
297 t.c_cflag = TTYDEF_CFLAG;
298 /* Make sure ucomparam() will do something. */
299 tp->t_ospeed = 0;
300 (void)ucomparam(tp, &t);
301 tp->t_iflag = TTYDEF_IFLAG;
302 tp->t_oflag = TTYDEF_OFLAG;
303 tp->t_lflag = TTYDEF_LFLAG;
304 ttychars(tp);
305 ttsetwater(tp);
308 * Turn on DTR. We must always do this, even if carrier is not
309 * present, because otherwise we'd have to use TIOCSDTR
310 * immediately after setting CLOCAL, which applications do not
311 * expect. We always assert DTR while the device is open
312 * unless explicitly requested to deassert it.
314 (void)ucomctl(sc, TIOCM_DTR | TIOCM_RTS, DMBIS);
316 /* Device specific open */
317 if (sc->sc_callback->ucom_open != NULL) {
318 error = sc->sc_callback->ucom_open(sc->sc_parent,
319 sc->sc_portno);
320 if (error) {
321 ucom_cleanup(sc);
322 sc->sc_opening = 0;
323 wakeup(&sc->sc_opening);
324 crit_exit();
325 return (error);
329 DPRINTF(("ucomopen: open pipes in = %d out = %d\n",
330 sc->sc_bulkin_no, sc->sc_bulkout_no));
332 /* Open the bulk pipes */
333 /* Bulk-in pipe */
334 err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no, 0,
335 &sc->sc_bulkin_pipe);
336 if (err) {
337 kprintf("%s: open bulk in error (addr %d): %s\n",
338 device_get_nameunit(sc->sc_dev), sc->sc_bulkin_no,
339 usbd_errstr(err));
340 error = EIO;
341 goto fail_0;
343 /* Bulk-out pipe */
344 err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
345 USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
346 if (err) {
347 kprintf("%s: open bulk out error (addr %d): %s\n",
348 device_get_nameunit(sc->sc_dev), sc->sc_bulkout_no,
349 usbd_errstr(err));
350 error = EIO;
351 goto fail_1;
354 /* Allocate a request and an input buffer and start reading. */
355 sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
356 if (sc->sc_ixfer == NULL) {
357 error = ENOMEM;
358 goto fail_2;
361 sc->sc_ibuf = usbd_alloc_buffer(sc->sc_ixfer,
362 sc->sc_ibufsizepad);
363 if (sc->sc_ibuf == NULL) {
364 error = ENOMEM;
365 goto fail_3;
368 sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
369 if (sc->sc_oxfer == NULL) {
370 error = ENOMEM;
371 goto fail_3;
374 sc->sc_obuf = usbd_alloc_buffer(sc->sc_oxfer,
375 sc->sc_obufsize +
376 sc->sc_opkthdrlen);
377 if (sc->sc_obuf == NULL) {
378 error = ENOMEM;
379 goto fail_4;
383 * Handle initial DCD.
385 if (ISSET(sc->sc_msr, UMSR_DCD) ||
386 (minor(dev) & UCOM_CALLOUT_MASK))
387 (*linesw[tp->t_line].l_modem)(tp, 1);
389 ucomstartread(sc);
392 sc->sc_opening = 0;
393 wakeup(&sc->sc_opening);
394 crit_exit();
396 error = ttyopen(dev, tp);
397 if (error)
398 goto bad;
400 error = (*linesw[tp->t_line].l_open)(dev, tp);
401 if (error)
402 goto bad;
404 disc_optim(tp, &tp->t_termios, sc);
406 DPRINTF(("%s: ucomopen: success\n", device_get_nameunit(sc->sc_dev)));
408 sc->sc_poll = 1;
409 sc->sc_refcnt++;
411 return (0);
413 fail_4:
414 usbd_free_xfer(sc->sc_oxfer);
415 sc->sc_oxfer = NULL;
416 fail_3:
417 usbd_free_xfer(sc->sc_ixfer);
418 sc->sc_ixfer = NULL;
419 fail_2:
420 usbd_close_pipe(sc->sc_bulkout_pipe);
421 sc->sc_bulkout_pipe = NULL;
422 fail_1:
423 usbd_close_pipe(sc->sc_bulkin_pipe);
424 sc->sc_bulkin_pipe = NULL;
425 fail_0:
426 sc->sc_opening = 0;
427 wakeup(&sc->sc_opening);
428 crit_exit();
429 return (error);
431 bad:
432 if (!ISSET(tp->t_state, TS_ISOPEN)) {
434 * We failed to open the device, and nobody else had it opened.
435 * Clean up the state as appropriate.
437 ucom_cleanup(sc);
440 DPRINTF(("%s: ucomopen: failed\n", device_get_nameunit(sc->sc_dev)));
442 return (error);
445 static int
446 ucomclose(struct dev_close_args *ap)
448 cdev_t dev = ap->a_head.a_dev;
449 struct ucom_softc *sc;
450 struct tty *tp;
452 USB_GET_SC(ucom, UCOMUNIT(dev), sc);
454 tp = sc->sc_tty;
456 DPRINTF(("%s: ucomclose: unit = %d\n",
457 device_get_nameunit(sc->sc_dev), UCOMUNIT(dev)));
459 if (!ISSET(tp->t_state, TS_ISOPEN))
460 goto quit;
462 crit_enter();
463 (*linesw[tp->t_line].l_close)(tp, ap->a_fflag);
464 disc_optim(tp, &tp->t_termios, sc);
465 ttyclose(tp);
466 crit_exit();
468 if (sc->sc_dying)
469 goto quit;
471 if (!ISSET(tp->t_state, TS_ISOPEN)) {
473 * Although we got a last close, the device may still be in
474 * use; e.g. if this was the dialout node, and there are still
475 * processes waiting for carrier on the non-dialout node.
477 ucom_cleanup(sc);
480 if (sc->sc_callback->ucom_close != NULL)
481 sc->sc_callback->ucom_close(sc->sc_parent, sc->sc_portno);
483 quit:
484 if (tp->t_dev) {
485 release_dev(tp->t_dev);
486 tp->t_dev = NULL;
489 if (--sc->sc_refcnt < 0)
490 usb_detach_wakeup(sc->sc_dev);
492 return (0);
495 static int
496 ucomread(struct dev_read_args *ap)
498 cdev_t dev = ap->a_head.a_dev;
499 struct ucom_softc *sc;
500 struct tty *tp;
501 int error;
503 USB_GET_SC(ucom, UCOMUNIT(dev), sc);
504 tp = sc->sc_tty;
506 DPRINTF(("ucomread: tp = %p, flag = 0x%x\n", tp, ap->a_ioflag));
508 if (sc->sc_dying)
509 return (EIO);
511 error = (*linesw[tp->t_line].l_read)(tp, ap->a_uio, ap->a_ioflag);
513 DPRINTF(("ucomread: error = %d\n", error));
515 return (error);
518 static int
519 ucomwrite(struct dev_write_args *ap)
521 cdev_t dev = ap->a_head.a_dev;
522 struct ucom_softc *sc;
523 struct tty *tp;
524 int error;
526 USB_GET_SC(ucom, UCOMUNIT(dev), sc);
527 tp = sc->sc_tty;
529 DPRINTF(("ucomwrite: tp = %p, flag = 0x%x\n", tp, ap->a_ioflag));
531 if (sc->sc_dying)
532 return (EIO);
534 error = (*linesw[tp->t_line].l_write)(tp, ap->a_uio, ap->a_ioflag);
536 DPRINTF(("ucomwrite: error = %d\n", error));
538 return (error);
541 static int
542 ucomioctl(struct dev_ioctl_args *ap)
544 cdev_t dev = ap->a_head.a_dev;
545 struct ucom_softc *sc;
546 struct tty *tp;
547 int error;
548 int d;
550 USB_GET_SC(ucom, UCOMUNIT(dev), sc);
551 tp = sc->sc_tty;
553 if (sc->sc_dying)
554 return (EIO);
556 DPRINTF(("ucomioctl: cmd = 0x%08lx\n", ap->a_cmd));
558 error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data,
559 ap->a_fflag, ap->a_cred);
560 if (error != ENOIOCTL) {
561 DPRINTF(("ucomioctl: l_ioctl: error = %d\n", error));
562 return (error);
565 crit_enter();
567 error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag);
568 disc_optim(tp, &tp->t_termios, sc);
569 if (error != ENOIOCTL) {
570 crit_exit();
571 DPRINTF(("ucomioctl: ttioctl: error = %d\n", error));
572 return (error);
575 if (sc->sc_callback->ucom_ioctl != NULL) {
576 error = sc->sc_callback->ucom_ioctl(sc->sc_parent,
577 sc->sc_portno,
578 ap->a_cmd, ap->a_data,
579 ap->a_fflag, curthread);
580 if (error >= 0) {
581 crit_exit();
582 return (error);
586 error = 0;
588 DPRINTF(("ucomioctl: our cmd = 0x%08lx\n", ap->a_cmd));
590 switch (ap->a_cmd) {
591 case TIOCSBRK:
592 DPRINTF(("ucomioctl: TIOCSBRK\n"));
593 ucom_break(sc, 1);
594 break;
595 case TIOCCBRK:
596 DPRINTF(("ucomioctl: TIOCCBRK\n"));
597 ucom_break(sc, 0);
598 break;
600 case TIOCSDTR:
601 DPRINTF(("ucomioctl: TIOCSDTR\n"));
602 (void)ucomctl(sc, TIOCM_DTR, DMBIS);
603 break;
604 case TIOCCDTR:
605 DPRINTF(("ucomioctl: TIOCCDTR\n"));
606 (void)ucomctl(sc, TIOCM_DTR, DMBIC);
607 break;
609 case TIOCMSET:
610 d = *(int *)ap->a_data;
611 DPRINTF(("ucomioctl: TIOCMSET, 0x%x\n", d));
612 (void)ucomctl(sc, d, DMSET);
613 break;
614 case TIOCMBIS:
615 d = *(int *)ap->a_data;
616 DPRINTF(("ucomioctl: TIOCMBIS, 0x%x\n", d));
617 (void)ucomctl(sc, d, DMBIS);
618 break;
619 case TIOCMBIC:
620 d = *(int *)ap->a_data;
621 DPRINTF(("ucomioctl: TIOCMBIC, 0x%x\n", d));
622 (void)ucomctl(sc, d, DMBIC);
623 break;
624 case TIOCMGET:
625 d = ucomctl(sc, 0, DMGET);
626 DPRINTF(("ucomioctl: TIOCMGET, 0x%x\n", d));
627 *(int *)ap->a_data = d;
628 break;
630 default:
631 DPRINTF(("ucomioctl: error: our cmd = 0x%08lx\n", ap->a_cmd));
632 error = ENOTTY;
633 break;
636 crit_exit();
638 return (error);
641 static int
642 ucomctl(struct ucom_softc *sc, int bits, int how)
644 int mcr;
645 int msr;
646 int onoff;
648 DPRINTF(("ucomctl: bits = 0x%x, how = %d\n", bits, how));
650 if (how == DMGET) {
651 SET(bits, TIOCM_LE); /* always set TIOCM_LE bit */
652 DPRINTF(("ucomctl: DMGET: LE"));
654 mcr = sc->sc_mcr;
655 if (ISSET(mcr, UMCR_DTR)) {
656 SET(bits, TIOCM_DTR);
657 DPRINTF((" DTR"));
659 if (ISSET(mcr, UMCR_RTS)) {
660 SET(bits, TIOCM_RTS);
661 DPRINTF((" RTS"));
664 msr = sc->sc_msr;
665 if (ISSET(msr, UMSR_CTS)) {
666 SET(bits, TIOCM_CTS);
667 DPRINTF((" CTS"));
669 if (ISSET(msr, UMSR_DCD)) {
670 SET(bits, TIOCM_CD);
671 DPRINTF((" CD"));
673 if (ISSET(msr, UMSR_DSR)) {
674 SET(bits, TIOCM_DSR);
675 DPRINTF((" DSR"));
677 if (ISSET(msr, UMSR_RI)) {
678 SET(bits, TIOCM_RI);
679 DPRINTF((" RI"));
682 DPRINTF(("\n"));
684 return (bits);
687 mcr = 0;
688 if (ISSET(bits, TIOCM_DTR))
689 SET(mcr, UMCR_DTR);
690 if (ISSET(bits, TIOCM_RTS))
691 SET(mcr, UMCR_RTS);
693 switch (how) {
694 case DMSET:
695 sc->sc_mcr = mcr;
696 break;
697 case DMBIS:
698 sc->sc_mcr |= mcr;
699 break;
700 case DMBIC:
701 sc->sc_mcr &= ~mcr;
702 break;
705 onoff = ISSET(sc->sc_mcr, UMCR_DTR) ? 1 : 0;
706 ucom_dtr(sc, onoff);
708 onoff = ISSET(sc->sc_mcr, UMCR_RTS) ? 1 : 0;
709 ucom_rts(sc, onoff);
711 return (0);
714 static void
715 ucom_break(struct ucom_softc *sc, int onoff)
717 DPRINTF(("ucom_break: onoff = %d\n", onoff));
719 if (sc->sc_callback->ucom_set == NULL)
720 return;
721 sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
722 UCOM_SET_BREAK, onoff);
725 static void
726 ucom_dtr(struct ucom_softc *sc, int onoff)
728 DPRINTF(("ucom_dtr: onoff = %d\n", onoff));
730 if (sc->sc_callback->ucom_set == NULL)
731 return;
732 sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
733 UCOM_SET_DTR, onoff);
736 static void
737 ucom_rts(struct ucom_softc *sc, int onoff)
739 DPRINTF(("ucom_rts: onoff = %d\n", onoff));
741 if (sc->sc_callback->ucom_set == NULL)
742 return;
743 sc->sc_callback->ucom_set(sc->sc_parent, sc->sc_portno,
744 UCOM_SET_RTS, onoff);
747 void
748 ucom_status_change(struct ucom_softc *sc)
750 struct tty *tp = sc->sc_tty;
751 u_char old_msr;
752 int onoff;
754 if (sc->sc_callback->ucom_get_status == NULL) {
755 sc->sc_lsr = 0;
756 sc->sc_msr = 0;
757 return;
760 old_msr = sc->sc_msr;
761 sc->sc_callback->ucom_get_status(sc->sc_parent, sc->sc_portno,
762 &sc->sc_lsr, &sc->sc_msr);
763 if (ISSET((sc->sc_msr ^ old_msr), UMSR_DCD)) {
764 if (sc->sc_poll == 0)
765 return;
766 onoff = ISSET(sc->sc_msr, UMSR_DCD) ? 1 : 0;
767 DPRINTF(("ucom_status_change: DCD changed to %d\n", onoff));
768 (*linesw[tp->t_line].l_modem)(tp, onoff);
772 static int
773 ucomparam(struct tty *tp, struct termios *t)
775 struct ucom_softc *sc;
776 int error;
777 usbd_status uerr;
779 USB_GET_SC(ucom, UCOMUNIT(tp->t_dev), sc);
781 if (sc->sc_dying)
782 return (EIO);
784 DPRINTF(("ucomparam: sc = %p\n", sc));
786 /* Check requested parameters. */
787 if (t->c_ospeed < 0) {
788 DPRINTF(("ucomparam: negative ospeed\n"));
789 return (EINVAL);
791 if (t->c_ispeed && t->c_ispeed != t->c_ospeed) {
792 DPRINTF(("ucomparam: mismatch ispeed and ospeed\n"));
793 return (EINVAL);
797 * If there were no changes, don't do anything. This avoids dropping
798 * input and improves performance when all we did was frob things like
799 * VMIN and VTIME.
801 if (tp->t_ospeed == t->c_ospeed &&
802 tp->t_cflag == t->c_cflag)
803 return (0);
805 /* And copy to tty. */
806 tp->t_ispeed = 0;
807 tp->t_ospeed = t->c_ospeed;
808 tp->t_cflag = t->c_cflag;
810 if (sc->sc_callback->ucom_param == NULL)
811 return (0);
813 ucomstopread(sc);
815 error = sc->sc_callback->ucom_param(sc->sc_parent, sc->sc_portno, t);
816 if (error) {
817 DPRINTF(("ucomparam: callback: error = %d\n", error));
818 return (error);
821 ttsetwater(tp);
823 if (t->c_cflag & CRTS_IFLOW) {
824 sc->sc_state |= UCS_RTS_IFLOW;
825 } else if (sc->sc_state & UCS_RTS_IFLOW) {
826 sc->sc_state &= ~UCS_RTS_IFLOW;
827 (void)ucomctl(sc, UMCR_RTS, DMBIS);
830 disc_optim(tp, t, sc);
832 uerr = ucomstartread(sc);
833 if (uerr != USBD_NORMAL_COMPLETION)
834 return (EIO);
836 return (0);
839 static void
840 ucomstart(struct tty *tp)
842 struct ucom_softc *sc;
843 struct cblock *cbp;
844 usbd_status err;
845 u_char *data;
846 int cnt;
848 USB_GET_SC(ucom, UCOMUNIT(tp->t_dev), sc);
849 DPRINTF(("ucomstart: sc = %p\n", sc));
851 if (sc->sc_dying)
852 return;
854 crit_enter();
856 if (tp->t_state & TS_TBLOCK) {
857 if (ISSET(sc->sc_mcr, UMCR_RTS) &&
858 ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
859 DPRINTF(("ucomstart: clear RTS\n"));
860 (void)ucomctl(sc, UMCR_RTS, DMBIC);
862 } else {
863 if (!ISSET(sc->sc_mcr, UMCR_RTS) &&
864 tp->t_rawq.c_cc <= tp->t_ilowat &&
865 ISSET(sc->sc_state, UCS_RTS_IFLOW)) {
866 DPRINTF(("ucomstart: set RTS\n"));
867 (void)ucomctl(sc, UMCR_RTS, DMBIS);
871 if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
872 ttwwakeup(tp);
873 DPRINTF(("ucomstart: stopped\n"));
874 goto out;
877 if (tp->t_outq.c_cc <= tp->t_olowat) {
878 if (ISSET(tp->t_state, TS_SO_OLOWAT)) {
879 CLR(tp->t_state, TS_SO_OLOWAT);
880 wakeup(TSA_OLOWAT(tp));
882 selwakeuppri(&tp->t_wsel, TTIPRI);
883 if (tp->t_outq.c_cc == 0) {
884 if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
885 TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
886 CLR(tp->t_state, TS_SO_OCOMPLETE);
887 wakeup(TSA_OCOMPLETE(tp));
889 goto out;
893 /* Grab the first contiguous region of buffer space. */
894 data = tp->t_outq.c_cf;
895 cbp = (struct cblock *) ((intptr_t) tp->t_outq.c_cf & ~CROUND);
896 cnt = min((char *) (cbp+1) - tp->t_outq.c_cf, tp->t_outq.c_cc);
898 if (cnt == 0) {
899 DPRINTF(("ucomstart: cnt == 0\n"));
900 goto out;
903 SET(tp->t_state, TS_BUSY);
905 if (cnt > sc->sc_obufsize) {
906 DPRINTF(("ucomstart: big buffer %d chars\n", cnt));
907 cnt = sc->sc_obufsize;
909 if (sc->sc_callback->ucom_write != NULL)
910 sc->sc_callback->ucom_write(sc->sc_parent, sc->sc_portno,
911 sc->sc_obuf, data, &cnt);
912 else
913 memcpy(sc->sc_obuf, data, cnt);
915 DPRINTF(("ucomstart: %d chars\n", cnt));
916 usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe,
917 (usbd_private_handle)sc, sc->sc_obuf, cnt,
918 USBD_NO_COPY, USBD_NO_TIMEOUT, ucomwritecb);
919 /* What can we do on error? */
920 err = usbd_transfer(sc->sc_oxfer);
921 if (err != USBD_IN_PROGRESS)
922 kprintf("ucomstart: err=%s\n", usbd_errstr(err));
924 ttwwakeup(tp);
926 out:
927 crit_exit();
930 static void
931 ucomstop(struct tty *tp, int flag)
933 struct ucom_softc *sc;
935 USB_GET_SC(ucom, UCOMUNIT(tp->t_dev), sc);
937 DPRINTF(("ucomstop: %d\n", flag));
939 if (flag & FREAD) {
941 * This is just supposed to flush pending receive data,
942 * not stop the reception of data entirely!
944 DPRINTF(("ucomstop: read\n"));
945 ucomstopread(sc);
946 ucomstartread(sc);
949 if (flag & FWRITE) {
950 DPRINTF(("ucomstop: write\n"));
951 crit_enter();
952 if (ISSET(tp->t_state, TS_BUSY)) {
953 /* XXX do what? */
954 if (!ISSET(tp->t_state, TS_TTSTOP))
955 SET(tp->t_state, TS_FLUSH);
957 crit_exit();
960 DPRINTF(("ucomstop: done\n"));
963 static void
964 ucomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
966 struct ucom_softc *sc = (struct ucom_softc *)p;
967 struct tty *tp = sc->sc_tty;
968 u_int32_t cc;
970 DPRINTF(("ucomwritecb: status = %d\n", status));
972 if (status == USBD_CANCELLED || sc->sc_dying)
973 goto error;
975 if (status != USBD_NORMAL_COMPLETION) {
976 kprintf("%s: ucomwritecb: %s\n",
977 device_get_nameunit(sc->sc_dev), usbd_errstr(status));
978 if (status == USBD_STALLED)
979 usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
981 * XXX. We may need a flag to sequence ucomstopread() and
982 * ucomstartread() to handle the case where ucomstartread()
983 * is called after ucomstopread() but before the request has
984 * been properly canceled?
986 goto error;
989 usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
990 DPRINTF(("ucomwritecb: cc = %d\n", cc));
991 if (cc <= sc->sc_opkthdrlen) {
992 kprintf("%s: sent size too small, cc = %d\n",
993 device_get_nameunit(sc->sc_dev), cc);
994 goto error;
997 /* convert from USB bytes to tty bytes */
998 cc -= sc->sc_opkthdrlen;
1000 crit_enter();
1001 CLR(tp->t_state, TS_BUSY);
1002 if (ISSET(tp->t_state, TS_FLUSH))
1003 CLR(tp->t_state, TS_FLUSH);
1004 else
1005 ndflush(&tp->t_outq, cc);
1006 (*linesw[tp->t_line].l_start)(tp);
1007 crit_exit();
1009 return;
1011 error:
1012 crit_enter();
1013 CLR(tp->t_state, TS_BUSY);
1014 crit_exit();
1015 return;
1018 static usbd_status
1019 ucomstartread(struct ucom_softc *sc)
1021 usbd_status err;
1023 DPRINTF(("ucomstartread: start\n"));
1025 sc->sc_state &= ~UCS_RXSTOP;
1027 if (sc->sc_bulkin_pipe == NULL)
1028 return (USBD_NORMAL_COMPLETION);
1030 usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe,
1031 (usbd_private_handle)sc,
1032 sc->sc_ibuf, sc->sc_ibufsize,
1033 USBD_SHORT_XFER_OK | USBD_NO_COPY,
1034 USBD_NO_TIMEOUT, ucomreadcb);
1036 err = usbd_transfer(sc->sc_ixfer);
1037 if (err != USBD_IN_PROGRESS) {
1038 DPRINTF(("ucomstartread: err = %s\n", usbd_errstr(err)));
1039 return (err);
1042 return (USBD_NORMAL_COMPLETION);
1045 static void
1046 ucomreadcb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
1048 struct ucom_softc *sc = (struct ucom_softc *)p;
1049 struct tty *tp = sc->sc_tty;
1050 int (*rint) (int c, struct tty *tp) = linesw[tp->t_line].l_rint;
1051 usbd_status err;
1052 u_int32_t cc;
1053 u_char *cp;
1054 int lostcc;
1056 DPRINTF(("ucomreadcb: status = %d\n", status));
1058 if (status != USBD_NORMAL_COMPLETION) {
1059 if (!(sc->sc_state & UCS_RXSTOP))
1060 kprintf("%s: ucomreadcb: %s\n",
1061 device_get_nameunit(sc->sc_dev), usbd_errstr(status));
1062 if (status == USBD_STALLED)
1063 usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
1064 /* XXX we should restart after some delay. */
1065 return;
1068 usbd_get_xfer_status(xfer, NULL, (void **)&cp, &cc, NULL);
1069 DPRINTF(("ucomreadcb: got %d chars, tp = %p\n", cc, tp));
1070 if (cc == 0)
1071 goto resubmit;
1073 if (sc->sc_callback->ucom_read != NULL) {
1074 sc->sc_callback->ucom_read(sc->sc_parent, sc->sc_portno,
1075 &cp, &cc);
1078 if (cc > sc->sc_ibufsize) {
1079 kprintf("%s: invalid receive data size, %d chars\n",
1080 device_get_nameunit(sc->sc_dev), cc);
1081 goto resubmit;
1083 if (cc < 1)
1084 goto resubmit;
1086 crit_enter();
1087 if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
1088 if (tp->t_rawq.c_cc + cc > tp->t_ihiwat
1089 && (sc->sc_state & UCS_RTS_IFLOW
1090 || tp->t_iflag & IXOFF)
1091 && !(tp->t_state & TS_TBLOCK))
1092 ttyblock(tp);
1093 lostcc = b_to_q((char *)cp, cc, &tp->t_rawq);
1094 tp->t_rawcc += cc;
1095 if (sc->hotchar) {
1096 while (cc) {
1097 if (*cp == sc->hotchar)
1098 break;
1099 --cc;
1100 ++cp;
1102 if (cc)
1103 setsofttty();
1105 ttwakeup(tp);
1106 if (tp->t_state & TS_TTSTOP
1107 && (tp->t_iflag & IXANY
1108 || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
1109 tp->t_state &= ~TS_TTSTOP;
1110 tp->t_lflag &= ~FLUSHO;
1111 ucomstart(tp);
1113 if (lostcc > 0)
1114 kprintf("%s: lost %d chars\n", device_get_nameunit(sc->sc_dev),
1115 lostcc);
1116 } else {
1117 /* Give characters to tty layer. */
1118 while (cc > 0) {
1119 DPRINTFN(7, ("ucomreadcb: char = 0x%02x\n", *cp));
1120 if ((*rint)(*cp, tp) == -1) {
1121 /* XXX what should we do? */
1122 kprintf("%s: lost %d chars\n",
1123 device_get_nameunit(sc->sc_dev), cc);
1124 break;
1126 cc--;
1127 cp++;
1130 crit_exit();
1132 resubmit:
1133 err = ucomstartread(sc);
1134 if (err) {
1135 kprintf("%s: read start failed\n", device_get_nameunit(sc->sc_dev));
1136 /* XXX what should we dow now? */
1139 if ((sc->sc_state & UCS_RTS_IFLOW) && !ISSET(sc->sc_mcr, UMCR_RTS)
1140 && !(tp->t_state & TS_TBLOCK))
1141 ucomctl(sc, UMCR_RTS, DMBIS);
1144 static void
1145 ucom_cleanup(struct ucom_softc *sc)
1147 DPRINTF(("ucom_cleanup: closing pipes\n"));
1149 ucom_shutdown(sc);
1150 if (sc->sc_bulkin_pipe != NULL) {
1151 usbd_abort_pipe(sc->sc_bulkin_pipe);
1152 usbd_close_pipe(sc->sc_bulkin_pipe);
1153 sc->sc_bulkin_pipe = NULL;
1155 if (sc->sc_bulkout_pipe != NULL) {
1156 usbd_abort_pipe(sc->sc_bulkout_pipe);
1157 usbd_close_pipe(sc->sc_bulkout_pipe);
1158 sc->sc_bulkout_pipe = NULL;
1160 if (sc->sc_ixfer != NULL) {
1161 usbd_free_xfer(sc->sc_ixfer);
1162 sc->sc_ixfer = NULL;
1164 if (sc->sc_oxfer != NULL) {
1165 usbd_free_xfer(sc->sc_oxfer);
1166 sc->sc_oxfer = NULL;
1170 static void
1171 ucomstopread(struct ucom_softc *sc)
1173 usbd_status err;
1175 DPRINTF(("ucomstopread: enter\n"));
1177 if (!(sc->sc_state & UCS_RXSTOP)) {
1178 sc->sc_state |= UCS_RXSTOP;
1179 if (sc->sc_bulkin_pipe == NULL) {
1180 DPRINTF(("ucomstopread: bulkin pipe NULL\n"));
1181 return;
1183 err = usbd_abort_pipe(sc->sc_bulkin_pipe);
1184 if (err) {
1185 DPRINTF(("ucomstopread: err = %s\n",
1186 usbd_errstr(err)));
1190 DPRINTF(("ucomstopread: leave\n"));
1193 static void
1194 disc_optim(struct tty *tp, struct termios *t, struct ucom_softc *sc)
1196 if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
1197 && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
1198 && (!(t->c_iflag & PARMRK)
1199 || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
1200 && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
1201 && linesw[tp->t_line].l_rint == ttyinput) {
1202 DPRINTF(("disc_optim: bypass l_rint\n"));
1203 tp->t_state |= TS_CAN_BYPASS_L_RINT;
1204 } else {
1205 DPRINTF(("disc_optim: can't bypass l_rint\n"));
1206 tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
1208 sc->hotchar = linesw[tp->t_line].l_hotchar;