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