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