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
31 * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
32 * All rights reserved.
34 * This code is derived from software contributed to The NetBSD Foundation
35 * by Lennart Augustsson (lennart@augustsson.net) at
36 * Carlstedt Research & Technology.
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 * 3. All advertising materials mentioning features or use of this software
47 * must display the following acknowledgement:
48 * This product includes software developed by the NetBSD
49 * Foundation, Inc. and its contributors.
50 * 4. Neither the name of The NetBSD Foundation nor the names of its
51 * contributors may be used to endorse or promote products derived
52 * from this software without specific prior written permission.
54 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
55 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
56 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
58 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
59 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
60 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
61 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
62 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
63 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
64 * POSSIBILITY OF SUCH DAMAGE.
67 #include <sys/stdint.h>
68 #include <sys/param.h>
69 #include <sys/queue.h>
70 #include <sys/types.h>
71 #include <sys/systm.h>
72 #include <sys/kernel.h>
74 #include <sys/module.h>
76 #include <sys/condvar.h>
77 #include <sys/sysctl.h>
78 #include <sys/unistd.h>
79 #include <sys/callout.h>
80 #include <sys/malloc.h>
83 //#include <sys/kdb.h>
85 #include <bus/u4b/usb.h>
86 #include <bus/u4b/usbdi.h>
87 #include <bus/u4b/usbdi_util.h>
89 #define USB_DEBUG_VAR ucom_debug
90 #include <bus/u4b/usb_debug.h>
91 #include <bus/u4b/usb_busdma.h>
92 #include <bus/u4b/usb_process.h>
94 #include <bus/u4b/serial/usb_serial.h>
96 //#include "opt_gdb.h"
98 static SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
101 static int ucom_debug = 0;
103 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
104 &ucom_debug, 0, "ucom debug level");
107 #define UCOM_CONS_BUFSIZE 1024
109 static uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE];
110 static uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE];
112 static unsigned int ucom_cons_rx_low = 0;
113 static unsigned int ucom_cons_rx_high = 0;
115 static unsigned int ucom_cons_tx_low = 0;
116 static unsigned int ucom_cons_tx_high = 0;
118 static int ucom_cons_unit = -1;
119 static int ucom_cons_subunit = 0;
120 static int ucom_cons_baud = 9600;
121 static struct ucom_softc *ucom_cons_softc = NULL;
123 TUNABLE_INT("hw.usb.ucom.cons_unit", &ucom_cons_unit);
124 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RW,
125 &ucom_cons_unit, 0, "console unit number");
126 TUNABLE_INT("hw.usb.ucom.cons_subunit", &ucom_cons_subunit);
127 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_subunit, CTLFLAG_RW,
128 &ucom_cons_subunit, 0, "console subunit number");
129 TUNABLE_INT("hw.usb.ucom.cons_baud", &ucom_cons_baud);
130 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RW,
131 &ucom_cons_baud, 0, "console baud rate");
133 static usb_proc_callback_t ucom_cfg_start_transfers;
134 static usb_proc_callback_t ucom_cfg_open;
135 static usb_proc_callback_t ucom_cfg_close;
136 static usb_proc_callback_t ucom_cfg_line_state;
137 static usb_proc_callback_t ucom_cfg_status_change;
138 static usb_proc_callback_t ucom_cfg_param;
140 static int ucom_unit_alloc(void);
141 static void ucom_unit_free(int);
142 static int ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *);
143 static void ucom_detach_tty(struct ucom_softc *);
144 static void ucom_queue_command(struct ucom_softc *,
145 usb_proc_callback_t *, struct termios *pt,
146 struct usb_proc_msg *t0, struct usb_proc_msg *t1);
147 static void ucom_shutdown(struct ucom_softc *);
148 static void ucom_ring(struct ucom_softc *, uint8_t);
149 static void ucom_break(struct ucom_softc *, uint8_t);
150 static void ucom_dtr(struct ucom_softc *, uint8_t);
151 static void ucom_rts(struct ucom_softc *, uint8_t);
153 static tsw_open_t ucom_open;
154 static tsw_close_t ucom_close;
155 static tsw_ioctl_t ucom_ioctl;
156 static tsw_modem_t ucom_modem;
157 static tsw_param_t ucom_param;
158 static tsw_outwakeup_t ucom_outwakeup;
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_ioctl = ucom_ioctl,
167 .tsw_param = ucom_param,
168 .tsw_modem = ucom_modem,
169 .tsw_free = ucom_free,
172 MODULE_DEPEND(ucom, usb, 1, 1, 1);
173 MODULE_VERSION(ucom, 1);
175 #define UCOM_UNIT_MAX 128 /* limits size of ucom_bitmap */
177 static uint8_t ucom_bitmap[(UCOM_UNIT_MAX + 7) / 8];
178 static struct lock ucom_bitmap_lock;
179 LOCK_SYSINIT(ucom_bitmap_lock, &ucom_bitmap_lock, "ucom bitmap", LK_CANRECURSE);
181 #define UCOM_TTY_PREFIX "U"
184 * Mark a unit number (the X in cuaUX) as in use.
186 * Note that devices using a different naming scheme (see ucom_tty_name()
187 * callback) still use this unit allocation.
190 ucom_unit_alloc(void)
194 lockmgr(&ucom_bitmap_lock, LK_EXCLUSIVE);
196 for (unit = 0; unit < UCOM_UNIT_MAX; unit++) {
197 if ((ucom_bitmap[unit / 8] & (1 << (unit % 8))) == 0) {
198 ucom_bitmap[unit / 8] |= (1 << (unit % 8));
203 lockmgr(&ucom_bitmap_lock, LK_RELEASE);
205 if (unit == UCOM_UNIT_MAX)
212 * Mark the unit number as not in use.
215 ucom_unit_free(int unit)
217 lockmgr(&ucom_bitmap_lock, LK_EXCLUSIVE);
219 ucom_bitmap[unit / 8] &= ~(1 << (unit % 8));
221 lockmgr(&ucom_bitmap_lock, LK_RELEASE);
225 * Setup a group of one or more serial ports.
227 * The lock pointed to by "lock" is applied before all
228 * callbacks are called back. Also "lock" must be applied
229 * before calling into the ucom-layer!
232 ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
233 uint32_t subunits, void *parent,
234 const struct ucom_callback *callback, struct lock *lock)
241 (callback == NULL)) {
245 /* allocate a uniq unit number */
246 ssc->sc_unit = ucom_unit_alloc();
247 if (ssc->sc_unit == -1)
250 /* generate TTY name string */
251 ksnprintf(ssc->sc_ttyname, sizeof(ssc->sc_ttyname),
252 UCOM_TTY_PREFIX "%d", ssc->sc_unit);
254 /* create USB request handling process */
255 error = usb_proc_create(&ssc->sc_tq, lock, "ucom", USB_PRI_MED);
257 ucom_unit_free(ssc->sc_unit);
260 ssc->sc_subunits = subunits;
262 for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
263 sc[subunit].sc_subunit = subunit;
264 sc[subunit].sc_super = ssc;
265 sc[subunit].sc_lock = lock;
266 sc[subunit].sc_parent = parent;
267 sc[subunit].sc_callback = callback;
269 error = ucom_attach_tty(ssc, &sc[subunit]);
271 ucom_detach(ssc, &sc[0]);
274 sc[subunit].sc_flag |= UCOM_FLAG_ATTACHED;
277 DPRINTF("tp = %p, unit = %d, subunits = %d\n",
278 sc->sc_tty, ssc->sc_unit, ssc->sc_subunits);
284 * NOTE: the following function will do nothing if
285 * the structure pointed to by "ssc" and "sc" is zero.
288 ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc)
292 if (ssc->sc_subunits == 0)
293 return; /* not initialized */
295 if (ssc->sc_sysctl_ttyname != NULL) {
296 sysctl_remove_oid(ssc->sc_sysctl_ttyname, 1, 0);
297 ssc->sc_sysctl_ttyname = NULL;
300 if (ssc->sc_sysctl_ttyports != NULL) {
301 sysctl_remove_oid(ssc->sc_sysctl_ttyports, 1, 0);
302 ssc->sc_sysctl_ttyports = NULL;
305 usb_proc_drain(&ssc->sc_tq);
307 for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
308 if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) {
310 ucom_detach_tty(&sc[subunit]);
312 /* avoid duplicate detach */
313 sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED;
316 ucom_unit_free(ssc->sc_unit);
317 usb_proc_free(&ssc->sc_tq);
321 ucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
324 char buf[32]; /* temporary TTY device name buffer */
326 tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_lock);
330 /* Check if the client has a custom TTY name */
332 if (sc->sc_callback->ucom_tty_name) {
333 sc->sc_callback->ucom_tty_name(sc, buf,
334 sizeof(buf), ssc->sc_unit, sc->sc_subunit);
337 /* Use default TTY name */
338 if (ssc->sc_subunits > 1) {
339 /* multiple modems in one */
340 ksnprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u.%u",
341 ssc->sc_unit, sc->sc_subunit);
344 ksnprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u",
348 tty_makedev(tp, NULL, "%s", buf);
352 DPRINTF("ttycreate: %s\n", buf);
353 cv_init(&sc->sc_cv, "ucom");
355 /* Check if this device should be a console */
356 if ((ucom_cons_softc == NULL) &&
357 (ssc->sc_unit == ucom_cons_unit) &&
358 (sc->sc_subunit == ucom_cons_subunit)) {
361 DPRINTF("unit %d subunit %d is console", ssc->sc_unit, sc->sc_subunit);
363 ucom_cons_softc = sc;
365 memset(&t, 0, sizeof(t));
366 t.c_ispeed = ucom_cons_baud;
367 t.c_ospeed = t.c_ispeed;
370 lockmgr(ucom_cons_softc->sc_lock, LK_EXCLUSIVE);
371 ucom_cons_rx_low = 0;
372 ucom_cons_rx_high = 0;
373 ucom_cons_tx_low = 0;
374 ucom_cons_tx_high = 0;
375 sc->sc_flag |= UCOM_FLAG_CONSOLE;
376 ucom_open(ucom_cons_softc->sc_tty);
377 ucom_param(ucom_cons_softc->sc_tty, &t);
378 lockmgr(ucom_cons_softc->sc_lock, LK_RELEASE);
385 ucom_detach_tty(struct ucom_softc *sc)
387 struct tty *tp = sc->sc_tty;
389 DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
391 if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
392 lockmgr(ucom_cons_softc->sc_lock, LK_EXCLUSIVE);
393 ucom_close(ucom_cons_softc->sc_tty);
394 sc->sc_flag &= ~UCOM_FLAG_CONSOLE;
395 lockmgr(ucom_cons_softc->sc_lock, LK_RELEASE);
396 ucom_cons_softc = NULL;
399 /* the config thread has been stopped when we get here */
401 lockmgr(sc->sc_lock, LK_EXCLUSIVE);
402 sc->sc_flag |= UCOM_FLAG_GONE;
403 sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY);
404 lockmgr(sc->sc_lock, LK_RELEASE);
408 ucom_close(tp); /* close, if any */
412 lockmgr(sc->sc_lock, LK_EXCLUSIVE);
413 /* Wait for the callback after the TTY is torn down */
414 while (sc->sc_ttyfreed == 0)
415 cv_wait(&sc->sc_cv, sc->sc_lock);
417 * make sure that read and write transfers are stopped
419 if (sc->sc_callback->ucom_stop_read) {
420 (sc->sc_callback->ucom_stop_read) (sc);
422 if (sc->sc_callback->ucom_stop_write) {
423 (sc->sc_callback->ucom_stop_write) (sc);
425 lockmgr(sc->sc_lock, LK_RELEASE);
427 cv_destroy(&sc->sc_cv);
431 ucom_set_pnpinfo_usb(struct ucom_super_softc *ssc, device_t dev)
435 struct usb_attach_arg *uaa;
437 ksnprintf(buf, sizeof(buf), "ttyname=" UCOM_TTY_PREFIX
438 "%d ttyports=%d", ssc->sc_unit, ssc->sc_subunits);
440 /* Store the PNP info in the first interface for the device */
441 uaa = device_get_ivars(dev);
442 iface_index = uaa->info.bIfaceIndex;
444 if (usbd_set_pnpinfo(uaa->device, iface_index, buf) != 0)
445 device_printf(dev, "Could not set PNP info\n");
448 * The following information is also replicated in the PNP-info
449 * string which is registered above:
451 if (ssc->sc_sysctl_ttyname == NULL) {
452 ssc->sc_sysctl_ttyname = SYSCTL_ADD_STRING(NULL,
453 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
454 OID_AUTO, "ttyname", CTLFLAG_RD, ssc->sc_ttyname, 0,
455 "TTY device basename");
457 if (ssc->sc_sysctl_ttyports == NULL) {
458 ssc->sc_sysctl_ttyports = SYSCTL_ADD_INT(NULL,
459 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
460 OID_AUTO, "ttyports", CTLFLAG_RD,
461 NULL, ssc->sc_subunits, "Number of ports");
466 ucom_queue_command(struct ucom_softc *sc,
467 usb_proc_callback_t *fn, struct termios *pt,
468 struct usb_proc_msg *t0, struct usb_proc_msg *t1)
470 struct ucom_super_softc *ssc = sc->sc_super;
471 struct ucom_param_task *task;
473 KKASSERT(lockowned(sc->sc_lock));
475 if (usb_proc_is_gone(&ssc->sc_tq)) {
476 DPRINTF("proc is gone\n");
477 return; /* nothing to do */
480 * NOTE: The task cannot get executed before we drop the
481 * "sc_lock" lock. It is safe to update fields in the message
482 * structure after that the message got queued.
484 task = (struct ucom_param_task *)
485 usb_proc_msignal(&ssc->sc_tq, t0, t1);
487 /* Setup callback and softc pointers */
488 task->hdr.pm_callback = fn;
492 * Make a copy of the termios. This field is only present if
493 * the "pt" field is not NULL.
496 task->termios_copy = *pt;
499 * Closing the device should be synchronous.
501 if (fn == ucom_cfg_close)
502 usb_proc_mwait(&ssc->sc_tq, t0, t1);
505 * In case of multiple configure requests,
506 * keep track of the last one!
508 if (fn == ucom_cfg_start_transfers)
509 sc->sc_last_start_xfer = &task->hdr;
513 ucom_shutdown(struct ucom_softc *sc)
515 struct tty *tp = sc->sc_tty;
517 KKASSERT(lockowned(sc->sc_lock));
522 * Hang up if necessary:
524 if (tp->t_termios.c_cflag & HUPCL) {
525 ucom_modem(tp, 0, SER_DTR);
532 * else: taskqueue is draining or gone
535 ucom_cfg_is_gone(struct ucom_softc *sc)
537 struct ucom_super_softc *ssc = sc->sc_super;
539 return (usb_proc_is_gone(&ssc->sc_tq));
543 ucom_cfg_start_transfers(struct usb_proc_msg *_task)
545 struct ucom_cfg_task *task =
546 (struct ucom_cfg_task *)_task;
547 struct ucom_softc *sc = task->sc;
549 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
552 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
553 /* TTY device closed */
557 if (_task == sc->sc_last_start_xfer)
558 sc->sc_flag |= UCOM_FLAG_GP_DATA;
560 if (sc->sc_callback->ucom_start_read) {
561 (sc->sc_callback->ucom_start_read) (sc);
563 if (sc->sc_callback->ucom_start_write) {
564 (sc->sc_callback->ucom_start_write) (sc);
569 ucom_start_transfers(struct ucom_softc *sc)
571 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
575 * Make sure that data transfers are started in both
578 if (sc->sc_callback->ucom_start_read) {
579 (sc->sc_callback->ucom_start_read) (sc);
581 if (sc->sc_callback->ucom_start_write) {
582 (sc->sc_callback->ucom_start_write) (sc);
587 ucom_cfg_open(struct usb_proc_msg *_task)
589 struct ucom_cfg_task *task =
590 (struct ucom_cfg_task *)_task;
591 struct ucom_softc *sc = task->sc;
595 if (sc->sc_flag & UCOM_FLAG_LL_READY) {
601 sc->sc_flag |= UCOM_FLAG_LL_READY;
603 if (sc->sc_callback->ucom_cfg_open) {
604 (sc->sc_callback->ucom_cfg_open) (sc);
607 usb_pause_mtx(sc->sc_lock, hz / 10);
613 ucom_open(struct tty *tp)
615 struct ucom_softc *sc = tty_softc(tp);
618 KKASSERT(lockowned(sc->sc_lock));
620 if (sc->sc_flag & UCOM_FLAG_GONE) {
623 if (sc->sc_flag & UCOM_FLAG_HL_READY) {
627 DPRINTF("tp = %p\n", tp);
629 if (sc->sc_callback->ucom_pre_open) {
631 * give the lower layer a chance to disallow TTY open, for
632 * example if the device is not present:
634 error = (sc->sc_callback->ucom_pre_open) (sc);
639 sc->sc_flag |= UCOM_FLAG_HL_READY;
641 /* Disable transfers */
642 sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
648 /* reset programmed line state */
653 ucom_queue_command(sc, ucom_cfg_open, NULL,
654 &sc->sc_open_task[0].hdr,
655 &sc->sc_open_task[1].hdr);
657 /* Queue transfer enable command last */
658 ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
659 &sc->sc_start_task[0].hdr,
660 &sc->sc_start_task[1].hdr);
662 ucom_modem(tp, SER_DTR | SER_RTS, 0);
668 ucom_status_change(sc);
674 ucom_cfg_close(struct usb_proc_msg *_task)
676 struct ucom_cfg_task *task =
677 (struct ucom_cfg_task *)_task;
678 struct ucom_softc *sc = task->sc;
682 if (sc->sc_flag & UCOM_FLAG_LL_READY) {
683 sc->sc_flag &= ~UCOM_FLAG_LL_READY;
684 if (sc->sc_callback->ucom_cfg_close)
685 (sc->sc_callback->ucom_cfg_close) (sc);
692 ucom_close(struct tty *tp)
694 struct ucom_softc *sc = tty_softc(tp);
696 KKASSERT(lockowned(sc->sc_lock));
698 DPRINTF("tp=%p\n", tp);
700 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
701 DPRINTF("tp=%p already closed\n", tp);
706 ucom_queue_command(sc, ucom_cfg_close, NULL,
707 &sc->sc_close_task[0].hdr,
708 &sc->sc_close_task[1].hdr);
710 sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
712 if (sc->sc_callback->ucom_stop_read) {
713 (sc->sc_callback->ucom_stop_read) (sc);
718 ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
720 struct ucom_softc *sc = tty_softc(tp);
723 KKASSERT(lockowned(sc->sc_lock));
725 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
728 DPRINTF("cmd = 0x%08lx\n", cmd);
750 if (sc->sc_callback->ucom_ioctl) {
751 error = (sc->sc_callback->ucom_ioctl)
752 (sc, cmd, data, 0, td);
762 ucom_modem(struct tty *tp, int sigon, int sigoff)
764 struct ucom_softc *sc = tty_softc(tp);
767 KKASSERT(lockowned(sc->sc_lock));
769 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
772 if ((sigon == 0) && (sigoff == 0)) {
774 if (sc->sc_mcr & SER_DTR) {
777 if (sc->sc_mcr & SER_RTS) {
780 if (sc->sc_msr & SER_CTS) {
783 if (sc->sc_msr & SER_DCD) {
786 if (sc->sc_msr & SER_DSR) {
789 if (sc->sc_msr & SER_RI) {
794 if (sigon & SER_DTR) {
795 sc->sc_mcr |= SER_DTR;
797 if (sigoff & SER_DTR) {
798 sc->sc_mcr &= ~SER_DTR;
800 if (sigon & SER_RTS) {
801 sc->sc_mcr |= SER_RTS;
803 if (sigoff & SER_RTS) {
804 sc->sc_mcr &= ~SER_RTS;
806 onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
809 onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
816 ucom_cfg_line_state(struct usb_proc_msg *_task)
818 struct ucom_cfg_task *task =
819 (struct ucom_cfg_task *)_task;
820 struct ucom_softc *sc = task->sc;
827 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
832 /* compute callback mask */
833 if (sc->sc_callback->ucom_cfg_set_dtr)
835 if (sc->sc_callback->ucom_cfg_set_rts)
837 if (sc->sc_callback->ucom_cfg_set_break)
838 mask |= UCOM_LS_BREAK;
839 if (sc->sc_callback->ucom_cfg_set_ring)
840 mask |= UCOM_LS_RING;
842 /* compute the bits we are to program */
843 notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
844 any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
845 prev_value = sc->sc_pls_curr ^ notch_bits;
846 last_value = sc->sc_pls_curr;
848 /* reset programmed line state */
853 /* ensure that we don't loose any levels */
854 if (notch_bits & UCOM_LS_DTR)
855 sc->sc_callback->ucom_cfg_set_dtr(sc,
856 (prev_value & UCOM_LS_DTR) ? 1 : 0);
857 if (notch_bits & UCOM_LS_RTS)
858 sc->sc_callback->ucom_cfg_set_rts(sc,
859 (prev_value & UCOM_LS_RTS) ? 1 : 0);
860 if (notch_bits & UCOM_LS_BREAK)
861 sc->sc_callback->ucom_cfg_set_break(sc,
862 (prev_value & UCOM_LS_BREAK) ? 1 : 0);
863 if (notch_bits & UCOM_LS_RING)
864 sc->sc_callback->ucom_cfg_set_ring(sc,
865 (prev_value & UCOM_LS_RING) ? 1 : 0);
868 if (any_bits & UCOM_LS_DTR)
869 sc->sc_callback->ucom_cfg_set_dtr(sc,
870 (last_value & UCOM_LS_DTR) ? 1 : 0);
871 if (any_bits & UCOM_LS_RTS)
872 sc->sc_callback->ucom_cfg_set_rts(sc,
873 (last_value & UCOM_LS_RTS) ? 1 : 0);
874 if (any_bits & UCOM_LS_BREAK)
875 sc->sc_callback->ucom_cfg_set_break(sc,
876 (last_value & UCOM_LS_BREAK) ? 1 : 0);
877 if (any_bits & UCOM_LS_RING)
878 sc->sc_callback->ucom_cfg_set_ring(sc,
879 (last_value & UCOM_LS_RING) ? 1 : 0);
883 ucom_line_state(struct ucom_softc *sc,
884 uint8_t set_bits, uint8_t clear_bits)
886 KKASSERT(lockowned(sc->sc_lock));
888 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
892 DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
894 /* update current programmed line state */
895 sc->sc_pls_curr |= set_bits;
896 sc->sc_pls_curr &= ~clear_bits;
897 sc->sc_pls_set |= set_bits;
898 sc->sc_pls_clr |= clear_bits;
900 /* defer driver programming */
901 ucom_queue_command(sc, ucom_cfg_line_state, NULL,
902 &sc->sc_line_state_task[0].hdr,
903 &sc->sc_line_state_task[1].hdr);
907 ucom_ring(struct ucom_softc *sc, uint8_t onoff)
909 DPRINTF("onoff = %d\n", onoff);
912 ucom_line_state(sc, UCOM_LS_RING, 0);
914 ucom_line_state(sc, 0, UCOM_LS_RING);
918 ucom_break(struct ucom_softc *sc, uint8_t onoff)
920 DPRINTF("onoff = %d\n", onoff);
923 ucom_line_state(sc, UCOM_LS_BREAK, 0);
925 ucom_line_state(sc, 0, UCOM_LS_BREAK);
929 ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
931 DPRINTF("onoff = %d\n", onoff);
934 ucom_line_state(sc, UCOM_LS_DTR, 0);
936 ucom_line_state(sc, 0, UCOM_LS_DTR);
940 ucom_rts(struct ucom_softc *sc, uint8_t onoff)
942 DPRINTF("onoff = %d\n", onoff);
945 ucom_line_state(sc, UCOM_LS_RTS, 0);
947 ucom_line_state(sc, 0, UCOM_LS_RTS);
951 ucom_cfg_status_change(struct usb_proc_msg *_task)
953 struct ucom_cfg_task *task =
954 (struct ucom_cfg_task *)_task;
955 struct ucom_softc *sc = task->sc;
964 KKASSERT(lockowned(sc->sc_lock));
966 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
969 if (sc->sc_callback->ucom_cfg_get_status == NULL) {
977 (sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
979 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
980 /* TTY device closed */
983 onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
984 lsr_delta = (sc->sc_lsr ^ new_lsr);
986 sc->sc_msr = new_msr;
987 sc->sc_lsr = new_lsr;
991 onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
993 DPRINTF("DCD changed to %d\n", onoff);
995 ttydisc_modem(tp, onoff);
998 if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
1000 DPRINTF("BREAK detected\n");
1002 ttydisc_rint(tp, 0, TRE_BREAK);
1003 ttydisc_rint_done(tp);
1006 if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) {
1008 DPRINTF("Frame error detected\n");
1010 ttydisc_rint(tp, 0, TRE_FRAMING);
1011 ttydisc_rint_done(tp);
1014 if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) {
1016 DPRINTF("Parity error detected\n");
1018 ttydisc_rint(tp, 0, TRE_PARITY);
1019 ttydisc_rint_done(tp);
1024 ucom_status_change(struct ucom_softc *sc)
1026 KKASSERT(lockowned(sc->sc_lock));
1028 if (sc->sc_flag & UCOM_FLAG_CONSOLE)
1029 return; /* not supported */
1031 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1036 ucom_queue_command(sc, ucom_cfg_status_change, NULL,
1037 &sc->sc_status_task[0].hdr,
1038 &sc->sc_status_task[1].hdr);
1042 ucom_cfg_param(struct usb_proc_msg *_task)
1044 struct ucom_param_task *task =
1045 (struct ucom_param_task *)_task;
1046 struct ucom_softc *sc = task->sc;
1048 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1051 if (sc->sc_callback->ucom_cfg_param == NULL) {
1055 (sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
1058 usb_pause_mtx(sc->sc_lock, hz / 10);
1062 ucom_param(struct tty *tp, struct termios *t)
1064 struct ucom_softc *sc = tty_softc(tp);
1068 KKASSERT(lockowned(sc->sc_lock));
1073 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1075 /* XXX the TTY layer should call "open()" first! */
1077 error = ucom_open(tp);
1083 DPRINTF("sc = %p\n", sc);
1085 /* Check requested parameters. */
1086 if (t->c_ospeed < 0) {
1087 DPRINTF("negative ospeed\n");
1091 if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
1092 DPRINTF("mismatch ispeed and ospeed\n");
1096 t->c_ispeed = t->c_ospeed;
1098 if (sc->sc_callback->ucom_pre_param) {
1099 /* Let the lower layer verify the parameters */
1100 error = (sc->sc_callback->ucom_pre_param) (sc, t);
1102 DPRINTF("callback error = %d\n", error);
1107 /* Disable transfers */
1108 sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
1110 /* Queue baud rate programming command first */
1111 ucom_queue_command(sc, ucom_cfg_param, t,
1112 &sc->sc_param_task[0].hdr,
1113 &sc->sc_param_task[1].hdr);
1115 /* Queue transfer enable command last */
1116 ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
1117 &sc->sc_start_task[0].hdr,
1118 &sc->sc_start_task[1].hdr);
1120 if (t->c_cflag & CRTS_IFLOW) {
1121 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
1122 } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
1123 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
1124 ucom_modem(tp, SER_RTS, 0);
1136 ucom_outwakeup(struct tty *tp)
1138 struct ucom_softc *sc = tty_softc(tp);
1140 KKASSERT(lockowned(sc->sc_lock));
1142 DPRINTF("sc = %p\n", sc);
1144 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1145 /* The higher layer is not ready */
1148 ucom_start_transfers(sc);
1151 /*------------------------------------------------------------------------*
1155 * 0: No data is available.
1156 * Else: Data is available.
1157 *------------------------------------------------------------------------*/
1159 ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1160 uint32_t offset, uint32_t len, uint32_t *actlen)
1162 struct usb_page_search res;
1163 struct tty *tp = sc->sc_tty;
1165 uint32_t offset_orig;
1167 KKASSERT(lockowned(sc->sc_lock));
1169 if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1172 /* get total TX length */
1174 temp = ucom_cons_tx_high - ucom_cons_tx_low;
1175 temp %= UCOM_CONS_BUFSIZE;
1177 /* limit TX length */
1179 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low))
1180 temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
1187 usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
1189 /* update counters */
1191 ucom_cons_tx_low += temp;
1192 ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
1194 /* store actual length */
1198 return (temp ? 1 : 0);
1202 !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
1204 return (0); /* multiport device polling */
1206 offset_orig = offset;
1210 usbd_get_page(pc, offset, &res);
1212 if (res.length > len) {
1215 /* copy data directly into USB buffer */
1216 cnt = ttydisc_getc(tp, res.buffer, res.length);
1221 if (cnt < res.length) {
1227 actlen[0] = offset - offset_orig;
1229 DPRINTF("cnt=%d\n", actlen[0]);
1231 if (actlen[0] == 0) {
1238 ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1239 uint32_t offset, uint32_t len)
1241 struct usb_page_search res;
1242 struct tty *tp = sc->sc_tty;
1246 KKASSERT(lockowned(sc->sc_lock));
1248 if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1251 /* get maximum RX length */
1253 temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
1254 temp %= UCOM_CONS_BUFSIZE;
1256 /* limit RX length */
1258 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high))
1259 temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
1266 usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
1268 /* update counters */
1270 ucom_cons_rx_high += temp;
1271 ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
1277 return; /* multiport device polling */
1280 return; /* no data */
1282 /* set a flag to prevent recursation ? */
1286 usbd_get_page(pc, offset, &res);
1288 if (res.length > len) {
1292 offset += res.length;
1294 /* pass characters to tty layer */
1299 /* first check if we can pass the buffer directly */
1301 if (ttydisc_can_bypass(tp)) {
1302 if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1303 DPRINTF("tp=%p, data lost\n", tp);
1309 for (cnt = 0; cnt != res.length; cnt++) {
1310 if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
1311 /* XXX what should we do? */
1313 DPRINTF("tp=%p, lost %d "
1314 "chars\n", tp, res.length - cnt);
1319 ttydisc_rint_done(tp);
1323 ucom_free(void *xsc)
1325 struct ucom_softc *sc = xsc;
1327 lockmgr(sc->sc_lock, LK_EXCLUSIVE);
1328 sc->sc_ttyfreed = 1;
1329 cv_signal(&sc->sc_cv);
1330 lockmgr(sc->sc_lock, LK_RELEASE);
1333 static cn_probe_t ucom_cnprobe;
1334 static cn_init_t ucom_cninit;
1335 static cn_term_t ucom_cnterm;
1336 static cn_getc_t ucom_cngetc;
1337 static cn_putc_t ucom_cnputc;
1338 static cn_grab_t ucom_cngrab;
1339 static cn_ungrab_t ucom_cnungrab;
1341 CONSOLE_DRIVER(ucom);
1344 ucom_cnprobe(struct consdev *cp)
1346 if (ucom_cons_unit != -1)
1347 cp->cn_pri = CN_NORMAL;
1349 cp->cn_pri = CN_DEAD;
1351 strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name));
1355 ucom_cninit(struct consdev *cp)
1360 ucom_cnterm(struct consdev *cp)
1365 ucom_cngrab(struct consdev *cp)
1370 ucom_cnungrab(struct consdev *cp)
1375 ucom_cngetc(struct consdev *cd)
1377 struct ucom_softc *sc = ucom_cons_softc;
1383 lockmgr(sc->sc_lock, LK_EXCLUSIVE);
1385 if (ucom_cons_rx_low != ucom_cons_rx_high) {
1386 c = ucom_cons_rx_buf[ucom_cons_rx_low];
1387 ucom_cons_rx_low ++;
1388 ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
1393 /* start USB transfers */
1394 ucom_outwakeup(sc->sc_tty);
1396 lockmgr(sc->sc_lock, LK_RELEASE);
1398 /* poll if necessary */
1399 if (kdb_active && sc->sc_callback->ucom_poll)
1400 (sc->sc_callback->ucom_poll) (sc);
1406 ucom_cnputc(struct consdev *cd, int c)
1408 struct ucom_softc *sc = ucom_cons_softc;
1416 lockmgr(sc->sc_lock, LK_EXCLUSIVE);
1418 /* compute maximum TX length */
1420 temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
1421 temp %= UCOM_CONS_BUFSIZE;
1424 ucom_cons_tx_buf[ucom_cons_tx_high] = c;
1425 ucom_cons_tx_high ++;
1426 ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
1429 /* start USB transfers */
1430 ucom_outwakeup(sc->sc_tty);
1432 lockmgr(sc->sc_lock, LK_RELEASE);
1434 /* poll if necessary */
1435 if (kdb_active && sc->sc_callback->ucom_poll) {
1436 (sc->sc_callback->ucom_poll) (sc);
1437 /* simple flow control */
1445 #include <gdb/gdb.h>
1447 static gdb_probe_f ucom_gdbprobe;
1448 static gdb_init_f ucom_gdbinit;
1449 static gdb_term_f ucom_gdbterm;
1450 static gdb_getc_f ucom_gdbgetc;
1451 static gdb_putc_f ucom_gdbputc;
1453 GDB_DBGPORT(sio, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc);
1458 return ((ucom_cons_softc != NULL) ? 0 : -1);
1474 ucom_cnputc(NULL, c);
1480 return (ucom_cngetc(NULL));