1 /* $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $ */
4 * Copyright (c) 2001-2003, 2005, 2008
5 * Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
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
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.
50 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
51 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
52 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
53 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
54 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
57 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
58 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
59 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
60 * POSSIBILITY OF SUCH DAMAGE.
63 #include <sys/stdint.h>
64 #include <sys/stddef.h>
65 #include <sys/param.h>
66 #include <sys/queue.h>
67 #include <sys/types.h>
68 #include <sys/systm.h>
69 #include <sys/kernel.h>
71 #include <sys/module.h>
73 #include <sys/mutex.h>
74 #include <sys/condvar.h>
75 #include <sys/sysctl.h>
77 #include <sys/unistd.h>
78 #include <sys/callout.h>
79 #include <sys/malloc.h>
84 #include <dev/usb/usb.h>
85 #include <dev/usb/usbdi.h>
86 #include <dev/usb/usbdi_util.h>
88 #define USB_DEBUG_VAR ucom_debug
89 #include <dev/usb/usb_debug.h>
90 #include <dev/usb/usb_busdma.h>
91 #include <dev/usb/usb_process.h>
93 #include <dev/usb/serial/usb_serial.h>
97 static SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
100 static int ucom_debug = 0;
102 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
103 &ucom_debug, 0, "ucom debug level");
106 #define UCOM_CONS_BUFSIZE 1024
108 static uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE];
109 static uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE];
111 static unsigned int ucom_cons_rx_low = 0;
112 static unsigned int ucom_cons_rx_high = 0;
114 static unsigned int ucom_cons_tx_low = 0;
115 static unsigned int ucom_cons_tx_high = 0;
117 static int ucom_cons_unit = -1;
118 static int ucom_cons_subunit = 0;
119 static int ucom_cons_baud = 9600;
120 static struct ucom_softc *ucom_cons_softc = NULL;
122 TUNABLE_INT("hw.usb.ucom.cons_unit", &ucom_cons_unit);
123 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RW | CTLFLAG_TUN,
124 &ucom_cons_unit, 0, "console unit number");
125 TUNABLE_INT("hw.usb.ucom.cons_subunit", &ucom_cons_subunit);
126 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_subunit, CTLFLAG_RW | CTLFLAG_TUN,
127 &ucom_cons_subunit, 0, "console subunit number");
128 TUNABLE_INT("hw.usb.ucom.cons_baud", &ucom_cons_baud);
129 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RW | CTLFLAG_TUN,
130 &ucom_cons_baud, 0, "console baud rate");
132 static usb_proc_callback_t ucom_cfg_start_transfers;
133 static usb_proc_callback_t ucom_cfg_open;
134 static usb_proc_callback_t ucom_cfg_close;
135 static usb_proc_callback_t ucom_cfg_line_state;
136 static usb_proc_callback_t ucom_cfg_status_change;
137 static usb_proc_callback_t ucom_cfg_param;
139 static int ucom_unit_alloc(void);
140 static void ucom_unit_free(int);
141 static int ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *);
142 static void ucom_detach_tty(struct ucom_super_softc *, struct ucom_softc *);
143 static void ucom_queue_command(struct ucom_softc *,
144 usb_proc_callback_t *, struct termios *pt,
145 struct usb_proc_msg *t0, struct usb_proc_msg *t1);
146 static void ucom_shutdown(struct ucom_softc *);
147 static void ucom_ring(struct ucom_softc *, uint8_t);
148 static void ucom_break(struct ucom_softc *, uint8_t);
149 static void ucom_dtr(struct ucom_softc *, uint8_t);
150 static void ucom_rts(struct ucom_softc *, uint8_t);
152 static tsw_open_t ucom_open;
153 static tsw_close_t ucom_close;
154 static tsw_ioctl_t ucom_ioctl;
155 static tsw_modem_t ucom_modem;
156 static tsw_param_t ucom_param;
157 static tsw_outwakeup_t ucom_outwakeup;
158 static tsw_inwakeup_t ucom_inwakeup;
159 static tsw_free_t ucom_free;
161 static struct ttydevsw ucom_class = {
162 .tsw_flags = TF_INITLOCK | TF_CALLOUT,
163 .tsw_open = ucom_open,
164 .tsw_close = ucom_close,
165 .tsw_outwakeup = ucom_outwakeup,
166 .tsw_inwakeup = ucom_inwakeup,
167 .tsw_ioctl = ucom_ioctl,
168 .tsw_param = ucom_param,
169 .tsw_modem = ucom_modem,
170 .tsw_free = ucom_free,
173 MODULE_DEPEND(ucom, usb, 1, 1, 1);
174 MODULE_VERSION(ucom, 1);
176 #define UCOM_UNIT_MAX 128 /* maximum number of units */
177 #define UCOM_TTY_PREFIX "U"
179 static struct unrhdr *ucom_unrhdr;
180 static struct mtx ucom_mtx;
181 static int ucom_close_refs;
187 ucom_unrhdr = new_unrhdr(0, UCOM_UNIT_MAX - 1, NULL);
188 mtx_init(&ucom_mtx, "UCOM MTX", NULL, MTX_DEF);
190 SYSINIT(ucom_init, SI_SUB_KLD - 1, SI_ORDER_ANY, ucom_init, NULL);
193 ucom_uninit(void *arg)
204 mtx_destroy(&ucom_mtx);
206 SYSUNINIT(ucom_uninit, SI_SUB_KLD - 2, SI_ORDER_ANY, ucom_uninit, NULL);
209 * Mark a unit number (the X in cuaUX) as in use.
211 * Note that devices using a different naming scheme (see ucom_tty_name()
212 * callback) still use this unit allocation.
215 ucom_unit_alloc(void)
220 if (ucom_unrhdr == NULL) {
221 DPRINTF("ucom_unrhdr is NULL\n");
224 unit = alloc_unr(ucom_unrhdr);
225 DPRINTF("unit %d is allocated\n", unit);
230 * Mark the unit number as not in use.
233 ucom_unit_free(int unit)
236 if (unit < 0 || unit >= UCOM_UNIT_MAX || ucom_unrhdr == NULL) {
237 DPRINTF("cannot free unit number\n");
240 DPRINTF("unit %d is freed\n", unit);
241 free_unr(ucom_unrhdr, unit);
245 * Setup a group of one or more serial ports.
247 * The mutex pointed to by "mtx" is applied before all
248 * callbacks are called back. Also "mtx" must be applied
249 * before calling into the ucom-layer!
252 ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
253 int subunits, void *parent,
254 const struct ucom_callback *callback, struct mtx *mtx)
261 (callback == NULL) ||
266 /* allocate a uniq unit number */
267 ssc->sc_unit = ucom_unit_alloc();
268 if (ssc->sc_unit == -1)
271 /* generate TTY name string */
272 snprintf(ssc->sc_ttyname, sizeof(ssc->sc_ttyname),
273 UCOM_TTY_PREFIX "%d", ssc->sc_unit);
275 /* create USB request handling process */
276 error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
278 ucom_unit_free(ssc->sc_unit);
281 ssc->sc_subunits = subunits;
282 ssc->sc_flag = UCOM_FLAG_ATTACHED |
285 if (callback->ucom_free == NULL)
286 ssc->sc_flag |= UCOM_FLAG_WAIT_REFS;
288 /* increment reference count */
291 for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
292 sc[subunit].sc_subunit = subunit;
293 sc[subunit].sc_super = ssc;
294 sc[subunit].sc_mtx = mtx;
295 sc[subunit].sc_parent = parent;
296 sc[subunit].sc_callback = callback;
298 error = ucom_attach_tty(ssc, &sc[subunit]);
300 ucom_detach(ssc, &sc[0]);
303 /* increment reference count */
306 /* set subunit attached */
307 sc[subunit].sc_flag |= UCOM_FLAG_ATTACHED;
310 DPRINTF("tp = %p, unit = %d, subunits = %d\n",
311 sc->sc_tty, ssc->sc_unit, ssc->sc_subunits);
317 * The following function will do nothing if the structure pointed to
318 * by "ssc" and "sc" is zero or has already been detached.
321 ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc)
325 if (!(ssc->sc_flag & UCOM_FLAG_ATTACHED))
326 return; /* not initialized */
328 if (ssc->sc_sysctl_ttyname != NULL) {
329 sysctl_remove_oid(ssc->sc_sysctl_ttyname, 1, 0);
330 ssc->sc_sysctl_ttyname = NULL;
333 if (ssc->sc_sysctl_ttyports != NULL) {
334 sysctl_remove_oid(ssc->sc_sysctl_ttyports, 1, 0);
335 ssc->sc_sysctl_ttyports = NULL;
338 usb_proc_drain(&ssc->sc_tq);
340 for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
341 if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) {
343 ucom_detach_tty(ssc, &sc[subunit]);
345 /* avoid duplicate detach */
346 sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED;
349 usb_proc_free(&ssc->sc_tq);
353 if (ssc->sc_flag & UCOM_FLAG_WAIT_REFS)
356 /* make sure we don't detach twice */
357 ssc->sc_flag &= ~UCOM_FLAG_ATTACHED;
361 ucom_drain(struct ucom_super_softc *ssc)
364 while (ssc->sc_refs > 0) {
365 printf("ucom: Waiting for a TTY device to close.\n");
366 usb_pause_mtx(&ucom_mtx, hz);
368 mtx_unlock(&ucom_mtx);
372 ucom_drain_all(void *arg)
375 while (ucom_close_refs > 0) {
376 printf("ucom: Waiting for all detached TTY "
377 "devices to have open fds closed.\n");
378 usb_pause_mtx(&ucom_mtx, hz);
380 mtx_unlock(&ucom_mtx);
384 ucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
387 char buf[32]; /* temporary TTY device name buffer */
389 tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx);
393 /* Check if the client has a custom TTY name */
395 if (sc->sc_callback->ucom_tty_name) {
396 sc->sc_callback->ucom_tty_name(sc, buf,
397 sizeof(buf), ssc->sc_unit, sc->sc_subunit);
400 /* Use default TTY name */
401 if (ssc->sc_subunits > 1) {
402 /* multiple modems in one */
403 snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u.%u",
404 ssc->sc_unit, sc->sc_subunit);
407 snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u",
411 tty_makedev(tp, NULL, "%s", buf);
415 DPRINTF("ttycreate: %s\n", buf);
417 /* Check if this device should be a console */
418 if ((ucom_cons_softc == NULL) &&
419 (ssc->sc_unit == ucom_cons_unit) &&
420 (sc->sc_subunit == ucom_cons_subunit)) {
422 DPRINTF("unit %d subunit %d is console",
423 ssc->sc_unit, sc->sc_subunit);
425 ucom_cons_softc = sc;
427 tty_init_console(tp, ucom_cons_baud);
429 UCOM_MTX_LOCK(ucom_cons_softc);
430 ucom_cons_rx_low = 0;
431 ucom_cons_rx_high = 0;
432 ucom_cons_tx_low = 0;
433 ucom_cons_tx_high = 0;
434 sc->sc_flag |= UCOM_FLAG_CONSOLE;
435 ucom_open(ucom_cons_softc->sc_tty);
436 ucom_param(ucom_cons_softc->sc_tty, &tp->t_termios_init_in);
437 UCOM_MTX_UNLOCK(ucom_cons_softc);
444 ucom_detach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
446 struct tty *tp = sc->sc_tty;
448 DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
450 if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
451 UCOM_MTX_LOCK(ucom_cons_softc);
452 ucom_close(ucom_cons_softc->sc_tty);
453 sc->sc_flag &= ~UCOM_FLAG_CONSOLE;
454 UCOM_MTX_UNLOCK(ucom_cons_softc);
455 ucom_cons_softc = NULL;
458 /* the config thread has been stopped when we get here */
461 sc->sc_flag |= UCOM_FLAG_GONE;
462 sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY);
468 mtx_unlock(&ucom_mtx);
472 ucom_close(tp); /* close, if any */
478 * make sure that read and write transfers are stopped
480 if (sc->sc_callback->ucom_stop_read)
481 (sc->sc_callback->ucom_stop_read) (sc);
482 if (sc->sc_callback->ucom_stop_write)
483 (sc->sc_callback->ucom_stop_write) (sc);
489 ucom_set_pnpinfo_usb(struct ucom_super_softc *ssc, device_t dev)
493 struct usb_attach_arg *uaa;
495 snprintf(buf, sizeof(buf), "ttyname=" UCOM_TTY_PREFIX
496 "%d ttyports=%d", ssc->sc_unit, ssc->sc_subunits);
498 /* Store the PNP info in the first interface for the device */
499 uaa = device_get_ivars(dev);
500 iface_index = uaa->info.bIfaceIndex;
502 if (usbd_set_pnpinfo(uaa->device, iface_index, buf) != 0)
503 device_printf(dev, "Could not set PNP info\n");
506 * The following information is also replicated in the PNP-info
507 * string which is registered above:
509 if (ssc->sc_sysctl_ttyname == NULL) {
510 ssc->sc_sysctl_ttyname = SYSCTL_ADD_STRING(NULL,
511 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
512 OID_AUTO, "ttyname", CTLFLAG_RD, ssc->sc_ttyname, 0,
513 "TTY device basename");
515 if (ssc->sc_sysctl_ttyports == NULL) {
516 ssc->sc_sysctl_ttyports = SYSCTL_ADD_INT(NULL,
517 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
518 OID_AUTO, "ttyports", CTLFLAG_RD,
519 NULL, ssc->sc_subunits, "Number of ports");
524 ucom_queue_command(struct ucom_softc *sc,
525 usb_proc_callback_t *fn, struct termios *pt,
526 struct usb_proc_msg *t0, struct usb_proc_msg *t1)
528 struct ucom_super_softc *ssc = sc->sc_super;
529 struct ucom_param_task *task;
531 UCOM_MTX_ASSERT(sc, MA_OWNED);
533 if (usb_proc_is_gone(&ssc->sc_tq)) {
534 DPRINTF("proc is gone\n");
535 return; /* nothing to do */
538 * NOTE: The task cannot get executed before we drop the
539 * "sc_mtx" mutex. It is safe to update fields in the message
540 * structure after that the message got queued.
542 task = (struct ucom_param_task *)
543 usb_proc_msignal(&ssc->sc_tq, t0, t1);
545 /* Setup callback and softc pointers */
546 task->hdr.pm_callback = fn;
550 * Make a copy of the termios. This field is only present if
551 * the "pt" field is not NULL.
554 task->termios_copy = *pt;
557 * Closing the device should be synchronous.
559 if (fn == ucom_cfg_close)
560 usb_proc_mwait(&ssc->sc_tq, t0, t1);
563 * In case of multiple configure requests,
564 * keep track of the last one!
566 if (fn == ucom_cfg_start_transfers)
567 sc->sc_last_start_xfer = &task->hdr;
571 ucom_shutdown(struct ucom_softc *sc)
573 struct tty *tp = sc->sc_tty;
575 UCOM_MTX_ASSERT(sc, MA_OWNED);
580 * Hang up if necessary:
582 if (tp->t_termios.c_cflag & HUPCL) {
583 ucom_modem(tp, 0, SER_DTR);
590 * else: taskqueue is draining or gone
593 ucom_cfg_is_gone(struct ucom_softc *sc)
595 struct ucom_super_softc *ssc = sc->sc_super;
597 return (usb_proc_is_gone(&ssc->sc_tq));
601 ucom_cfg_start_transfers(struct usb_proc_msg *_task)
603 struct ucom_cfg_task *task =
604 (struct ucom_cfg_task *)_task;
605 struct ucom_softc *sc = task->sc;
607 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
610 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
611 /* TTY device closed */
615 if (_task == sc->sc_last_start_xfer)
616 sc->sc_flag |= UCOM_FLAG_GP_DATA;
618 if (sc->sc_callback->ucom_start_read) {
619 (sc->sc_callback->ucom_start_read) (sc);
621 if (sc->sc_callback->ucom_start_write) {
622 (sc->sc_callback->ucom_start_write) (sc);
627 ucom_start_transfers(struct ucom_softc *sc)
629 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
633 * Make sure that data transfers are started in both
636 if (sc->sc_callback->ucom_start_read) {
637 (sc->sc_callback->ucom_start_read) (sc);
639 if (sc->sc_callback->ucom_start_write) {
640 (sc->sc_callback->ucom_start_write) (sc);
645 ucom_cfg_open(struct usb_proc_msg *_task)
647 struct ucom_cfg_task *task =
648 (struct ucom_cfg_task *)_task;
649 struct ucom_softc *sc = task->sc;
653 if (sc->sc_flag & UCOM_FLAG_LL_READY) {
659 sc->sc_flag |= UCOM_FLAG_LL_READY;
661 if (sc->sc_callback->ucom_cfg_open) {
662 (sc->sc_callback->ucom_cfg_open) (sc);
665 usb_pause_mtx(sc->sc_mtx, hz / 10);
671 ucom_open(struct tty *tp)
673 struct ucom_softc *sc = tty_softc(tp);
676 UCOM_MTX_ASSERT(sc, MA_OWNED);
678 if (sc->sc_flag & UCOM_FLAG_GONE) {
681 if (sc->sc_flag & UCOM_FLAG_HL_READY) {
685 DPRINTF("tp = %p\n", tp);
687 if (sc->sc_callback->ucom_pre_open) {
689 * give the lower layer a chance to disallow TTY open, for
690 * example if the device is not present:
692 error = (sc->sc_callback->ucom_pre_open) (sc);
697 sc->sc_flag |= UCOM_FLAG_HL_READY;
699 /* Disable transfers */
700 sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
706 /* reset programmed line state */
711 /* reset jitter buffer */
712 sc->sc_jitterbuf_in = 0;
713 sc->sc_jitterbuf_out = 0;
715 ucom_queue_command(sc, ucom_cfg_open, NULL,
716 &sc->sc_open_task[0].hdr,
717 &sc->sc_open_task[1].hdr);
719 /* Queue transfer enable command last */
720 ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
721 &sc->sc_start_task[0].hdr,
722 &sc->sc_start_task[1].hdr);
724 ucom_modem(tp, SER_DTR | SER_RTS, 0);
730 ucom_status_change(sc);
736 ucom_cfg_close(struct usb_proc_msg *_task)
738 struct ucom_cfg_task *task =
739 (struct ucom_cfg_task *)_task;
740 struct ucom_softc *sc = task->sc;
744 if (sc->sc_flag & UCOM_FLAG_LL_READY) {
745 sc->sc_flag &= ~UCOM_FLAG_LL_READY;
746 if (sc->sc_callback->ucom_cfg_close)
747 (sc->sc_callback->ucom_cfg_close) (sc);
754 ucom_close(struct tty *tp)
756 struct ucom_softc *sc = tty_softc(tp);
758 UCOM_MTX_ASSERT(sc, MA_OWNED);
760 DPRINTF("tp=%p\n", tp);
762 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
763 DPRINTF("tp=%p already closed\n", tp);
768 ucom_queue_command(sc, ucom_cfg_close, NULL,
769 &sc->sc_close_task[0].hdr,
770 &sc->sc_close_task[1].hdr);
772 sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
774 if (sc->sc_callback->ucom_stop_read) {
775 (sc->sc_callback->ucom_stop_read) (sc);
780 ucom_inwakeup(struct tty *tp)
782 struct ucom_softc *sc = tty_softc(tp);
788 UCOM_MTX_ASSERT(sc, MA_OWNED);
790 DPRINTF("tp=%p\n", tp);
792 if (ttydisc_can_bypass(tp) != 0 ||
793 (sc->sc_flag & UCOM_FLAG_HL_READY) == 0 ||
794 (sc->sc_flag & UCOM_FLAG_INWAKEUP) != 0) {
798 /* prevent recursion */
799 sc->sc_flag |= UCOM_FLAG_INWAKEUP;
801 pos = sc->sc_jitterbuf_out;
803 while (sc->sc_jitterbuf_in != pos) {
806 c = (char)sc->sc_jitterbuf[pos];
808 if (ttydisc_rint(tp, c, 0) == -1)
811 if (pos >= UCOM_JITTERBUF_SIZE)
812 pos -= UCOM_JITTERBUF_SIZE;
815 sc->sc_jitterbuf_out = pos;
817 /* clear RTS in async fashion */
818 if ((sc->sc_jitterbuf_in == pos) &&
819 (sc->sc_flag & UCOM_FLAG_RTS_IFLOW))
822 sc->sc_flag &= ~UCOM_FLAG_INWAKEUP;
826 ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
828 struct ucom_softc *sc = tty_softc(tp);
831 UCOM_MTX_ASSERT(sc, MA_OWNED);
833 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
836 DPRINTF("cmd = 0x%08lx\n", cmd);
858 if (sc->sc_callback->ucom_ioctl) {
859 error = (sc->sc_callback->ucom_ioctl)
860 (sc, cmd, data, 0, td);
870 ucom_modem(struct tty *tp, int sigon, int sigoff)
872 struct ucom_softc *sc = tty_softc(tp);
875 UCOM_MTX_ASSERT(sc, MA_OWNED);
877 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
880 if ((sigon == 0) && (sigoff == 0)) {
882 if (sc->sc_mcr & SER_DTR) {
885 if (sc->sc_mcr & SER_RTS) {
888 if (sc->sc_msr & SER_CTS) {
891 if (sc->sc_msr & SER_DCD) {
894 if (sc->sc_msr & SER_DSR) {
897 if (sc->sc_msr & SER_RI) {
902 if (sigon & SER_DTR) {
903 sc->sc_mcr |= SER_DTR;
905 if (sigoff & SER_DTR) {
906 sc->sc_mcr &= ~SER_DTR;
908 if (sigon & SER_RTS) {
909 sc->sc_mcr |= SER_RTS;
911 if (sigoff & SER_RTS) {
912 sc->sc_mcr &= ~SER_RTS;
914 onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
917 onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
924 ucom_cfg_line_state(struct usb_proc_msg *_task)
926 struct ucom_cfg_task *task =
927 (struct ucom_cfg_task *)_task;
928 struct ucom_softc *sc = task->sc;
935 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
940 /* compute callback mask */
941 if (sc->sc_callback->ucom_cfg_set_dtr)
943 if (sc->sc_callback->ucom_cfg_set_rts)
945 if (sc->sc_callback->ucom_cfg_set_break)
946 mask |= UCOM_LS_BREAK;
947 if (sc->sc_callback->ucom_cfg_set_ring)
948 mask |= UCOM_LS_RING;
950 /* compute the bits we are to program */
951 notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
952 any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
953 prev_value = sc->sc_pls_curr ^ notch_bits;
954 last_value = sc->sc_pls_curr;
956 /* reset programmed line state */
961 /* ensure that we don't lose any levels */
962 if (notch_bits & UCOM_LS_DTR)
963 sc->sc_callback->ucom_cfg_set_dtr(sc,
964 (prev_value & UCOM_LS_DTR) ? 1 : 0);
965 if (notch_bits & UCOM_LS_RTS)
966 sc->sc_callback->ucom_cfg_set_rts(sc,
967 (prev_value & UCOM_LS_RTS) ? 1 : 0);
968 if (notch_bits & UCOM_LS_BREAK)
969 sc->sc_callback->ucom_cfg_set_break(sc,
970 (prev_value & UCOM_LS_BREAK) ? 1 : 0);
971 if (notch_bits & UCOM_LS_RING)
972 sc->sc_callback->ucom_cfg_set_ring(sc,
973 (prev_value & UCOM_LS_RING) ? 1 : 0);
976 if (any_bits & UCOM_LS_DTR)
977 sc->sc_callback->ucom_cfg_set_dtr(sc,
978 (last_value & UCOM_LS_DTR) ? 1 : 0);
979 if (any_bits & UCOM_LS_RTS)
980 sc->sc_callback->ucom_cfg_set_rts(sc,
981 (last_value & UCOM_LS_RTS) ? 1 : 0);
982 if (any_bits & UCOM_LS_BREAK)
983 sc->sc_callback->ucom_cfg_set_break(sc,
984 (last_value & UCOM_LS_BREAK) ? 1 : 0);
985 if (any_bits & UCOM_LS_RING)
986 sc->sc_callback->ucom_cfg_set_ring(sc,
987 (last_value & UCOM_LS_RING) ? 1 : 0);
991 ucom_line_state(struct ucom_softc *sc,
992 uint8_t set_bits, uint8_t clear_bits)
994 UCOM_MTX_ASSERT(sc, MA_OWNED);
996 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1000 DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
1002 /* update current programmed line state */
1003 sc->sc_pls_curr |= set_bits;
1004 sc->sc_pls_curr &= ~clear_bits;
1005 sc->sc_pls_set |= set_bits;
1006 sc->sc_pls_clr |= clear_bits;
1008 /* defer driver programming */
1009 ucom_queue_command(sc, ucom_cfg_line_state, NULL,
1010 &sc->sc_line_state_task[0].hdr,
1011 &sc->sc_line_state_task[1].hdr);
1015 ucom_ring(struct ucom_softc *sc, uint8_t onoff)
1017 DPRINTF("onoff = %d\n", onoff);
1020 ucom_line_state(sc, UCOM_LS_RING, 0);
1022 ucom_line_state(sc, 0, UCOM_LS_RING);
1026 ucom_break(struct ucom_softc *sc, uint8_t onoff)
1028 DPRINTF("onoff = %d\n", onoff);
1031 ucom_line_state(sc, UCOM_LS_BREAK, 0);
1033 ucom_line_state(sc, 0, UCOM_LS_BREAK);
1037 ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
1039 DPRINTF("onoff = %d\n", onoff);
1042 ucom_line_state(sc, UCOM_LS_DTR, 0);
1044 ucom_line_state(sc, 0, UCOM_LS_DTR);
1048 ucom_rts(struct ucom_softc *sc, uint8_t onoff)
1050 DPRINTF("onoff = %d\n", onoff);
1053 ucom_line_state(sc, UCOM_LS_RTS, 0);
1055 ucom_line_state(sc, 0, UCOM_LS_RTS);
1059 ucom_cfg_status_change(struct usb_proc_msg *_task)
1061 struct ucom_cfg_task *task =
1062 (struct ucom_cfg_task *)_task;
1063 struct ucom_softc *sc = task->sc;
1072 UCOM_MTX_ASSERT(sc, MA_OWNED);
1074 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1077 if (sc->sc_callback->ucom_cfg_get_status == NULL) {
1085 (sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
1087 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1088 /* TTY device closed */
1091 onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
1092 lsr_delta = (sc->sc_lsr ^ new_lsr);
1094 sc->sc_msr = new_msr;
1095 sc->sc_lsr = new_lsr;
1099 onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
1101 DPRINTF("DCD changed to %d\n", onoff);
1103 ttydisc_modem(tp, onoff);
1106 if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
1108 DPRINTF("BREAK detected\n");
1110 ttydisc_rint(tp, 0, TRE_BREAK);
1111 ttydisc_rint_done(tp);
1114 if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) {
1116 DPRINTF("Frame error detected\n");
1118 ttydisc_rint(tp, 0, TRE_FRAMING);
1119 ttydisc_rint_done(tp);
1122 if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) {
1124 DPRINTF("Parity error detected\n");
1126 ttydisc_rint(tp, 0, TRE_PARITY);
1127 ttydisc_rint_done(tp);
1132 ucom_status_change(struct ucom_softc *sc)
1134 UCOM_MTX_ASSERT(sc, MA_OWNED);
1136 if (sc->sc_flag & UCOM_FLAG_CONSOLE)
1137 return; /* not supported */
1139 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1144 ucom_queue_command(sc, ucom_cfg_status_change, NULL,
1145 &sc->sc_status_task[0].hdr,
1146 &sc->sc_status_task[1].hdr);
1150 ucom_cfg_param(struct usb_proc_msg *_task)
1152 struct ucom_param_task *task =
1153 (struct ucom_param_task *)_task;
1154 struct ucom_softc *sc = task->sc;
1156 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1159 if (sc->sc_callback->ucom_cfg_param == NULL) {
1163 (sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
1166 usb_pause_mtx(sc->sc_mtx, hz / 10);
1170 ucom_param(struct tty *tp, struct termios *t)
1172 struct ucom_softc *sc = tty_softc(tp);
1176 UCOM_MTX_ASSERT(sc, MA_OWNED);
1181 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1183 /* XXX the TTY layer should call "open()" first! */
1185 * Not quite: Its ordering is partly backwards, but
1186 * some parameters must be set early in ttydev_open(),
1187 * possibly before calling ttydevsw_open().
1189 error = ucom_open(tp);
1195 DPRINTF("sc = %p\n", sc);
1197 /* Check requested parameters. */
1198 if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
1199 /* XXX c_ospeed == 0 is perfectly valid. */
1200 DPRINTF("mismatch ispeed and ospeed\n");
1204 t->c_ispeed = t->c_ospeed;
1206 if (sc->sc_callback->ucom_pre_param) {
1207 /* Let the lower layer verify the parameters */
1208 error = (sc->sc_callback->ucom_pre_param) (sc, t);
1210 DPRINTF("callback error = %d\n", error);
1215 /* Disable transfers */
1216 sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
1218 /* Queue baud rate programming command first */
1219 ucom_queue_command(sc, ucom_cfg_param, t,
1220 &sc->sc_param_task[0].hdr,
1221 &sc->sc_param_task[1].hdr);
1223 /* Queue transfer enable command last */
1224 ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
1225 &sc->sc_start_task[0].hdr,
1226 &sc->sc_start_task[1].hdr);
1228 if (t->c_cflag & CRTS_IFLOW) {
1229 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
1230 } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
1231 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
1232 ucom_modem(tp, SER_RTS, 0);
1244 ucom_outwakeup(struct tty *tp)
1246 struct ucom_softc *sc = tty_softc(tp);
1248 UCOM_MTX_ASSERT(sc, MA_OWNED);
1250 DPRINTF("sc = %p\n", sc);
1252 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1253 /* The higher layer is not ready */
1256 ucom_start_transfers(sc);
1259 /*------------------------------------------------------------------------*
1263 * 0: No data is available.
1264 * Else: Data is available.
1265 *------------------------------------------------------------------------*/
1267 ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1268 uint32_t offset, uint32_t len, uint32_t *actlen)
1270 struct usb_page_search res;
1271 struct tty *tp = sc->sc_tty;
1273 uint32_t offset_orig;
1275 UCOM_MTX_ASSERT(sc, MA_OWNED);
1277 if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1280 /* get total TX length */
1282 temp = ucom_cons_tx_high - ucom_cons_tx_low;
1283 temp %= UCOM_CONS_BUFSIZE;
1285 /* limit TX length */
1287 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low))
1288 temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
1295 usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
1297 /* update counters */
1299 ucom_cons_tx_low += temp;
1300 ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
1302 /* store actual length */
1306 return (temp ? 1 : 0);
1310 !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
1312 return (0); /* multiport device polling */
1314 offset_orig = offset;
1318 usbd_get_page(pc, offset, &res);
1320 if (res.length > len) {
1323 /* copy data directly into USB buffer */
1324 cnt = ttydisc_getc(tp, res.buffer, res.length);
1329 if (cnt < res.length) {
1335 actlen[0] = offset - offset_orig;
1337 DPRINTF("cnt=%d\n", actlen[0]);
1339 if (actlen[0] == 0) {
1346 ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1347 uint32_t offset, uint32_t len)
1349 struct usb_page_search res;
1350 struct tty *tp = sc->sc_tty;
1354 UCOM_MTX_ASSERT(sc, MA_OWNED);
1356 if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1359 /* get maximum RX length */
1361 temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
1362 temp %= UCOM_CONS_BUFSIZE;
1364 /* limit RX length */
1366 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high))
1367 temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
1374 usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
1376 /* update counters */
1378 ucom_cons_rx_high += temp;
1379 ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
1385 return; /* multiport device polling */
1388 return; /* no data */
1390 /* set a flag to prevent recursation ? */
1394 usbd_get_page(pc, offset, &res);
1396 if (res.length > len) {
1400 offset += res.length;
1402 /* pass characters to tty layer */
1407 /* first check if we can pass the buffer directly */
1409 if (ttydisc_can_bypass(tp)) {
1411 /* clear any jitter buffer */
1412 sc->sc_jitterbuf_in = 0;
1413 sc->sc_jitterbuf_out = 0;
1415 if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1416 DPRINTF("tp=%p, data lost\n", tp);
1422 for (cnt = 0; cnt != res.length; cnt++) {
1423 if (sc->sc_jitterbuf_in != sc->sc_jitterbuf_out ||
1424 ttydisc_rint(tp, buf[cnt], 0) == -1) {
1428 pos = sc->sc_jitterbuf_in;
1429 end = sc->sc_jitterbuf_out +
1430 UCOM_JITTERBUF_SIZE - 1;
1431 if (end >= UCOM_JITTERBUF_SIZE)
1432 end -= UCOM_JITTERBUF_SIZE;
1434 for (; cnt != res.length; cnt++) {
1437 sc->sc_jitterbuf[pos] = buf[cnt];
1439 if (pos >= UCOM_JITTERBUF_SIZE)
1440 pos -= UCOM_JITTERBUF_SIZE;
1443 sc->sc_jitterbuf_in = pos;
1445 /* set RTS in async fashion */
1446 if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW)
1449 DPRINTF("tp=%p, lost %d "
1450 "chars\n", tp, res.length - cnt);
1455 ttydisc_rint_done(tp);
1459 ucom_free(void *xsc)
1461 struct ucom_softc *sc = xsc;
1463 if (sc->sc_callback->ucom_free != NULL)
1464 sc->sc_callback->ucom_free(sc);
1466 ucom_unref(sc->sc_super);
1468 mtx_lock(&ucom_mtx);
1470 mtx_unlock(&ucom_mtx);
1473 static cn_probe_t ucom_cnprobe;
1474 static cn_init_t ucom_cninit;
1475 static cn_term_t ucom_cnterm;
1476 static cn_getc_t ucom_cngetc;
1477 static cn_putc_t ucom_cnputc;
1478 static cn_grab_t ucom_cngrab;
1479 static cn_ungrab_t ucom_cnungrab;
1481 CONSOLE_DRIVER(ucom);
1484 ucom_cnprobe(struct consdev *cp)
1486 if (ucom_cons_unit != -1)
1487 cp->cn_pri = CN_NORMAL;
1489 cp->cn_pri = CN_DEAD;
1491 strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name));
1495 ucom_cninit(struct consdev *cp)
1500 ucom_cnterm(struct consdev *cp)
1505 ucom_cngrab(struct consdev *cp)
1510 ucom_cnungrab(struct consdev *cp)
1515 ucom_cngetc(struct consdev *cd)
1517 struct ucom_softc *sc = ucom_cons_softc;
1525 if (ucom_cons_rx_low != ucom_cons_rx_high) {
1526 c = ucom_cons_rx_buf[ucom_cons_rx_low];
1527 ucom_cons_rx_low ++;
1528 ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
1533 /* start USB transfers */
1534 ucom_outwakeup(sc->sc_tty);
1536 UCOM_MTX_UNLOCK(sc);
1538 /* poll if necessary */
1539 if (kdb_active && sc->sc_callback->ucom_poll)
1540 (sc->sc_callback->ucom_poll) (sc);
1546 ucom_cnputc(struct consdev *cd, int c)
1548 struct ucom_softc *sc = ucom_cons_softc;
1558 /* compute maximum TX length */
1560 temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
1561 temp %= UCOM_CONS_BUFSIZE;
1564 ucom_cons_tx_buf[ucom_cons_tx_high] = c;
1565 ucom_cons_tx_high ++;
1566 ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
1569 /* start USB transfers */
1570 ucom_outwakeup(sc->sc_tty);
1572 UCOM_MTX_UNLOCK(sc);
1574 /* poll if necessary */
1575 if (kdb_active && sc->sc_callback->ucom_poll) {
1576 (sc->sc_callback->ucom_poll) (sc);
1577 /* simple flow control */
1583 /*------------------------------------------------------------------------*
1586 * This function will increment the super UCOM reference count.
1587 *------------------------------------------------------------------------*/
1589 ucom_ref(struct ucom_super_softc *ssc)
1591 mtx_lock(&ucom_mtx);
1593 mtx_unlock(&ucom_mtx);
1596 /*------------------------------------------------------------------------*
1599 * This function will free the super UCOM's allocated unit
1600 * number. This function can be called on a zero-initialized
1601 * structure. This function can be called multiple times.
1602 *------------------------------------------------------------------------*/
1604 ucom_free_unit(struct ucom_super_softc *ssc)
1606 if (!(ssc->sc_flag & UCOM_FLAG_FREE_UNIT))
1609 ucom_unit_free(ssc->sc_unit);
1611 ssc->sc_flag &= ~UCOM_FLAG_FREE_UNIT;
1614 /*------------------------------------------------------------------------*
1617 * This function will decrement the super UCOM reference count.
1620 * 0: UCOM structures are still referenced.
1621 * Else: UCOM structures are no longer referenced.
1622 *------------------------------------------------------------------------*/
1624 ucom_unref(struct ucom_super_softc *ssc)
1628 mtx_lock(&ucom_mtx);
1629 retval = (ssc->sc_refs < 2);
1631 mtx_unlock(&ucom_mtx);
1634 ucom_free_unit(ssc);
1641 #include <gdb/gdb.h>
1643 static gdb_probe_f ucom_gdbprobe;
1644 static gdb_init_f ucom_gdbinit;
1645 static gdb_term_f ucom_gdbterm;
1646 static gdb_getc_f ucom_gdbgetc;
1647 static gdb_putc_f ucom_gdbputc;
1649 GDB_DBGPORT(sio, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc);
1654 return ((ucom_cons_softc != NULL) ? 0 : -1);
1670 ucom_cnputc(NULL, c);
1676 return (ucom_cngetc(NULL));