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