usb_serial.c revision 213872
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 213872 2010-10-14 21:45:41Z hselasky $");
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
105SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
106
107#ifdef USB_DEBUG
108static int ucom_debug = 0;
109
110SYSCTL_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
116static uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE];
117static uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE];
118
119static unsigned int ucom_cons_rx_low = 0;
120static unsigned int ucom_cons_rx_high = 0;
121
122static unsigned int ucom_cons_tx_low = 0;
123static unsigned int ucom_cons_tx_high = 0;
124
125static int ucom_cons_unit = -1;
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");
132
133TUNABLE_INT("hw.usb.ucom.cons_baud", &ucom_cons_baud);
134SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RW,
135    &ucom_cons_baud, 0, "console baud rate");
136
137static usb_proc_callback_t ucom_cfg_start_transfers;
138static usb_proc_callback_t ucom_cfg_open;
139static usb_proc_callback_t ucom_cfg_close;
140static usb_proc_callback_t ucom_cfg_line_state;
141static usb_proc_callback_t ucom_cfg_status_change;
142static usb_proc_callback_t ucom_cfg_param;
143
144static uint8_t	ucom_units_alloc(uint32_t, uint32_t *);
145static void	ucom_units_free(uint32_t, uint32_t);
146static int	ucom_attach_tty(struct ucom_softc *, uint32_t);
147static void	ucom_detach_tty(struct ucom_softc *);
148static 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);
151static void	ucom_shutdown(struct ucom_softc *);
152static void	ucom_ring(struct ucom_softc *, uint8_t);
153static void	ucom_break(struct ucom_softc *, uint8_t);
154static void	ucom_dtr(struct ucom_softc *, uint8_t);
155static void	ucom_rts(struct ucom_softc *, uint8_t);
156
157static tsw_open_t ucom_open;
158static tsw_close_t ucom_close;
159static tsw_ioctl_t ucom_ioctl;
160static tsw_modem_t ucom_modem;
161static tsw_param_t ucom_param;
162static tsw_outwakeup_t ucom_outwakeup;
163static tsw_free_t ucom_free;
164
165static 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
176MODULE_DEPEND(ucom, usb, 1, 1, 1);
177MODULE_VERSION(ucom, 1);
178
179#define	UCOM_UNIT_MAX 0x200		/* exclusive */
180#define	UCOM_SUB_UNIT_MAX 0x100		/* exclusive */
181
182static uint8_t ucom_bitmap[(UCOM_UNIT_MAX + 7) / 8];
183static struct mtx ucom_bitmap_mtx;
184MTX_SYSINIT(ucom_bitmap_mtx, &ucom_bitmap_mtx, "ucom bitmap", MTX_DEF);
185
186static uint8_t
187ucom_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
223skip:		;
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
237static void
238ucom_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 */
262int
263ucom_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 */
312void
313ucom_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
334static int
335ucom_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	}
401done:
402	return (error);
403}
404
405static void
406ucom_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
450static void
451ucom_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
497static void
498ucom_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 */
519uint8_t
520ucom_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
527static void
528ucom_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
553static void
554ucom_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
571static void
572ucom_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
597static int
598ucom_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
658static void
659ucom_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
676static void
677ucom_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
702static int
703ucom_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
746static int
747ucom_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
800static void
801ucom_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
867static void
868ucom_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
891static void
892ucom_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
902static void
903ucom_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
913static void
914ucom_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
924static void
925ucom_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
935static void
936ucom_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
1008void
1009ucom_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
1026static void
1027ucom_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
1046static int
1047ucom_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	}
1111done:
1112	if (error) {
1113		if (opened) {
1114			ucom_close(tp);
1115		}
1116	}
1117	return (error);
1118}
1119
1120static void
1121ucom_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 *------------------------------------------------------------------------*/
1143uint8_t
1144ucom_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
1222void
1223ucom_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
1307static void
1308ucom_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
1318static cn_probe_t ucom_cnprobe;
1319static cn_init_t ucom_cninit;
1320static cn_term_t ucom_cnterm;
1321static cn_getc_t ucom_cngetc;
1322static cn_putc_t ucom_cnputc;
1323
1324CONSOLE_DRIVER(ucom);
1325
1326static void
1327ucom_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
1337static void
1338ucom_cninit(struct consdev  *cp)
1339{
1340}
1341
1342static void
1343ucom_cnterm(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