xref: /freebsd/sys/dev/usb/serial/usb_serial.c (revision 2608aefc0b9af62b8a8f3120bc94fd86fefd46fd)
1 /*	$NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $	*/
2 
3 /*-
4  * Copyright (c) 2001-2003, 2005, 2008
5  *	Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 /*-
34  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
35  * All rights reserved.
36  *
37  * This code is derived from software contributed to The NetBSD Foundation
38  * by Lennart Augustsson (lennart@augustsson.net) at
39  * Carlstedt Research & Technology.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. All advertising materials mentioning features or use of this software
50  *    must display the following acknowledgement:
51  *        This product includes software developed by the NetBSD
52  *        Foundation, Inc. and its contributors.
53  * 4. Neither the name of The NetBSD Foundation nor the names of its
54  *    contributors may be used to endorse or promote products derived
55  *    from this software without specific prior written permission.
56  *
57  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
58  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
59  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
60  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
61  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
62  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
63  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
64  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
66  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
67  * POSSIBILITY OF SUCH DAMAGE.
68  */
69 
70 #include <sys/stdint.h>
71 #include <sys/stddef.h>
72 #include <sys/param.h>
73 #include <sys/queue.h>
74 #include <sys/types.h>
75 #include <sys/systm.h>
76 #include <sys/kernel.h>
77 #include <sys/bus.h>
78 #include <sys/linker_set.h>
79 #include <sys/module.h>
80 #include <sys/lock.h>
81 #include <sys/mutex.h>
82 #include <sys/condvar.h>
83 #include <sys/sysctl.h>
84 #include <sys/sx.h>
85 #include <sys/unistd.h>
86 #include <sys/callout.h>
87 #include <sys/malloc.h>
88 #include <sys/priv.h>
89 #include <sys/cons.h>
90 #include <sys/kdb.h>
91 
92 #include <dev/usb/usb.h>
93 #include <dev/usb/usbdi.h>
94 #include <dev/usb/usbdi_util.h>
95 
96 #define	USB_DEBUG_VAR ucom_debug
97 #include <dev/usb/usb_debug.h>
98 #include <dev/usb/usb_busdma.h>
99 #include <dev/usb/usb_process.h>
100 
101 #include <dev/usb/serial/usb_serial.h>
102 
103 #include "opt_gdb.h"
104 
105 SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
106 
107 #ifdef USB_DEBUG
108 static int ucom_debug = 0;
109 
110 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
111     &ucom_debug, 0, "ucom debug level");
112 #endif
113 
114 #define	UCOM_CONS_BUFSIZE 1024
115 
116 static uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE];
117 static uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE];
118 
119 static unsigned int ucom_cons_rx_low = 0;
120 static unsigned int ucom_cons_rx_high = 0;
121 
122 static unsigned int ucom_cons_tx_low = 0;
123 static unsigned int ucom_cons_tx_high = 0;
124 
125 static int ucom_cons_unit = -1;
126 static int ucom_cons_subunit = 0;
127 static int ucom_cons_baud = 9600;
128 static struct ucom_softc *ucom_cons_softc = NULL;
129 
130 TUNABLE_INT("hw.usb.ucom.cons_unit", &ucom_cons_unit);
131 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RW,
132     &ucom_cons_unit, 0, "console unit number");
133 TUNABLE_INT("hw.usb.ucom.cons_subunit", &ucom_cons_subunit);
134 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_subunit, CTLFLAG_RW,
135     &ucom_cons_subunit, 0, "console subunit number");
136 TUNABLE_INT("hw.usb.ucom.cons_baud", &ucom_cons_baud);
137 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RW,
138     &ucom_cons_baud, 0, "console baud rate");
139 
140 static usb_proc_callback_t ucom_cfg_start_transfers;
141 static usb_proc_callback_t ucom_cfg_open;
142 static usb_proc_callback_t ucom_cfg_close;
143 static usb_proc_callback_t ucom_cfg_line_state;
144 static usb_proc_callback_t ucom_cfg_status_change;
145 static usb_proc_callback_t ucom_cfg_param;
146 
147 static int	ucom_unit_alloc(void);
148 static void	ucom_unit_free(int);
149 static int	ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *);
150 static void	ucom_detach_tty(struct ucom_softc *);
151 static void	ucom_queue_command(struct ucom_softc *,
152 		    usb_proc_callback_t *, struct termios *pt,
153 		    struct usb_proc_msg *t0, struct usb_proc_msg *t1);
154 static void	ucom_shutdown(struct ucom_softc *);
155 static void	ucom_ring(struct ucom_softc *, uint8_t);
156 static void	ucom_break(struct ucom_softc *, uint8_t);
157 static void	ucom_dtr(struct ucom_softc *, uint8_t);
158 static void	ucom_rts(struct ucom_softc *, uint8_t);
159 
160 static tsw_open_t ucom_open;
161 static tsw_close_t ucom_close;
162 static tsw_ioctl_t ucom_ioctl;
163 static tsw_modem_t ucom_modem;
164 static tsw_param_t ucom_param;
165 static tsw_outwakeup_t ucom_outwakeup;
166 static tsw_free_t ucom_free;
167 
168 static struct ttydevsw ucom_class = {
169 	.tsw_flags = TF_INITLOCK | TF_CALLOUT,
170 	.tsw_open = ucom_open,
171 	.tsw_close = ucom_close,
172 	.tsw_outwakeup = ucom_outwakeup,
173 	.tsw_ioctl = ucom_ioctl,
174 	.tsw_param = ucom_param,
175 	.tsw_modem = ucom_modem,
176 	.tsw_free = ucom_free,
177 };
178 
179 MODULE_DEPEND(ucom, usb, 1, 1, 1);
180 MODULE_VERSION(ucom, 1);
181 
182 #define	UCOM_UNIT_MAX 		128	/* limits size of ucom_bitmap */
183 
184 static uint8_t ucom_bitmap[(UCOM_UNIT_MAX + 7) / 8];
185 static struct mtx ucom_bitmap_mtx;
186 MTX_SYSINIT(ucom_bitmap_mtx, &ucom_bitmap_mtx, "ucom bitmap", MTX_DEF);
187 
188 #define UCOM_TTY_PREFIX		"U"
189 
190 /*
191  * Mark a unit number (the X in cuaUX) as in use.
192  *
193  * Note that devices using a different naming scheme (see ucom_tty_name()
194  * callback) still use this unit allocation.
195  */
196 static int
197 ucom_unit_alloc(void)
198 {
199 	int unit;
200 
201 	mtx_lock(&ucom_bitmap_mtx);
202 
203 	for (unit = 0; unit < UCOM_UNIT_MAX; unit++) {
204 		if ((ucom_bitmap[unit / 8] & (1 << (unit % 8))) == 0) {
205 			ucom_bitmap[unit / 8] |= (1 << (unit % 8));
206 			break;
207 		}
208 	}
209 
210 	mtx_unlock(&ucom_bitmap_mtx);
211 
212 	if (unit == UCOM_UNIT_MAX)
213 		return -1;
214 	else
215 		return unit;
216 }
217 
218 /*
219  * Mark the unit number as not in use.
220  */
221 static void
222 ucom_unit_free(int unit)
223 {
224 	mtx_lock(&ucom_bitmap_mtx);
225 
226 	ucom_bitmap[unit / 8] &= ~(1 << (unit % 8));
227 
228 	mtx_unlock(&ucom_bitmap_mtx);
229 }
230 
231 /*
232  * Setup a group of one or more serial ports.
233  *
234  * The mutex pointed to by "mtx" is applied before all
235  * callbacks are called back. Also "mtx" must be applied
236  * before calling into the ucom-layer!
237  */
238 int
239 ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
240     uint32_t subunits, void *parent,
241     const struct ucom_callback *callback, struct mtx *mtx)
242 {
243 	uint32_t subunit;
244 	int error = 0;
245 
246 	if ((sc == NULL) ||
247 	    (subunits == 0) ||
248 	    (callback == NULL)) {
249 		return (EINVAL);
250 	}
251 
252 	ssc->sc_unit = ucom_unit_alloc();
253 	if (ssc->sc_unit == -1)
254 		return (ENOMEM);
255 
256 	error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
257 	if (error) {
258 		ucom_unit_free(ssc->sc_unit);
259 		return (error);
260 	}
261 	ssc->sc_subunits = subunits;
262 
263 	for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
264 		sc[subunit].sc_subunit = subunit;
265 		sc[subunit].sc_super = ssc;
266 		sc[subunit].sc_mtx = mtx;
267 		sc[subunit].sc_parent = parent;
268 		sc[subunit].sc_callback = callback;
269 
270 		error = ucom_attach_tty(ssc, &sc[subunit]);
271 		if (error) {
272 			ucom_detach(ssc, &sc[0]);
273 			return (error);
274 		}
275 		sc[subunit].sc_flag |= UCOM_FLAG_ATTACHED;
276 	}
277 
278 	DPRINTF("tp = %p, unit = %d, subunits = %d\n",
279 		sc->sc_tty, ssc->sc_unit, ssc->sc_subunits);
280 
281 	return (0);
282 }
283 
284 /*
285  * NOTE: the following function will do nothing if
286  * the structure pointed to by "ssc" and "sc" is zero.
287  */
288 void
289 ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc)
290 {
291 	uint32_t subunit;
292 
293 	usb_proc_drain(&ssc->sc_tq);
294 
295 	for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
296 		if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) {
297 
298 			ucom_detach_tty(&sc[subunit]);
299 
300 			/* avoid duplicate detach */
301 			sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED;
302 		}
303 	}
304 	ucom_unit_free(ssc->sc_unit);
305 	usb_proc_free(&ssc->sc_tq);
306 }
307 
308 static int
309 ucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
310 {
311 	struct tty *tp;
312 	char buf[32];			/* temporary TTY device name buffer */
313 
314 	tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx);
315 	if (tp == NULL)
316 		return (ENOMEM);
317 
318 	/* Check if the client has a custom TTY name */
319 	buf[0] = '\0';
320 	if (sc->sc_callback->ucom_tty_name) {
321 		sc->sc_callback->ucom_tty_name(sc, buf,
322 		    sizeof(buf), ssc->sc_unit, sc->sc_subunit);
323 	}
324 	if (buf[0] == 0) {
325 		/* Use default TTY name */
326 		if (ssc->sc_subunits > 1) {
327 			/* multiple modems in one */
328 			snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u.%u",
329 			    ssc->sc_unit, sc->sc_subunit);
330 		} else {
331 			/* single modem */
332 			snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u",
333 			    ssc->sc_unit);
334 		}
335 	}
336 	tty_makedev(tp, NULL, "%s", buf);
337 
338 	sc->sc_tty = tp;
339 
340 	DPRINTF("ttycreate: %s\n", buf);
341 	cv_init(&sc->sc_cv, "ucom");
342 
343 	/* Check if this device should be a console */
344 	if ((ucom_cons_softc == NULL) &&
345 	    (ssc->sc_unit == ucom_cons_unit) &&
346 	    (sc->sc_subunit == ucom_cons_subunit)) {
347 		struct termios t;
348 
349 		DPRINTF("unit %d subunit %d is console", ssc->sc_unit, sc->sc_subunit);
350 
351 		ucom_cons_softc = sc;
352 
353 		memset(&t, 0, sizeof(t));
354 		t.c_ispeed = ucom_cons_baud;
355 		t.c_ospeed = t.c_ispeed;
356 		t.c_cflag = CS8;
357 
358 		mtx_lock(ucom_cons_softc->sc_mtx);
359 		ucom_cons_rx_low = 0;
360 		ucom_cons_rx_high = 0;
361 		ucom_cons_tx_low = 0;
362 		ucom_cons_tx_high = 0;
363 		sc->sc_flag |= UCOM_FLAG_CONSOLE;
364 		ucom_open(ucom_cons_softc->sc_tty);
365 		ucom_param(ucom_cons_softc->sc_tty, &t);
366 		mtx_unlock(ucom_cons_softc->sc_mtx);
367 	}
368 
369 	return (0);
370 }
371 
372 static void
373 ucom_detach_tty(struct ucom_softc *sc)
374 {
375 	struct tty *tp = sc->sc_tty;
376 
377 	DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
378 
379 	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
380 		mtx_lock(ucom_cons_softc->sc_mtx);
381 		ucom_close(ucom_cons_softc->sc_tty);
382 		sc->sc_flag &= ~UCOM_FLAG_CONSOLE;
383 		mtx_unlock(ucom_cons_softc->sc_mtx);
384 		ucom_cons_softc = NULL;
385 	}
386 
387 	/* the config thread has been stopped when we get here */
388 
389 	mtx_lock(sc->sc_mtx);
390 	sc->sc_flag |= UCOM_FLAG_GONE;
391 	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY);
392 	mtx_unlock(sc->sc_mtx);
393 	if (tp) {
394 		tty_lock(tp);
395 
396 		ucom_close(tp);	/* close, if any */
397 
398 		tty_rel_gone(tp);
399 
400 		mtx_lock(sc->sc_mtx);
401 		/* Wait for the callback after the TTY is torn down */
402 		while (sc->sc_ttyfreed == 0)
403 			cv_wait(&sc->sc_cv, sc->sc_mtx);
404 		/*
405 		 * make sure that read and write transfers are stopped
406 		 */
407 		if (sc->sc_callback->ucom_stop_read) {
408 			(sc->sc_callback->ucom_stop_read) (sc);
409 		}
410 		if (sc->sc_callback->ucom_stop_write) {
411 			(sc->sc_callback->ucom_stop_write) (sc);
412 		}
413 		mtx_unlock(sc->sc_mtx);
414 	}
415 	cv_destroy(&sc->sc_cv);
416 }
417 
418 void
419 ucom_set_pnpinfo_usb(struct ucom_super_softc *ssc, device_t dev)
420 {
421     char buf[64];
422     uint8_t iface_index;
423     struct usb_attach_arg *uaa;
424 
425     snprintf(buf, sizeof(buf), "ttyname=%s%d ttyports=%d",
426 	     UCOM_TTY_PREFIX, ssc->sc_unit, ssc->sc_subunits);
427 
428     /* Store the PNP info in the first interface for the dev */
429     uaa = device_get_ivars(dev);
430     iface_index = uaa->info.bIfaceIndex;
431 
432     if (usbd_set_pnpinfo(uaa->device, iface_index, buf) != 0)
433 	device_printf(dev, "Could not set PNP info\n");
434 }
435 
436 static void
437 ucom_queue_command(struct ucom_softc *sc,
438     usb_proc_callback_t *fn, struct termios *pt,
439     struct usb_proc_msg *t0, struct usb_proc_msg *t1)
440 {
441 	struct ucom_super_softc *ssc = sc->sc_super;
442 	struct ucom_param_task *task;
443 
444 	mtx_assert(sc->sc_mtx, MA_OWNED);
445 
446 	if (usb_proc_is_gone(&ssc->sc_tq)) {
447 		DPRINTF("proc is gone\n");
448 		return;         /* nothing to do */
449 	}
450 	/*
451 	 * NOTE: The task cannot get executed before we drop the
452 	 * "sc_mtx" mutex. It is safe to update fields in the message
453 	 * structure after that the message got queued.
454 	 */
455 	task = (struct ucom_param_task *)
456 	  usb_proc_msignal(&ssc->sc_tq, t0, t1);
457 
458 	/* Setup callback and softc pointers */
459 	task->hdr.pm_callback = fn;
460 	task->sc = sc;
461 
462 	/*
463 	 * Make a copy of the termios. This field is only present if
464 	 * the "pt" field is not NULL.
465 	 */
466 	if (pt != NULL)
467 		task->termios_copy = *pt;
468 
469 	/*
470 	 * Closing the device should be synchronous.
471 	 */
472 	if (fn == ucom_cfg_close)
473 		usb_proc_mwait(&ssc->sc_tq, t0, t1);
474 
475 	/*
476 	 * In case of multiple configure requests,
477 	 * keep track of the last one!
478 	 */
479 	if (fn == ucom_cfg_start_transfers)
480 		sc->sc_last_start_xfer = &task->hdr;
481 }
482 
483 static void
484 ucom_shutdown(struct ucom_softc *sc)
485 {
486 	struct tty *tp = sc->sc_tty;
487 
488 	mtx_assert(sc->sc_mtx, MA_OWNED);
489 
490 	DPRINTF("\n");
491 
492 	/*
493 	 * Hang up if necessary:
494 	 */
495 	if (tp->t_termios.c_cflag & HUPCL) {
496 		ucom_modem(tp, 0, SER_DTR);
497 	}
498 }
499 
500 /*
501  * Return values:
502  *    0: normal
503  * else: taskqueue is draining or gone
504  */
505 uint8_t
506 ucom_cfg_is_gone(struct ucom_softc *sc)
507 {
508 	struct ucom_super_softc *ssc = sc->sc_super;
509 
510 	return (usb_proc_is_gone(&ssc->sc_tq));
511 }
512 
513 static void
514 ucom_cfg_start_transfers(struct usb_proc_msg *_task)
515 {
516 	struct ucom_cfg_task *task =
517 	    (struct ucom_cfg_task *)_task;
518 	struct ucom_softc *sc = task->sc;
519 
520 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
521 		return;
522 	}
523 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
524 		/* TTY device closed */
525 		return;
526 	}
527 
528 	if (_task == sc->sc_last_start_xfer)
529 		sc->sc_flag |= UCOM_FLAG_GP_DATA;
530 
531 	if (sc->sc_callback->ucom_start_read) {
532 		(sc->sc_callback->ucom_start_read) (sc);
533 	}
534 	if (sc->sc_callback->ucom_start_write) {
535 		(sc->sc_callback->ucom_start_write) (sc);
536 	}
537 }
538 
539 static void
540 ucom_start_transfers(struct ucom_softc *sc)
541 {
542 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
543 		return;
544 	}
545 	/*
546 	 * Make sure that data transfers are started in both
547 	 * directions:
548 	 */
549 	if (sc->sc_callback->ucom_start_read) {
550 		(sc->sc_callback->ucom_start_read) (sc);
551 	}
552 	if (sc->sc_callback->ucom_start_write) {
553 		(sc->sc_callback->ucom_start_write) (sc);
554 	}
555 }
556 
557 static void
558 ucom_cfg_open(struct usb_proc_msg *_task)
559 {
560 	struct ucom_cfg_task *task =
561 	    (struct ucom_cfg_task *)_task;
562 	struct ucom_softc *sc = task->sc;
563 
564 	DPRINTF("\n");
565 
566 	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
567 
568 		/* already opened */
569 
570 	} else {
571 
572 		sc->sc_flag |= UCOM_FLAG_LL_READY;
573 
574 		if (sc->sc_callback->ucom_cfg_open) {
575 			(sc->sc_callback->ucom_cfg_open) (sc);
576 
577 			/* wait a little */
578 			usb_pause_mtx(sc->sc_mtx, hz / 10);
579 		}
580 	}
581 }
582 
583 static int
584 ucom_open(struct tty *tp)
585 {
586 	struct ucom_softc *sc = tty_softc(tp);
587 	int error;
588 
589 	mtx_assert(sc->sc_mtx, MA_OWNED);
590 
591 	if (sc->sc_flag & UCOM_FLAG_GONE) {
592 		return (ENXIO);
593 	}
594 	if (sc->sc_flag & UCOM_FLAG_HL_READY) {
595 		/* already opened */
596 		return (0);
597 	}
598 	DPRINTF("tp = %p\n", tp);
599 
600 	if (sc->sc_callback->ucom_pre_open) {
601 		/*
602 		 * give the lower layer a chance to disallow TTY open, for
603 		 * example if the device is not present:
604 		 */
605 		error = (sc->sc_callback->ucom_pre_open) (sc);
606 		if (error) {
607 			return (error);
608 		}
609 	}
610 	sc->sc_flag |= UCOM_FLAG_HL_READY;
611 
612 	/* Disable transfers */
613 	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
614 
615 	sc->sc_lsr = 0;
616 	sc->sc_msr = 0;
617 	sc->sc_mcr = 0;
618 
619 	/* reset programmed line state */
620 	sc->sc_pls_curr = 0;
621 	sc->sc_pls_set = 0;
622 	sc->sc_pls_clr = 0;
623 
624 	ucom_queue_command(sc, ucom_cfg_open, NULL,
625 	    &sc->sc_open_task[0].hdr,
626 	    &sc->sc_open_task[1].hdr);
627 
628 	/* Queue transfer enable command last */
629 	ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
630 	    &sc->sc_start_task[0].hdr,
631 	    &sc->sc_start_task[1].hdr);
632 
633 	ucom_modem(tp, SER_DTR | SER_RTS, 0);
634 
635 	ucom_ring(sc, 0);
636 
637 	ucom_break(sc, 0);
638 
639 	ucom_status_change(sc);
640 
641 	return (0);
642 }
643 
644 static void
645 ucom_cfg_close(struct usb_proc_msg *_task)
646 {
647 	struct ucom_cfg_task *task =
648 	    (struct ucom_cfg_task *)_task;
649 	struct ucom_softc *sc = task->sc;
650 
651 	DPRINTF("\n");
652 
653 	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
654 		sc->sc_flag &= ~UCOM_FLAG_LL_READY;
655 		if (sc->sc_callback->ucom_cfg_close)
656 			(sc->sc_callback->ucom_cfg_close) (sc);
657 	} else {
658 		/* already closed */
659 	}
660 }
661 
662 static void
663 ucom_close(struct tty *tp)
664 {
665 	struct ucom_softc *sc = tty_softc(tp);
666 
667 	mtx_assert(sc->sc_mtx, MA_OWNED);
668 
669 	DPRINTF("tp=%p\n", tp);
670 
671 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
672 		DPRINTF("tp=%p already closed\n", tp);
673 		return;
674 	}
675 	ucom_shutdown(sc);
676 
677 	ucom_queue_command(sc, ucom_cfg_close, NULL,
678 	    &sc->sc_close_task[0].hdr,
679 	    &sc->sc_close_task[1].hdr);
680 
681 	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
682 
683 	if (sc->sc_callback->ucom_stop_read) {
684 		(sc->sc_callback->ucom_stop_read) (sc);
685 	}
686 }
687 
688 static int
689 ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
690 {
691 	struct ucom_softc *sc = tty_softc(tp);
692 	int error;
693 
694 	mtx_assert(sc->sc_mtx, MA_OWNED);
695 
696 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
697 		return (EIO);
698 	}
699 	DPRINTF("cmd = 0x%08lx\n", cmd);
700 
701 	switch (cmd) {
702 #if 0
703 	case TIOCSRING:
704 		ucom_ring(sc, 1);
705 		error = 0;
706 		break;
707 	case TIOCCRING:
708 		ucom_ring(sc, 0);
709 		error = 0;
710 		break;
711 #endif
712 	case TIOCSBRK:
713 		ucom_break(sc, 1);
714 		error = 0;
715 		break;
716 	case TIOCCBRK:
717 		ucom_break(sc, 0);
718 		error = 0;
719 		break;
720 	default:
721 		if (sc->sc_callback->ucom_ioctl) {
722 			error = (sc->sc_callback->ucom_ioctl)
723 			    (sc, cmd, data, 0, td);
724 		} else {
725 			error = ENOIOCTL;
726 		}
727 		break;
728 	}
729 	return (error);
730 }
731 
732 static int
733 ucom_modem(struct tty *tp, int sigon, int sigoff)
734 {
735 	struct ucom_softc *sc = tty_softc(tp);
736 	uint8_t onoff;
737 
738 	mtx_assert(sc->sc_mtx, MA_OWNED);
739 
740 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
741 		return (0);
742 	}
743 	if ((sigon == 0) && (sigoff == 0)) {
744 
745 		if (sc->sc_mcr & SER_DTR) {
746 			sigon |= SER_DTR;
747 		}
748 		if (sc->sc_mcr & SER_RTS) {
749 			sigon |= SER_RTS;
750 		}
751 		if (sc->sc_msr & SER_CTS) {
752 			sigon |= SER_CTS;
753 		}
754 		if (sc->sc_msr & SER_DCD) {
755 			sigon |= SER_DCD;
756 		}
757 		if (sc->sc_msr & SER_DSR) {
758 			sigon |= SER_DSR;
759 		}
760 		if (sc->sc_msr & SER_RI) {
761 			sigon |= SER_RI;
762 		}
763 		return (sigon);
764 	}
765 	if (sigon & SER_DTR) {
766 		sc->sc_mcr |= SER_DTR;
767 	}
768 	if (sigoff & SER_DTR) {
769 		sc->sc_mcr &= ~SER_DTR;
770 	}
771 	if (sigon & SER_RTS) {
772 		sc->sc_mcr |= SER_RTS;
773 	}
774 	if (sigoff & SER_RTS) {
775 		sc->sc_mcr &= ~SER_RTS;
776 	}
777 	onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
778 	ucom_dtr(sc, onoff);
779 
780 	onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
781 	ucom_rts(sc, onoff);
782 
783 	return (0);
784 }
785 
786 static void
787 ucom_cfg_line_state(struct usb_proc_msg *_task)
788 {
789 	struct ucom_cfg_task *task =
790 	    (struct ucom_cfg_task *)_task;
791 	struct ucom_softc *sc = task->sc;
792 	uint8_t notch_bits;
793 	uint8_t any_bits;
794 	uint8_t prev_value;
795 	uint8_t last_value;
796 	uint8_t mask;
797 
798 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
799 		return;
800 	}
801 
802 	mask = 0;
803 	/* compute callback mask */
804 	if (sc->sc_callback->ucom_cfg_set_dtr)
805 		mask |= UCOM_LS_DTR;
806 	if (sc->sc_callback->ucom_cfg_set_rts)
807 		mask |= UCOM_LS_RTS;
808 	if (sc->sc_callback->ucom_cfg_set_break)
809 		mask |= UCOM_LS_BREAK;
810 	if (sc->sc_callback->ucom_cfg_set_ring)
811 		mask |= UCOM_LS_RING;
812 
813 	/* compute the bits we are to program */
814 	notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
815 	any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
816 	prev_value = sc->sc_pls_curr ^ notch_bits;
817 	last_value = sc->sc_pls_curr;
818 
819 	/* reset programmed line state */
820 	sc->sc_pls_curr = 0;
821 	sc->sc_pls_set = 0;
822 	sc->sc_pls_clr = 0;
823 
824 	/* ensure that we don't loose any levels */
825 	if (notch_bits & UCOM_LS_DTR)
826 		sc->sc_callback->ucom_cfg_set_dtr(sc,
827 		    (prev_value & UCOM_LS_DTR) ? 1 : 0);
828 	if (notch_bits & UCOM_LS_RTS)
829 		sc->sc_callback->ucom_cfg_set_rts(sc,
830 		    (prev_value & UCOM_LS_RTS) ? 1 : 0);
831 	if (notch_bits & UCOM_LS_BREAK)
832 		sc->sc_callback->ucom_cfg_set_break(sc,
833 		    (prev_value & UCOM_LS_BREAK) ? 1 : 0);
834 	if (notch_bits & UCOM_LS_RING)
835 		sc->sc_callback->ucom_cfg_set_ring(sc,
836 		    (prev_value & UCOM_LS_RING) ? 1 : 0);
837 
838 	/* set last value */
839 	if (any_bits & UCOM_LS_DTR)
840 		sc->sc_callback->ucom_cfg_set_dtr(sc,
841 		    (last_value & UCOM_LS_DTR) ? 1 : 0);
842 	if (any_bits & UCOM_LS_RTS)
843 		sc->sc_callback->ucom_cfg_set_rts(sc,
844 		    (last_value & UCOM_LS_RTS) ? 1 : 0);
845 	if (any_bits & UCOM_LS_BREAK)
846 		sc->sc_callback->ucom_cfg_set_break(sc,
847 		    (last_value & UCOM_LS_BREAK) ? 1 : 0);
848 	if (any_bits & UCOM_LS_RING)
849 		sc->sc_callback->ucom_cfg_set_ring(sc,
850 		    (last_value & UCOM_LS_RING) ? 1 : 0);
851 }
852 
853 static void
854 ucom_line_state(struct ucom_softc *sc,
855     uint8_t set_bits, uint8_t clear_bits)
856 {
857 	mtx_assert(sc->sc_mtx, MA_OWNED);
858 
859 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
860 		return;
861 	}
862 
863 	DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
864 
865 	/* update current programmed line state */
866 	sc->sc_pls_curr |= set_bits;
867 	sc->sc_pls_curr &= ~clear_bits;
868 	sc->sc_pls_set |= set_bits;
869 	sc->sc_pls_clr |= clear_bits;
870 
871 	/* defer driver programming */
872 	ucom_queue_command(sc, ucom_cfg_line_state, NULL,
873 	    &sc->sc_line_state_task[0].hdr,
874 	    &sc->sc_line_state_task[1].hdr);
875 }
876 
877 static void
878 ucom_ring(struct ucom_softc *sc, uint8_t onoff)
879 {
880 	DPRINTF("onoff = %d\n", onoff);
881 
882 	if (onoff)
883 		ucom_line_state(sc, UCOM_LS_RING, 0);
884 	else
885 		ucom_line_state(sc, 0, UCOM_LS_RING);
886 }
887 
888 static void
889 ucom_break(struct ucom_softc *sc, uint8_t onoff)
890 {
891 	DPRINTF("onoff = %d\n", onoff);
892 
893 	if (onoff)
894 		ucom_line_state(sc, UCOM_LS_BREAK, 0);
895 	else
896 		ucom_line_state(sc, 0, UCOM_LS_BREAK);
897 }
898 
899 static void
900 ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
901 {
902 	DPRINTF("onoff = %d\n", onoff);
903 
904 	if (onoff)
905 		ucom_line_state(sc, UCOM_LS_DTR, 0);
906 	else
907 		ucom_line_state(sc, 0, UCOM_LS_DTR);
908 }
909 
910 static void
911 ucom_rts(struct ucom_softc *sc, uint8_t onoff)
912 {
913 	DPRINTF("onoff = %d\n", onoff);
914 
915 	if (onoff)
916 		ucom_line_state(sc, UCOM_LS_RTS, 0);
917 	else
918 		ucom_line_state(sc, 0, UCOM_LS_RTS);
919 }
920 
921 static void
922 ucom_cfg_status_change(struct usb_proc_msg *_task)
923 {
924 	struct ucom_cfg_task *task =
925 	    (struct ucom_cfg_task *)_task;
926 	struct ucom_softc *sc = task->sc;
927 	struct tty *tp;
928 	uint8_t new_msr;
929 	uint8_t new_lsr;
930 	uint8_t onoff;
931 	uint8_t lsr_delta;
932 
933 	tp = sc->sc_tty;
934 
935 	mtx_assert(sc->sc_mtx, MA_OWNED);
936 
937 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
938 		return;
939 	}
940 	if (sc->sc_callback->ucom_cfg_get_status == NULL) {
941 		return;
942 	}
943 	/* get status */
944 
945 	new_msr = 0;
946 	new_lsr = 0;
947 
948 	(sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
949 
950 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
951 		/* TTY device closed */
952 		return;
953 	}
954 	onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
955 	lsr_delta = (sc->sc_lsr ^ new_lsr);
956 
957 	sc->sc_msr = new_msr;
958 	sc->sc_lsr = new_lsr;
959 
960 	if (onoff) {
961 
962 		onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
963 
964 		DPRINTF("DCD changed to %d\n", onoff);
965 
966 		ttydisc_modem(tp, onoff);
967 	}
968 
969 	if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
970 
971 		DPRINTF("BREAK detected\n");
972 
973 		ttydisc_rint(tp, 0, TRE_BREAK);
974 		ttydisc_rint_done(tp);
975 	}
976 
977 	if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) {
978 
979 		DPRINTF("Frame error detected\n");
980 
981 		ttydisc_rint(tp, 0, TRE_FRAMING);
982 		ttydisc_rint_done(tp);
983 	}
984 
985 	if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) {
986 
987 		DPRINTF("Parity error detected\n");
988 
989 		ttydisc_rint(tp, 0, TRE_PARITY);
990 		ttydisc_rint_done(tp);
991 	}
992 }
993 
994 void
995 ucom_status_change(struct ucom_softc *sc)
996 {
997 	mtx_assert(sc->sc_mtx, MA_OWNED);
998 
999 	if (sc->sc_flag & UCOM_FLAG_CONSOLE)
1000 		return;		/* not supported */
1001 
1002 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1003 		return;
1004 	}
1005 	DPRINTF("\n");
1006 
1007 	ucom_queue_command(sc, ucom_cfg_status_change, NULL,
1008 	    &sc->sc_status_task[0].hdr,
1009 	    &sc->sc_status_task[1].hdr);
1010 }
1011 
1012 static void
1013 ucom_cfg_param(struct usb_proc_msg *_task)
1014 {
1015 	struct ucom_param_task *task =
1016 	    (struct ucom_param_task *)_task;
1017 	struct ucom_softc *sc = task->sc;
1018 
1019 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1020 		return;
1021 	}
1022 	if (sc->sc_callback->ucom_cfg_param == NULL) {
1023 		return;
1024 	}
1025 
1026 	(sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
1027 
1028 	/* wait a little */
1029 	usb_pause_mtx(sc->sc_mtx, hz / 10);
1030 }
1031 
1032 static int
1033 ucom_param(struct tty *tp, struct termios *t)
1034 {
1035 	struct ucom_softc *sc = tty_softc(tp);
1036 	uint8_t opened;
1037 	int error;
1038 
1039 	mtx_assert(sc->sc_mtx, MA_OWNED);
1040 
1041 	opened = 0;
1042 	error = 0;
1043 
1044 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1045 
1046 		/* XXX the TTY layer should call "open()" first! */
1047 
1048 		error = ucom_open(tp);
1049 		if (error) {
1050 			goto done;
1051 		}
1052 		opened = 1;
1053 	}
1054 	DPRINTF("sc = %p\n", sc);
1055 
1056 	/* Check requested parameters. */
1057 	if (t->c_ospeed < 0) {
1058 		DPRINTF("negative ospeed\n");
1059 		error = EINVAL;
1060 		goto done;
1061 	}
1062 	if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
1063 		DPRINTF("mismatch ispeed and ospeed\n");
1064 		error = EINVAL;
1065 		goto done;
1066 	}
1067 	t->c_ispeed = t->c_ospeed;
1068 
1069 	if (sc->sc_callback->ucom_pre_param) {
1070 		/* Let the lower layer verify the parameters */
1071 		error = (sc->sc_callback->ucom_pre_param) (sc, t);
1072 		if (error) {
1073 			DPRINTF("callback error = %d\n", error);
1074 			goto done;
1075 		}
1076 	}
1077 
1078 	/* Disable transfers */
1079 	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
1080 
1081 	/* Queue baud rate programming command first */
1082 	ucom_queue_command(sc, ucom_cfg_param, t,
1083 	    &sc->sc_param_task[0].hdr,
1084 	    &sc->sc_param_task[1].hdr);
1085 
1086 	/* Queue transfer enable command last */
1087 	ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
1088 	    &sc->sc_start_task[0].hdr,
1089 	    &sc->sc_start_task[1].hdr);
1090 
1091 	if (t->c_cflag & CRTS_IFLOW) {
1092 		sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
1093 	} else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
1094 		sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
1095 		ucom_modem(tp, SER_RTS, 0);
1096 	}
1097 done:
1098 	if (error) {
1099 		if (opened) {
1100 			ucom_close(tp);
1101 		}
1102 	}
1103 	return (error);
1104 }
1105 
1106 static void
1107 ucom_outwakeup(struct tty *tp)
1108 {
1109 	struct ucom_softc *sc = tty_softc(tp);
1110 
1111 	mtx_assert(sc->sc_mtx, MA_OWNED);
1112 
1113 	DPRINTF("sc = %p\n", sc);
1114 
1115 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1116 		/* The higher layer is not ready */
1117 		return;
1118 	}
1119 	ucom_start_transfers(sc);
1120 }
1121 
1122 /*------------------------------------------------------------------------*
1123  *	ucom_get_data
1124  *
1125  * Return values:
1126  * 0: No data is available.
1127  * Else: Data is available.
1128  *------------------------------------------------------------------------*/
1129 uint8_t
1130 ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1131     uint32_t offset, uint32_t len, uint32_t *actlen)
1132 {
1133 	struct usb_page_search res;
1134 	struct tty *tp = sc->sc_tty;
1135 	uint32_t cnt;
1136 	uint32_t offset_orig;
1137 
1138 	mtx_assert(sc->sc_mtx, MA_OWNED);
1139 
1140 	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1141 		unsigned int temp;
1142 
1143 		/* get total TX length */
1144 
1145 		temp = ucom_cons_tx_high - ucom_cons_tx_low;
1146 		temp %= UCOM_CONS_BUFSIZE;
1147 
1148 		/* limit TX length */
1149 
1150 		if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low))
1151 			temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
1152 
1153 		if (temp > len)
1154 			temp = len;
1155 
1156 		/* copy in data */
1157 
1158 		usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
1159 
1160 		/* update counters */
1161 
1162 		ucom_cons_tx_low += temp;
1163 		ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
1164 
1165 		/* store actual length */
1166 
1167 		*actlen = temp;
1168 
1169 		return (temp ? 1 : 0);
1170 	}
1171 
1172 	if (tty_gone(tp) ||
1173 	    !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
1174 		actlen[0] = 0;
1175 		return (0);		/* multiport device polling */
1176 	}
1177 	offset_orig = offset;
1178 
1179 	while (len != 0) {
1180 
1181 		usbd_get_page(pc, offset, &res);
1182 
1183 		if (res.length > len) {
1184 			res.length = len;
1185 		}
1186 		/* copy data directly into USB buffer */
1187 		cnt = ttydisc_getc(tp, res.buffer, res.length);
1188 
1189 		offset += cnt;
1190 		len -= cnt;
1191 
1192 		if (cnt < res.length) {
1193 			/* end of buffer */
1194 			break;
1195 		}
1196 	}
1197 
1198 	actlen[0] = offset - offset_orig;
1199 
1200 	DPRINTF("cnt=%d\n", actlen[0]);
1201 
1202 	if (actlen[0] == 0) {
1203 		return (0);
1204 	}
1205 	return (1);
1206 }
1207 
1208 void
1209 ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1210     uint32_t offset, uint32_t len)
1211 {
1212 	struct usb_page_search res;
1213 	struct tty *tp = sc->sc_tty;
1214 	char *buf;
1215 	uint32_t cnt;
1216 
1217 	mtx_assert(sc->sc_mtx, MA_OWNED);
1218 
1219 	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1220 		unsigned int temp;
1221 
1222 		/* get maximum RX length */
1223 
1224 		temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
1225 		temp %= UCOM_CONS_BUFSIZE;
1226 
1227 		/* limit RX length */
1228 
1229 		if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high))
1230 			temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
1231 
1232 		if (temp > len)
1233 			temp = len;
1234 
1235 		/* copy out data */
1236 
1237 		usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
1238 
1239 		/* update counters */
1240 
1241 		ucom_cons_rx_high += temp;
1242 		ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
1243 
1244 		return;
1245 	}
1246 
1247 	if (tty_gone(tp))
1248 		return;			/* multiport device polling */
1249 
1250 	if (len == 0)
1251 		return;			/* no data */
1252 
1253 	/* set a flag to prevent recursation ? */
1254 
1255 	while (len > 0) {
1256 
1257 		usbd_get_page(pc, offset, &res);
1258 
1259 		if (res.length > len) {
1260 			res.length = len;
1261 		}
1262 		len -= res.length;
1263 		offset += res.length;
1264 
1265 		/* pass characters to tty layer */
1266 
1267 		buf = res.buffer;
1268 		cnt = res.length;
1269 
1270 		/* first check if we can pass the buffer directly */
1271 
1272 		if (ttydisc_can_bypass(tp)) {
1273 			if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1274 				DPRINTF("tp=%p, data lost\n", tp);
1275 			}
1276 			continue;
1277 		}
1278 		/* need to loop */
1279 
1280 		for (cnt = 0; cnt != res.length; cnt++) {
1281 			if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
1282 				/* XXX what should we do? */
1283 
1284 				DPRINTF("tp=%p, lost %d "
1285 				    "chars\n", tp, res.length - cnt);
1286 				break;
1287 			}
1288 		}
1289 	}
1290 	ttydisc_rint_done(tp);
1291 }
1292 
1293 static void
1294 ucom_free(void *xsc)
1295 {
1296 	struct ucom_softc *sc = xsc;
1297 
1298 	mtx_lock(sc->sc_mtx);
1299 	sc->sc_ttyfreed = 1;
1300 	cv_signal(&sc->sc_cv);
1301 	mtx_unlock(sc->sc_mtx);
1302 }
1303 
1304 static cn_probe_t ucom_cnprobe;
1305 static cn_init_t ucom_cninit;
1306 static cn_term_t ucom_cnterm;
1307 static cn_getc_t ucom_cngetc;
1308 static cn_putc_t ucom_cnputc;
1309 
1310 CONSOLE_DRIVER(ucom);
1311 
1312 static void
1313 ucom_cnprobe(struct consdev  *cp)
1314 {
1315 	if (ucom_cons_unit != -1)
1316 		cp->cn_pri = CN_NORMAL;
1317 	else
1318 		cp->cn_pri = CN_DEAD;
1319 
1320 	strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name));
1321 }
1322 
1323 static void
1324 ucom_cninit(struct consdev  *cp)
1325 {
1326 }
1327 
1328 static void
1329 ucom_cnterm(struct consdev  *cp)
1330 {
1331 }
1332 
1333 static int
1334 ucom_cngetc(struct consdev *cd)
1335 {
1336 	struct ucom_softc *sc = ucom_cons_softc;
1337 	int c;
1338 
1339 	if (sc == NULL)
1340 		return (-1);
1341 
1342 	mtx_lock(sc->sc_mtx);
1343 
1344 	if (ucom_cons_rx_low != ucom_cons_rx_high) {
1345 		c = ucom_cons_rx_buf[ucom_cons_rx_low];
1346 		ucom_cons_rx_low ++;
1347 		ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
1348 	} else {
1349 		c = -1;
1350 	}
1351 
1352 	/* start USB transfers */
1353 	ucom_outwakeup(sc->sc_tty);
1354 
1355 	mtx_unlock(sc->sc_mtx);
1356 
1357 	/* poll if necessary */
1358 	if (kdb_active && sc->sc_callback->ucom_poll)
1359 		(sc->sc_callback->ucom_poll) (sc);
1360 
1361 	return (c);
1362 }
1363 
1364 static void
1365 ucom_cnputc(struct consdev *cd, int c)
1366 {
1367 	struct ucom_softc *sc = ucom_cons_softc;
1368 	unsigned int temp;
1369 
1370 	if (sc == NULL)
1371 		return;
1372 
1373  repeat:
1374 
1375 	mtx_lock(sc->sc_mtx);
1376 
1377 	/* compute maximum TX length */
1378 
1379 	temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
1380 	temp %= UCOM_CONS_BUFSIZE;
1381 
1382 	if (temp) {
1383 		ucom_cons_tx_buf[ucom_cons_tx_high] = c;
1384 		ucom_cons_tx_high ++;
1385 		ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
1386 	}
1387 
1388 	/* start USB transfers */
1389 	ucom_outwakeup(sc->sc_tty);
1390 
1391 	mtx_unlock(sc->sc_mtx);
1392 
1393 	/* poll if necessary */
1394 	if (kdb_active && sc->sc_callback->ucom_poll) {
1395 		(sc->sc_callback->ucom_poll) (sc);
1396 		/* simple flow control */
1397 		if (temp == 0)
1398 			goto repeat;
1399 	}
1400 }
1401 
1402 #if defined(GDB)
1403 
1404 #include <gdb/gdb.h>
1405 
1406 static gdb_probe_f ucom_gdbprobe;
1407 static gdb_init_f ucom_gdbinit;
1408 static gdb_term_f ucom_gdbterm;
1409 static gdb_getc_f ucom_gdbgetc;
1410 static gdb_putc_f ucom_gdbputc;
1411 
1412 GDB_DBGPORT(sio, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc);
1413 
1414 static int
1415 ucom_gdbprobe(void)
1416 {
1417 	return ((ucom_cons_softc != NULL) ? 0 : -1);
1418 }
1419 
1420 static void
1421 ucom_gdbinit(void)
1422 {
1423 }
1424 
1425 static void
1426 ucom_gdbterm(void)
1427 {
1428 }
1429 
1430 static void
1431 ucom_gdbputc(int c)
1432 {
1433         ucom_cnputc(NULL, c);
1434 }
1435 
1436 static int
1437 ucom_gdbgetc(void)
1438 {
1439         return (ucom_cngetc(NULL));
1440 }
1441 
1442 #endif
1443