usb_serial.c revision 213872
1184610Salfred/*	$NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $	*/
2184610Salfred
3184610Salfred/*-
4184610Salfred * Copyright (c) 2001-2003, 2005, 2008
5184610Salfred *	Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
6184610Salfred * All rights reserved.
7184610Salfred *
8184610Salfred * Redistribution and use in source and binary forms, with or without
9184610Salfred * modification, are permitted provided that the following conditions
10184610Salfred * are met:
11184610Salfred * 1. Redistributions of source code must retain the above copyright
12184610Salfred *    notice, this list of conditions and the following disclaimer.
13184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
14184610Salfred *    notice, this list of conditions and the following disclaimer in the
15184610Salfred *    documentation and/or other materials provided with the distribution.
16184610Salfred *
17184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27184610Salfred * SUCH DAMAGE.
28184610Salfred */
29184610Salfred
30184610Salfred#include <sys/cdefs.h>
31184610Salfred__FBSDID("$FreeBSD: head/sys/dev/usb/serial/usb_serial.c 213872 2010-10-14 21:45:41Z hselasky $");
32184610Salfred
33184610Salfred/*-
34184610Salfred * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
35184610Salfred * All rights reserved.
36184610Salfred *
37184610Salfred * This code is derived from software contributed to The NetBSD Foundation
38184610Salfred * by Lennart Augustsson (lennart@augustsson.net) at
39184610Salfred * Carlstedt Research & Technology.
40184610Salfred *
41184610Salfred * Redistribution and use in source and binary forms, with or without
42184610Salfred * modification, are permitted provided that the following conditions
43184610Salfred * are met:
44184610Salfred * 1. Redistributions of source code must retain the above copyright
45184610Salfred *    notice, this list of conditions and the following disclaimer.
46184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
47184610Salfred *    notice, this list of conditions and the following disclaimer in the
48184610Salfred *    documentation and/or other materials provided with the distribution.
49184610Salfred * 3. All advertising materials mentioning features or use of this software
50184610Salfred *    must display the following acknowledgement:
51184610Salfred *        This product includes software developed by the NetBSD
52184610Salfred *        Foundation, Inc. and its contributors.
53184610Salfred * 4. Neither the name of The NetBSD Foundation nor the names of its
54184610Salfred *    contributors may be used to endorse or promote products derived
55184610Salfred *    from this software without specific prior written permission.
56184610Salfred *
57184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
58184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
59184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
60184610Salfred * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
61184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
62184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
63184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
64184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
66184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
67184610Salfred * POSSIBILITY OF SUCH DAMAGE.
68184610Salfred */
69184610Salfred
70194677Sthompsa#include <sys/stdint.h>
71194677Sthompsa#include <sys/stddef.h>
72194677Sthompsa#include <sys/param.h>
73194677Sthompsa#include <sys/queue.h>
74194677Sthompsa#include <sys/types.h>
75194677Sthompsa#include <sys/systm.h>
76194677Sthompsa#include <sys/kernel.h>
77194677Sthompsa#include <sys/bus.h>
78194677Sthompsa#include <sys/linker_set.h>
79194677Sthompsa#include <sys/module.h>
80194677Sthompsa#include <sys/lock.h>
81194677Sthompsa#include <sys/mutex.h>
82194677Sthompsa#include <sys/condvar.h>
83194677Sthompsa#include <sys/sysctl.h>
84194677Sthompsa#include <sys/sx.h>
85194677Sthompsa#include <sys/unistd.h>
86194677Sthompsa#include <sys/callout.h>
87194677Sthompsa#include <sys/malloc.h>
88194677Sthompsa#include <sys/priv.h>
89197570Sthompsa#include <sys/cons.h>
90197570Sthompsa#include <sys/kdb.h>
91194677Sthompsa
92188942Sthompsa#include <dev/usb/usb.h>
93194677Sthompsa#include <dev/usb/usbdi.h>
94194677Sthompsa#include <dev/usb/usbdi_util.h>
95184610Salfred
96194228Sthompsa#define	USB_DEBUG_VAR ucom_debug
97188942Sthompsa#include <dev/usb/usb_debug.h>
98194677Sthompsa#include <dev/usb/usb_busdma.h>
99188942Sthompsa#include <dev/usb/usb_process.h>
100184610Salfred
101188942Sthompsa#include <dev/usb/serial/usb_serial.h>
102184610Salfred
103197570Sthompsa#include "opt_gdb.h"
104197570Sthompsa
105197570SthompsaSYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
106197570Sthompsa
107207077Sthompsa#ifdef USB_DEBUG
108194228Sthompsastatic int ucom_debug = 0;
109184610Salfred
110192502SthompsaSYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
111194228Sthompsa    &ucom_debug, 0, "ucom debug level");
112184610Salfred#endif
113184610Salfred
114197570Sthompsa#define	UCOM_CONS_BUFSIZE 1024
115197570Sthompsa
116197570Sthompsastatic uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE];
117197570Sthompsastatic uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE];
118197570Sthompsa
119197570Sthompsastatic unsigned int ucom_cons_rx_low = 0;
120197570Sthompsastatic unsigned int ucom_cons_rx_high = 0;
121197570Sthompsa
122197570Sthompsastatic unsigned int ucom_cons_tx_low = 0;
123197570Sthompsastatic unsigned int ucom_cons_tx_high = 0;
124197570Sthompsa
125197570Sthompsastatic int ucom_cons_unit = -1;
126197570Sthompsastatic int ucom_cons_baud = 9600;
127197570Sthompsastatic struct ucom_softc *ucom_cons_softc = NULL;
128197570Sthompsa
129197570SthompsaTUNABLE_INT("hw.usb.ucom.cons_unit", &ucom_cons_unit);
130197570SthompsaSYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RW,
131197570Sthompsa    &ucom_cons_unit, 0, "console unit number");
132197570Sthompsa
133197570SthompsaTUNABLE_INT("hw.usb.ucom.cons_baud", &ucom_cons_baud);
134197570SthompsaSYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RW,
135197570Sthompsa    &ucom_cons_baud, 0, "console baud rate");
136197570Sthompsa
137194228Sthompsastatic usb_proc_callback_t ucom_cfg_start_transfers;
138194228Sthompsastatic usb_proc_callback_t ucom_cfg_open;
139194228Sthompsastatic usb_proc_callback_t ucom_cfg_close;
140194228Sthompsastatic usb_proc_callback_t ucom_cfg_line_state;
141194228Sthompsastatic usb_proc_callback_t ucom_cfg_status_change;
142194228Sthompsastatic usb_proc_callback_t ucom_cfg_param;
143184610Salfred
144194228Sthompsastatic uint8_t	ucom_units_alloc(uint32_t, uint32_t *);
145194228Sthompsastatic void	ucom_units_free(uint32_t, uint32_t);
146194228Sthompsastatic int	ucom_attach_tty(struct ucom_softc *, uint32_t);
147194228Sthompsastatic void	ucom_detach_tty(struct ucom_softc *);
148194228Sthompsastatic void	ucom_queue_command(struct ucom_softc *,
149193045Sthompsa		    usb_proc_callback_t *, struct termios *pt,
150192984Sthompsa		    struct usb_proc_msg *t0, struct usb_proc_msg *t1);
151194228Sthompsastatic void	ucom_shutdown(struct ucom_softc *);
152197570Sthompsastatic void	ucom_ring(struct ucom_softc *, uint8_t);
153194228Sthompsastatic void	ucom_break(struct ucom_softc *, uint8_t);
154194228Sthompsastatic void	ucom_dtr(struct ucom_softc *, uint8_t);
155194228Sthompsastatic void	ucom_rts(struct ucom_softc *, uint8_t);
156184610Salfred
157194228Sthompsastatic tsw_open_t ucom_open;
158194228Sthompsastatic tsw_close_t ucom_close;
159194228Sthompsastatic tsw_ioctl_t ucom_ioctl;
160194228Sthompsastatic tsw_modem_t ucom_modem;
161194228Sthompsastatic tsw_param_t ucom_param;
162194228Sthompsastatic tsw_outwakeup_t ucom_outwakeup;
163194228Sthompsastatic tsw_free_t ucom_free;
164184610Salfred
165194228Sthompsastatic struct ttydevsw ucom_class = {
166184610Salfred	.tsw_flags = TF_INITLOCK | TF_CALLOUT,
167194228Sthompsa	.tsw_open = ucom_open,
168194228Sthompsa	.tsw_close = ucom_close,
169194228Sthompsa	.tsw_outwakeup = ucom_outwakeup,
170194228Sthompsa	.tsw_ioctl = ucom_ioctl,
171194228Sthompsa	.tsw_param = ucom_param,
172194228Sthompsa	.tsw_modem = ucom_modem,
173194228Sthompsa	.tsw_free = ucom_free,
174184610Salfred};
175184610Salfred
176188942SthompsaMODULE_DEPEND(ucom, usb, 1, 1, 1);
177188942SthompsaMODULE_VERSION(ucom, 1);
178184610Salfred
179197570Sthompsa#define	UCOM_UNIT_MAX 0x200		/* exclusive */
180184610Salfred#define	UCOM_SUB_UNIT_MAX 0x100		/* exclusive */
181184610Salfred
182194228Sthompsastatic uint8_t ucom_bitmap[(UCOM_UNIT_MAX + 7) / 8];
183195146Sedstatic struct mtx ucom_bitmap_mtx;
184195146SedMTX_SYSINIT(ucom_bitmap_mtx, &ucom_bitmap_mtx, "ucom bitmap", MTX_DEF);
185184610Salfred
186184610Salfredstatic uint8_t
187194228Sthompsaucom_units_alloc(uint32_t sub_units, uint32_t *p_root_unit)
188184610Salfred{
189184610Salfred	uint32_t n;
190184610Salfred	uint32_t o;
191184610Salfred	uint32_t x;
192184610Salfred	uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units);
193184610Salfred	uint8_t error = 1;
194184610Salfred
195195146Sed	mtx_lock(&ucom_bitmap_mtx);
196184610Salfred
197184610Salfred	for (n = 0; n < max; n += sub_units) {
198184610Salfred
199184610Salfred		/* check for free consecutive bits */
200184610Salfred
201184610Salfred		for (o = 0; o < sub_units; o++) {
202184610Salfred
203184610Salfred			x = n + o;
204184610Salfred
205194228Sthompsa			if (ucom_bitmap[x / 8] & (1 << (x % 8))) {
206184610Salfred				goto skip;
207184610Salfred			}
208184610Salfred		}
209184610Salfred
210184610Salfred		/* allocate */
211184610Salfred
212184610Salfred		for (o = 0; o < sub_units; o++) {
213184610Salfred
214184610Salfred			x = n + o;
215184610Salfred
216194228Sthompsa			ucom_bitmap[x / 8] |= (1 << (x % 8));
217184610Salfred		}
218184610Salfred
219184610Salfred		error = 0;
220184610Salfred
221184610Salfred		break;
222184610Salfred
223184610Salfredskip:		;
224184610Salfred	}
225184610Salfred
226195146Sed	mtx_unlock(&ucom_bitmap_mtx);
227184610Salfred
228184610Salfred	/*
229184610Salfred	 * Always set the variable pointed to by "p_root_unit" so that
230184610Salfred	 * the compiler does not think that it is used uninitialised:
231184610Salfred	 */
232184610Salfred	*p_root_unit = n;
233184610Salfred
234184610Salfred	return (error);
235184610Salfred}
236184610Salfred
237184610Salfredstatic void
238194228Sthompsaucom_units_free(uint32_t root_unit, uint32_t sub_units)
239184610Salfred{
240184610Salfred	uint32_t x;
241184610Salfred
242195146Sed	mtx_lock(&ucom_bitmap_mtx);
243184610Salfred
244184610Salfred	while (sub_units--) {
245184610Salfred		x = root_unit + sub_units;
246194228Sthompsa		ucom_bitmap[x / 8] &= ~(1 << (x % 8));
247184610Salfred	}
248184610Salfred
249195146Sed	mtx_unlock(&ucom_bitmap_mtx);
250184610Salfred}
251184610Salfred
252184610Salfred/*
253184610Salfred * "N" sub_units are setup at a time. All sub-units will
254184610Salfred * be given sequential unit numbers. The number of
255184610Salfred * sub-units can be used to differentiate among
256184610Salfred * different types of devices.
257184610Salfred *
258188413Sthompsa * The mutex pointed to by "mtx" is applied before all
259188413Sthompsa * callbacks are called back. Also "mtx" must be applied
260188413Sthompsa * before calling into the ucom-layer!
261184610Salfred */
262184610Salfredint
263194228Sthompsaucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
264184610Salfred    uint32_t sub_units, void *parent,
265192984Sthompsa    const struct ucom_callback *callback, struct mtx *mtx)
266184610Salfred{
267184610Salfred	uint32_t n;
268184610Salfred	uint32_t root_unit;
269184610Salfred	int error = 0;
270184610Salfred
271184610Salfred	if ((sc == NULL) ||
272184610Salfred	    (sub_units == 0) ||
273184610Salfred	    (sub_units > UCOM_SUB_UNIT_MAX) ||
274184610Salfred	    (callback == NULL)) {
275184610Salfred		return (EINVAL);
276184610Salfred	}
277188413Sthompsa
278188413Sthompsa	/* XXX unit management does not really belong here */
279194228Sthompsa	if (ucom_units_alloc(sub_units, &root_unit)) {
280184610Salfred		return (ENOMEM);
281184610Salfred	}
282188413Sthompsa
283194228Sthompsa	error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
284188413Sthompsa	if (error) {
285194228Sthompsa		ucom_units_free(root_unit, sub_units);
286188413Sthompsa		return (error);
287184610Salfred	}
288188413Sthompsa
289188413Sthompsa	for (n = 0; n != sub_units; n++, sc++) {
290184610Salfred		sc->sc_unit = root_unit + n;
291184610Salfred		sc->sc_local_unit = n;
292184610Salfred		sc->sc_super = ssc;
293188413Sthompsa		sc->sc_mtx = mtx;
294184610Salfred		sc->sc_parent = parent;
295184610Salfred		sc->sc_callback = callback;
296184610Salfred
297194228Sthompsa		error = ucom_attach_tty(sc, sub_units);
298184610Salfred		if (error) {
299194228Sthompsa			ucom_detach(ssc, sc - n, n);
300194228Sthompsa			ucom_units_free(root_unit + n, sub_units - n);
301188413Sthompsa			return (error);
302184610Salfred		}
303184610Salfred		sc->sc_flag |= UCOM_FLAG_ATTACHED;
304184610Salfred	}
305188413Sthompsa	return (0);
306184610Salfred}
307184610Salfred
308188413Sthompsa/*
309188413Sthompsa * NOTE: the following function will do nothing if
310184610Salfred * the structure pointed to by "ssc" and "sc" is zero.
311184610Salfred */
312184610Salfredvoid
313194228Sthompsaucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
314184610Salfred    uint32_t sub_units)
315184610Salfred{
316184610Salfred	uint32_t n;
317184610Salfred
318194228Sthompsa	usb_proc_drain(&ssc->sc_tq);
319184610Salfred
320188413Sthompsa	for (n = 0; n != sub_units; n++, sc++) {
321184610Salfred		if (sc->sc_flag & UCOM_FLAG_ATTACHED) {
322184610Salfred
323194228Sthompsa			ucom_detach_tty(sc);
324184610Salfred
325194228Sthompsa			ucom_units_free(sc->sc_unit, 1);
326184610Salfred
327184610Salfred			/* avoid duplicate detach: */
328184610Salfred			sc->sc_flag &= ~UCOM_FLAG_ATTACHED;
329184610Salfred		}
330184610Salfred	}
331194228Sthompsa	usb_proc_free(&ssc->sc_tq);
332184610Salfred}
333184610Salfred
334184610Salfredstatic int
335194228Sthompsaucom_attach_tty(struct ucom_softc *sc, uint32_t sub_units)
336184610Salfred{
337184610Salfred	struct tty *tp;
338184610Salfred	int error = 0;
339184610Salfred	char buf[32];			/* temporary TTY device name buffer */
340184610Salfred
341194228Sthompsa	tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx);
342184610Salfred	if (tp == NULL) {
343184610Salfred		error = ENOMEM;
344184610Salfred		goto done;
345184610Salfred	}
346184610Salfred	DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit);
347184610Salfred
348184610Salfred	buf[0] = 0;			/* set some default value */
349184610Salfred
350184610Salfred	/* Check if the client has a custom TTY name */
351194228Sthompsa	if (sc->sc_callback->ucom_tty_name) {
352194228Sthompsa		sc->sc_callback->ucom_tty_name(sc, buf,
353184610Salfred		    sizeof(buf), sc->sc_local_unit);
354184610Salfred	}
355184610Salfred	if (buf[0] == 0) {
356184610Salfred		/* Use default TTY name */
357188413Sthompsa		if (sub_units > 1) {
358188413Sthompsa			/* multiple modems in one */
359188413Sthompsa			if (snprintf(buf, sizeof(buf), "U%u.%u",
360188413Sthompsa			    sc->sc_unit - sc->sc_local_unit,
361188413Sthompsa			    sc->sc_local_unit)) {
362188413Sthompsa				/* ignore */
363188413Sthompsa			}
364188413Sthompsa		} else {
365188413Sthompsa			/* single modem */
366188413Sthompsa			if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) {
367188413Sthompsa				/* ignore */
368188413Sthompsa			}
369184610Salfred		}
370184610Salfred	}
371184610Salfred	tty_makedev(tp, NULL, "%s", buf);
372184610Salfred
373184610Salfred	sc->sc_tty = tp;
374184610Salfred
375184610Salfred	DPRINTF("ttycreate: %s\n", buf);
376194228Sthompsa	cv_init(&sc->sc_cv, "ucom");
377184610Salfred
378197570Sthompsa	/* Check if this device should be a console */
379197570Sthompsa	if ((ucom_cons_softc == NULL) &&
380197570Sthompsa	    (sc->sc_unit == ucom_cons_unit)) {
381197570Sthompsa
382197570Sthompsa		struct termios t;
383197570Sthompsa
384197570Sthompsa		ucom_cons_softc = sc;
385197570Sthompsa
386197570Sthompsa		memset(&t, 0, sizeof(t));
387197570Sthompsa		t.c_ispeed = ucom_cons_baud;
388197570Sthompsa		t.c_ospeed = t.c_ispeed;
389197570Sthompsa		t.c_cflag = CS8;
390197570Sthompsa
391197570Sthompsa		mtx_lock(ucom_cons_softc->sc_mtx);
392197570Sthompsa		ucom_cons_rx_low = 0;
393197570Sthompsa		ucom_cons_rx_high = 0;
394197570Sthompsa		ucom_cons_tx_low = 0;
395197570Sthompsa		ucom_cons_tx_high = 0;
396197570Sthompsa		sc->sc_flag |= UCOM_FLAG_CONSOLE;
397197570Sthompsa		ucom_open(ucom_cons_softc->sc_tty);
398197570Sthompsa		ucom_param(ucom_cons_softc->sc_tty, &t);
399197570Sthompsa		mtx_unlock(ucom_cons_softc->sc_mtx);
400197570Sthompsa	}
401184610Salfreddone:
402184610Salfred	return (error);
403184610Salfred}
404184610Salfred
405184610Salfredstatic void
406194228Sthompsaucom_detach_tty(struct ucom_softc *sc)
407184610Salfred{
408184610Salfred	struct tty *tp = sc->sc_tty;
409184610Salfred
410184610Salfred	DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
411184610Salfred
412197570Sthompsa	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
413197570Sthompsa		mtx_lock(ucom_cons_softc->sc_mtx);
414197570Sthompsa		ucom_close(ucom_cons_softc->sc_tty);
415197570Sthompsa		mtx_unlock(ucom_cons_softc->sc_mtx);
416197570Sthompsa		ucom_cons_softc = NULL;
417197570Sthompsa	}
418197570Sthompsa
419184610Salfred	/* the config thread has been stopped when we get here */
420184610Salfred
421188413Sthompsa	mtx_lock(sc->sc_mtx);
422184610Salfred	sc->sc_flag |= UCOM_FLAG_GONE;
423197570Sthompsa	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY);
424188413Sthompsa	mtx_unlock(sc->sc_mtx);
425184610Salfred	if (tp) {
426184610Salfred		tty_lock(tp);
427184610Salfred
428194228Sthompsa		ucom_close(tp);	/* close, if any */
429184610Salfred
430184610Salfred		tty_rel_gone(tp);
431184610Salfred
432188413Sthompsa		mtx_lock(sc->sc_mtx);
433184610Salfred		/* Wait for the callback after the TTY is torn down */
434184610Salfred		while (sc->sc_ttyfreed == 0)
435194227Sthompsa			cv_wait(&sc->sc_cv, sc->sc_mtx);
436184610Salfred		/*
437184610Salfred		 * make sure that read and write transfers are stopped
438184610Salfred		 */
439194228Sthompsa		if (sc->sc_callback->ucom_stop_read) {
440194228Sthompsa			(sc->sc_callback->ucom_stop_read) (sc);
441184610Salfred		}
442194228Sthompsa		if (sc->sc_callback->ucom_stop_write) {
443194228Sthompsa			(sc->sc_callback->ucom_stop_write) (sc);
444184610Salfred		}
445188413Sthompsa		mtx_unlock(sc->sc_mtx);
446184610Salfred	}
447194227Sthompsa	cv_destroy(&sc->sc_cv);
448184610Salfred}
449184610Salfred
450184610Salfredstatic void
451194228Sthompsaucom_queue_command(struct ucom_softc *sc,
452193045Sthompsa    usb_proc_callback_t *fn, struct termios *pt,
453192984Sthompsa    struct usb_proc_msg *t0, struct usb_proc_msg *t1)
454184610Salfred{
455192984Sthompsa	struct ucom_super_softc *ssc = sc->sc_super;
456192984Sthompsa	struct ucom_param_task *task;
457187176Sthompsa
458188413Sthompsa	mtx_assert(sc->sc_mtx, MA_OWNED);
459188413Sthompsa
460194228Sthompsa	if (usb_proc_is_gone(&ssc->sc_tq)) {
461187176Sthompsa		DPRINTF("proc is gone\n");
462188413Sthompsa		return;         /* nothing to do */
463187176Sthompsa	}
464188413Sthompsa	/*
465188413Sthompsa	 * NOTE: The task cannot get executed before we drop the
466188413Sthompsa	 * "sc_mtx" mutex. It is safe to update fields in the message
467188413Sthompsa	 * structure after that the message got queued.
468188413Sthompsa	 */
469192984Sthompsa	task = (struct ucom_param_task *)
470194228Sthompsa	  usb_proc_msignal(&ssc->sc_tq, t0, t1);
471187176Sthompsa
472188413Sthompsa	/* Setup callback and softc pointers */
473188413Sthompsa	task->hdr.pm_callback = fn;
474188413Sthompsa	task->sc = sc;
475184610Salfred
476188413Sthompsa	/*
477188413Sthompsa	 * Make a copy of the termios. This field is only present if
478188413Sthompsa	 * the "pt" field is not NULL.
479188413Sthompsa	 */
480188413Sthompsa	if (pt != NULL)
481188413Sthompsa		task->termios_copy = *pt;
482184610Salfred
483188413Sthompsa	/*
484188413Sthompsa	 * Closing the device should be synchronous.
485188413Sthompsa	 */
486194228Sthompsa	if (fn == ucom_cfg_close)
487194228Sthompsa		usb_proc_mwait(&ssc->sc_tq, t0, t1);
488188413Sthompsa
489190742Sthompsa	/*
490190742Sthompsa	 * In case of multiple configure requests,
491190742Sthompsa	 * keep track of the last one!
492190742Sthompsa	 */
493194228Sthompsa	if (fn == ucom_cfg_start_transfers)
494190742Sthompsa		sc->sc_last_start_xfer = &task->hdr;
495184610Salfred}
496184610Salfred
497184610Salfredstatic void
498194228Sthompsaucom_shutdown(struct ucom_softc *sc)
499184610Salfred{
500184610Salfred	struct tty *tp = sc->sc_tty;
501184610Salfred
502188413Sthompsa	mtx_assert(sc->sc_mtx, MA_OWNED);
503184610Salfred
504184610Salfred	DPRINTF("\n");
505184610Salfred
506184610Salfred	/*
507184610Salfred	 * Hang up if necessary:
508184610Salfred	 */
509184610Salfred	if (tp->t_termios.c_cflag & HUPCL) {
510194228Sthompsa		ucom_modem(tp, 0, SER_DTR);
511184610Salfred	}
512184610Salfred}
513184610Salfred
514184610Salfred/*
515184610Salfred * Return values:
516184610Salfred *    0: normal
517188413Sthompsa * else: taskqueue is draining or gone
518184610Salfred */
519184610Salfreduint8_t
520194228Sthompsaucom_cfg_is_gone(struct ucom_softc *sc)
521184610Salfred{
522192984Sthompsa	struct ucom_super_softc *ssc = sc->sc_super;
523184610Salfred
524194228Sthompsa	return (usb_proc_is_gone(&ssc->sc_tq));
525184610Salfred}
526184610Salfred
527184610Salfredstatic void
528194228Sthompsaucom_cfg_start_transfers(struct usb_proc_msg *_task)
529184610Salfred{
530192984Sthompsa	struct ucom_cfg_task *task =
531192984Sthompsa	    (struct ucom_cfg_task *)_task;
532192984Sthompsa	struct ucom_softc *sc = task->sc;
533187176Sthompsa
534184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
535184610Salfred		return;
536184610Salfred	}
537184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
538184610Salfred		/* TTY device closed */
539184610Salfred		return;
540184610Salfred	}
541184610Salfred
542190742Sthompsa	if (_task == sc->sc_last_start_xfer)
543190742Sthompsa		sc->sc_flag |= UCOM_FLAG_GP_DATA;
544190742Sthompsa
545194228Sthompsa	if (sc->sc_callback->ucom_start_read) {
546194228Sthompsa		(sc->sc_callback->ucom_start_read) (sc);
547184610Salfred	}
548194228Sthompsa	if (sc->sc_callback->ucom_start_write) {
549194228Sthompsa		(sc->sc_callback->ucom_start_write) (sc);
550184610Salfred	}
551184610Salfred}
552184610Salfred
553184610Salfredstatic void
554194228Sthompsaucom_start_transfers(struct ucom_softc *sc)
555184610Salfred{
556184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
557184610Salfred		return;
558184610Salfred	}
559184610Salfred	/*
560188413Sthompsa	 * Make sure that data transfers are started in both
561188413Sthompsa	 * directions:
562184610Salfred	 */
563194228Sthompsa	if (sc->sc_callback->ucom_start_read) {
564194228Sthompsa		(sc->sc_callback->ucom_start_read) (sc);
565184610Salfred	}
566194228Sthompsa	if (sc->sc_callback->ucom_start_write) {
567194228Sthompsa		(sc->sc_callback->ucom_start_write) (sc);
568184610Salfred	}
569184610Salfred}
570184610Salfred
571184610Salfredstatic void
572194228Sthompsaucom_cfg_open(struct usb_proc_msg *_task)
573184610Salfred{
574192984Sthompsa	struct ucom_cfg_task *task =
575192984Sthompsa	    (struct ucom_cfg_task *)_task;
576192984Sthompsa	struct ucom_softc *sc = task->sc;
577187176Sthompsa
578184610Salfred	DPRINTF("\n");
579184610Salfred
580184610Salfred	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
581184610Salfred
582184610Salfred		/* already opened */
583184610Salfred
584184610Salfred	} else {
585184610Salfred
586184610Salfred		sc->sc_flag |= UCOM_FLAG_LL_READY;
587184610Salfred
588194228Sthompsa		if (sc->sc_callback->ucom_cfg_open) {
589194228Sthompsa			(sc->sc_callback->ucom_cfg_open) (sc);
590184610Salfred
591184610Salfred			/* wait a little */
592194228Sthompsa			usb_pause_mtx(sc->sc_mtx, hz / 10);
593184610Salfred		}
594184610Salfred	}
595184610Salfred}
596184610Salfred
597184610Salfredstatic int
598194228Sthompsaucom_open(struct tty *tp)
599184610Salfred{
600192984Sthompsa	struct ucom_softc *sc = tty_softc(tp);
601184610Salfred	int error;
602184610Salfred
603188413Sthompsa	mtx_assert(sc->sc_mtx, MA_OWNED);
604184610Salfred
605184610Salfred	if (sc->sc_flag & UCOM_FLAG_GONE) {
606184610Salfred		return (ENXIO);
607184610Salfred	}
608184610Salfred	if (sc->sc_flag & UCOM_FLAG_HL_READY) {
609184610Salfred		/* already opened */
610184610Salfred		return (0);
611184610Salfred	}
612184610Salfred	DPRINTF("tp = %p\n", tp);
613184610Salfred
614194228Sthompsa	if (sc->sc_callback->ucom_pre_open) {
615184610Salfred		/*
616184610Salfred		 * give the lower layer a chance to disallow TTY open, for
617184610Salfred		 * example if the device is not present:
618184610Salfred		 */
619194228Sthompsa		error = (sc->sc_callback->ucom_pre_open) (sc);
620184610Salfred		if (error) {
621184610Salfred			return (error);
622184610Salfred		}
623184610Salfred	}
624184610Salfred	sc->sc_flag |= UCOM_FLAG_HL_READY;
625184610Salfred
626184610Salfred	/* Disable transfers */
627184610Salfred	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
628184610Salfred
629184610Salfred	sc->sc_lsr = 0;
630184610Salfred	sc->sc_msr = 0;
631184610Salfred	sc->sc_mcr = 0;
632184610Salfred
633188413Sthompsa	/* reset programmed line state */
634188413Sthompsa	sc->sc_pls_curr = 0;
635188413Sthompsa	sc->sc_pls_set = 0;
636188413Sthompsa	sc->sc_pls_clr = 0;
637184610Salfred
638194228Sthompsa	ucom_queue_command(sc, ucom_cfg_open, NULL,
639188413Sthompsa	    &sc->sc_open_task[0].hdr,
640188413Sthompsa	    &sc->sc_open_task[1].hdr);
641184610Salfred
642188413Sthompsa	/* Queue transfer enable command last */
643194228Sthompsa	ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
644188413Sthompsa	    &sc->sc_start_task[0].hdr,
645188413Sthompsa	    &sc->sc_start_task[1].hdr);
646188413Sthompsa
647194228Sthompsa	ucom_modem(tp, SER_DTR | SER_RTS, 0);
648184610Salfred
649197570Sthompsa	ucom_ring(sc, 0);
650197570Sthompsa
651194228Sthompsa	ucom_break(sc, 0);
652184610Salfred
653194228Sthompsa	ucom_status_change(sc);
654184610Salfred
655184610Salfred	return (0);
656184610Salfred}
657184610Salfred
658184610Salfredstatic void
659194228Sthompsaucom_cfg_close(struct usb_proc_msg *_task)
660184610Salfred{
661192984Sthompsa	struct ucom_cfg_task *task =
662192984Sthompsa	    (struct ucom_cfg_task *)_task;
663192984Sthompsa	struct ucom_softc *sc = task->sc;
664187176Sthompsa
665184610Salfred	DPRINTF("\n");
666184610Salfred
667184610Salfred	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
668192820Sthompsa		sc->sc_flag &= ~UCOM_FLAG_LL_READY;
669194228Sthompsa		if (sc->sc_callback->ucom_cfg_close)
670194228Sthompsa			(sc->sc_callback->ucom_cfg_close) (sc);
671184610Salfred	} else {
672184610Salfred		/* already closed */
673184610Salfred	}
674184610Salfred}
675184610Salfred
676184610Salfredstatic void
677194228Sthompsaucom_close(struct tty *tp)
678184610Salfred{
679192984Sthompsa	struct ucom_softc *sc = tty_softc(tp);
680184610Salfred
681188413Sthompsa	mtx_assert(sc->sc_mtx, MA_OWNED);
682188413Sthompsa
683184610Salfred	DPRINTF("tp=%p\n", tp);
684184610Salfred
685184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
686184610Salfred		DPRINTF("tp=%p already closed\n", tp);
687184610Salfred		return;
688184610Salfred	}
689194228Sthompsa	ucom_shutdown(sc);
690184610Salfred
691194228Sthompsa	ucom_queue_command(sc, ucom_cfg_close, NULL,
692188413Sthompsa	    &sc->sc_close_task[0].hdr,
693188413Sthompsa	    &sc->sc_close_task[1].hdr);
694184610Salfred
695192820Sthompsa	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
696184610Salfred
697194228Sthompsa	if (sc->sc_callback->ucom_stop_read) {
698194228Sthompsa		(sc->sc_callback->ucom_stop_read) (sc);
699184610Salfred	}
700184610Salfred}
701184610Salfred
702184610Salfredstatic int
703194228Sthompsaucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
704184610Salfred{
705192984Sthompsa	struct ucom_softc *sc = tty_softc(tp);
706184610Salfred	int error;
707184610Salfred
708188413Sthompsa	mtx_assert(sc->sc_mtx, MA_OWNED);
709184610Salfred
710184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
711184610Salfred		return (EIO);
712184610Salfred	}
713184610Salfred	DPRINTF("cmd = 0x%08lx\n", cmd);
714184610Salfred
715184610Salfred	switch (cmd) {
716197570Sthompsa#if 0
717197570Sthompsa	case TIOCSRING:
718197570Sthompsa		ucom_ring(sc, 1);
719197570Sthompsa		error = 0;
720197570Sthompsa		break;
721197570Sthompsa	case TIOCCRING:
722197570Sthompsa		ucom_ring(sc, 0);
723197570Sthompsa		error = 0;
724197570Sthompsa		break;
725197570Sthompsa#endif
726184610Salfred	case TIOCSBRK:
727194228Sthompsa		ucom_break(sc, 1);
728184610Salfred		error = 0;
729184610Salfred		break;
730184610Salfred	case TIOCCBRK:
731194228Sthompsa		ucom_break(sc, 0);
732184610Salfred		error = 0;
733184610Salfred		break;
734184610Salfred	default:
735194228Sthompsa		if (sc->sc_callback->ucom_ioctl) {
736194228Sthompsa			error = (sc->sc_callback->ucom_ioctl)
737184610Salfred			    (sc, cmd, data, 0, td);
738184610Salfred		} else {
739184610Salfred			error = ENOIOCTL;
740184610Salfred		}
741184610Salfred		break;
742184610Salfred	}
743184610Salfred	return (error);
744184610Salfred}
745184610Salfred
746184610Salfredstatic int
747194228Sthompsaucom_modem(struct tty *tp, int sigon, int sigoff)
748184610Salfred{
749192984Sthompsa	struct ucom_softc *sc = tty_softc(tp);
750184610Salfred	uint8_t onoff;
751184610Salfred
752188413Sthompsa	mtx_assert(sc->sc_mtx, MA_OWNED);
753184610Salfred
754184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
755184610Salfred		return (0);
756184610Salfred	}
757184610Salfred	if ((sigon == 0) && (sigoff == 0)) {
758184610Salfred
759184610Salfred		if (sc->sc_mcr & SER_DTR) {
760184610Salfred			sigon |= SER_DTR;
761184610Salfred		}
762184610Salfred		if (sc->sc_mcr & SER_RTS) {
763184610Salfred			sigon |= SER_RTS;
764184610Salfred		}
765184610Salfred		if (sc->sc_msr & SER_CTS) {
766184610Salfred			sigon |= SER_CTS;
767184610Salfred		}
768184610Salfred		if (sc->sc_msr & SER_DCD) {
769184610Salfred			sigon |= SER_DCD;
770184610Salfred		}
771184610Salfred		if (sc->sc_msr & SER_DSR) {
772184610Salfred			sigon |= SER_DSR;
773184610Salfred		}
774184610Salfred		if (sc->sc_msr & SER_RI) {
775184610Salfred			sigon |= SER_RI;
776184610Salfred		}
777184610Salfred		return (sigon);
778184610Salfred	}
779184610Salfred	if (sigon & SER_DTR) {
780184610Salfred		sc->sc_mcr |= SER_DTR;
781184610Salfred	}
782184610Salfred	if (sigoff & SER_DTR) {
783184610Salfred		sc->sc_mcr &= ~SER_DTR;
784184610Salfred	}
785184610Salfred	if (sigon & SER_RTS) {
786184610Salfred		sc->sc_mcr |= SER_RTS;
787184610Salfred	}
788184610Salfred	if (sigoff & SER_RTS) {
789184610Salfred		sc->sc_mcr &= ~SER_RTS;
790184610Salfred	}
791184610Salfred	onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
792194228Sthompsa	ucom_dtr(sc, onoff);
793184610Salfred
794184610Salfred	onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
795194228Sthompsa	ucom_rts(sc, onoff);
796184610Salfred
797184610Salfred	return (0);
798184610Salfred}
799184610Salfred
800184610Salfredstatic void
801194228Sthompsaucom_cfg_line_state(struct usb_proc_msg *_task)
802184610Salfred{
803192984Sthompsa	struct ucom_cfg_task *task =
804192984Sthompsa	    (struct ucom_cfg_task *)_task;
805192984Sthompsa	struct ucom_softc *sc = task->sc;
806188413Sthompsa	uint8_t notch_bits;
807188413Sthompsa	uint8_t any_bits;
808188413Sthompsa	uint8_t prev_value;
809188413Sthompsa	uint8_t last_value;
810188413Sthompsa	uint8_t mask;
811187176Sthompsa
812184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
813184610Salfred		return;
814184610Salfred	}
815184610Salfred
816188413Sthompsa	mask = 0;
817188413Sthompsa	/* compute callback mask */
818194228Sthompsa	if (sc->sc_callback->ucom_cfg_set_dtr)
819188413Sthompsa		mask |= UCOM_LS_DTR;
820194228Sthompsa	if (sc->sc_callback->ucom_cfg_set_rts)
821188413Sthompsa		mask |= UCOM_LS_RTS;
822194228Sthompsa	if (sc->sc_callback->ucom_cfg_set_break)
823188413Sthompsa		mask |= UCOM_LS_BREAK;
824197570Sthompsa	if (sc->sc_callback->ucom_cfg_set_ring)
825197570Sthompsa		mask |= UCOM_LS_RING;
826184610Salfred
827188413Sthompsa	/* compute the bits we are to program */
828188413Sthompsa	notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
829188413Sthompsa	any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
830188413Sthompsa	prev_value = sc->sc_pls_curr ^ notch_bits;
831188413Sthompsa	last_value = sc->sc_pls_curr;
832187176Sthompsa
833188413Sthompsa	/* reset programmed line state */
834188413Sthompsa	sc->sc_pls_curr = 0;
835188413Sthompsa	sc->sc_pls_set = 0;
836188413Sthompsa	sc->sc_pls_clr = 0;
837188413Sthompsa
838188413Sthompsa	/* ensure that we don't loose any levels */
839188413Sthompsa	if (notch_bits & UCOM_LS_DTR)
840194228Sthompsa		sc->sc_callback->ucom_cfg_set_dtr(sc,
841188413Sthompsa		    (prev_value & UCOM_LS_DTR) ? 1 : 0);
842188413Sthompsa	if (notch_bits & UCOM_LS_RTS)
843194228Sthompsa		sc->sc_callback->ucom_cfg_set_rts(sc,
844188413Sthompsa		    (prev_value & UCOM_LS_RTS) ? 1 : 0);
845188413Sthompsa	if (notch_bits & UCOM_LS_BREAK)
846194228Sthompsa		sc->sc_callback->ucom_cfg_set_break(sc,
847188413Sthompsa		    (prev_value & UCOM_LS_BREAK) ? 1 : 0);
848197570Sthompsa	if (notch_bits & UCOM_LS_RING)
849197570Sthompsa		sc->sc_callback->ucom_cfg_set_ring(sc,
850197570Sthompsa		    (prev_value & UCOM_LS_RING) ? 1 : 0);
851188413Sthompsa
852188413Sthompsa	/* set last value */
853188413Sthompsa	if (any_bits & UCOM_LS_DTR)
854194228Sthompsa		sc->sc_callback->ucom_cfg_set_dtr(sc,
855188413Sthompsa		    (last_value & UCOM_LS_DTR) ? 1 : 0);
856188413Sthompsa	if (any_bits & UCOM_LS_RTS)
857194228Sthompsa		sc->sc_callback->ucom_cfg_set_rts(sc,
858188413Sthompsa		    (last_value & UCOM_LS_RTS) ? 1 : 0);
859188413Sthompsa	if (any_bits & UCOM_LS_BREAK)
860194228Sthompsa		sc->sc_callback->ucom_cfg_set_break(sc,
861188413Sthompsa		    (last_value & UCOM_LS_BREAK) ? 1 : 0);
862197570Sthompsa	if (any_bits & UCOM_LS_RING)
863197570Sthompsa		sc->sc_callback->ucom_cfg_set_ring(sc,
864197570Sthompsa		    (last_value & UCOM_LS_RING) ? 1 : 0);
865187176Sthompsa}
866187176Sthompsa
867187176Sthompsastatic void
868194228Sthompsaucom_line_state(struct ucom_softc *sc,
869188413Sthompsa    uint8_t set_bits, uint8_t clear_bits)
870184610Salfred{
871188413Sthompsa	mtx_assert(sc->sc_mtx, MA_OWNED);
872184610Salfred
873184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
874184610Salfred		return;
875184610Salfred	}
876184610Salfred
877188413Sthompsa	DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
878184610Salfred
879188413Sthompsa	/* update current programmed line state */
880188413Sthompsa	sc->sc_pls_curr |= set_bits;
881188413Sthompsa	sc->sc_pls_curr &= ~clear_bits;
882188413Sthompsa	sc->sc_pls_set |= set_bits;
883188413Sthompsa	sc->sc_pls_clr |= clear_bits;
884187176Sthompsa
885188413Sthompsa	/* defer driver programming */
886194228Sthompsa	ucom_queue_command(sc, ucom_cfg_line_state, NULL,
887188413Sthompsa	    &sc->sc_line_state_task[0].hdr,
888188413Sthompsa	    &sc->sc_line_state_task[1].hdr);
889184610Salfred}
890184610Salfred
891184610Salfredstatic void
892197570Sthompsaucom_ring(struct ucom_softc *sc, uint8_t onoff)
893197570Sthompsa{
894197570Sthompsa	DPRINTF("onoff = %d\n", onoff);
895197570Sthompsa
896197570Sthompsa	if (onoff)
897197570Sthompsa		ucom_line_state(sc, UCOM_LS_RING, 0);
898197570Sthompsa	else
899197570Sthompsa		ucom_line_state(sc, 0, UCOM_LS_RING);
900197570Sthompsa}
901197570Sthompsa
902197570Sthompsastatic void
903194228Sthompsaucom_break(struct ucom_softc *sc, uint8_t onoff)
904187176Sthompsa{
905188413Sthompsa	DPRINTF("onoff = %d\n", onoff);
906187176Sthompsa
907188413Sthompsa	if (onoff)
908194228Sthompsa		ucom_line_state(sc, UCOM_LS_BREAK, 0);
909188413Sthompsa	else
910194228Sthompsa		ucom_line_state(sc, 0, UCOM_LS_BREAK);
911187176Sthompsa}
912187176Sthompsa
913187176Sthompsastatic void
914194228Sthompsaucom_dtr(struct ucom_softc *sc, uint8_t onoff)
915184610Salfred{
916184610Salfred	DPRINTF("onoff = %d\n", onoff);
917184610Salfred
918188413Sthompsa	if (onoff)
919194228Sthompsa		ucom_line_state(sc, UCOM_LS_DTR, 0);
920188413Sthompsa	else
921194228Sthompsa		ucom_line_state(sc, 0, UCOM_LS_DTR);
922184610Salfred}
923184610Salfred
924184610Salfredstatic void
925194228Sthompsaucom_rts(struct ucom_softc *sc, uint8_t onoff)
926184610Salfred{
927184610Salfred	DPRINTF("onoff = %d\n", onoff);
928184610Salfred
929188413Sthompsa	if (onoff)
930194228Sthompsa		ucom_line_state(sc, UCOM_LS_RTS, 0);
931188413Sthompsa	else
932194228Sthompsa		ucom_line_state(sc, 0, UCOM_LS_RTS);
933184610Salfred}
934184610Salfred
935184610Salfredstatic void
936194228Sthompsaucom_cfg_status_change(struct usb_proc_msg *_task)
937184610Salfred{
938192984Sthompsa	struct ucom_cfg_task *task =
939192984Sthompsa	    (struct ucom_cfg_task *)_task;
940192984Sthompsa	struct ucom_softc *sc = task->sc;
941184610Salfred	struct tty *tp;
942184610Salfred	uint8_t new_msr;
943184610Salfred	uint8_t new_lsr;
944184610Salfred	uint8_t onoff;
945213872Shselasky	uint8_t lsr_delta;
946184610Salfred
947184610Salfred	tp = sc->sc_tty;
948184610Salfred
949188413Sthompsa	mtx_assert(sc->sc_mtx, MA_OWNED);
950184610Salfred
951184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
952184610Salfred		return;
953184610Salfred	}
954194228Sthompsa	if (sc->sc_callback->ucom_cfg_get_status == NULL) {
955184610Salfred		return;
956184610Salfred	}
957184610Salfred	/* get status */
958184610Salfred
959184610Salfred	new_msr = 0;
960184610Salfred	new_lsr = 0;
961184610Salfred
962194228Sthompsa	(sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
963184610Salfred
964184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
965184610Salfred		/* TTY device closed */
966184610Salfred		return;
967184610Salfred	}
968184610Salfred	onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
969213872Shselasky	lsr_delta = (sc->sc_lsr ^ new_lsr);
970184610Salfred
971184610Salfred	sc->sc_msr = new_msr;
972184610Salfred	sc->sc_lsr = new_lsr;
973184610Salfred
974184610Salfred	if (onoff) {
975184610Salfred
976184610Salfred		onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
977184610Salfred
978184610Salfred		DPRINTF("DCD changed to %d\n", onoff);
979184610Salfred
980184610Salfred		ttydisc_modem(tp, onoff);
981184610Salfred	}
982213872Shselasky
983213872Shselasky	if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
984213872Shselasky
985213872Shselasky		DPRINTF("BREAK detected\n");
986213872Shselasky
987213872Shselasky		ttydisc_rint(tp, 0, TRE_BREAK);
988213872Shselasky		ttydisc_rint_done(tp);
989213872Shselasky	}
990213872Shselasky
991213872Shselasky	if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) {
992213872Shselasky
993213872Shselasky		DPRINTF("Frame error detected\n");
994213872Shselasky
995213872Shselasky		ttydisc_rint(tp, 0, TRE_FRAMING);
996213872Shselasky		ttydisc_rint_done(tp);
997213872Shselasky	}
998213872Shselasky
999213872Shselasky	if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) {
1000213872Shselasky
1001213872Shselasky		DPRINTF("Parity error detected\n");
1002213872Shselasky
1003213872Shselasky		ttydisc_rint(tp, 0, TRE_PARITY);
1004213872Shselasky		ttydisc_rint_done(tp);
1005213872Shselasky	}
1006184610Salfred}
1007184610Salfred
1008184610Salfredvoid
1009194228Sthompsaucom_status_change(struct ucom_softc *sc)
1010184610Salfred{
1011188413Sthompsa	mtx_assert(sc->sc_mtx, MA_OWNED);
1012184610Salfred
1013197570Sthompsa	if (sc->sc_flag & UCOM_FLAG_CONSOLE)
1014197570Sthompsa		return;		/* not supported */
1015197570Sthompsa
1016184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1017184610Salfred		return;
1018184610Salfred	}
1019184610Salfred	DPRINTF("\n");
1020184610Salfred
1021194228Sthompsa	ucom_queue_command(sc, ucom_cfg_status_change, NULL,
1022188413Sthompsa	    &sc->sc_status_task[0].hdr,
1023188413Sthompsa	    &sc->sc_status_task[1].hdr);
1024184610Salfred}
1025184610Salfred
1026184610Salfredstatic void
1027194228Sthompsaucom_cfg_param(struct usb_proc_msg *_task)
1028184610Salfred{
1029192984Sthompsa	struct ucom_param_task *task =
1030192984Sthompsa	    (struct ucom_param_task *)_task;
1031192984Sthompsa	struct ucom_softc *sc = task->sc;
1032184610Salfred
1033184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1034184610Salfred		return;
1035184610Salfred	}
1036194228Sthompsa	if (sc->sc_callback->ucom_cfg_param == NULL) {
1037184610Salfred		return;
1038184610Salfred	}
1039184610Salfred
1040194228Sthompsa	(sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
1041184610Salfred
1042184610Salfred	/* wait a little */
1043194228Sthompsa	usb_pause_mtx(sc->sc_mtx, hz / 10);
1044184610Salfred}
1045184610Salfred
1046184610Salfredstatic int
1047194228Sthompsaucom_param(struct tty *tp, struct termios *t)
1048184610Salfred{
1049192984Sthompsa	struct ucom_softc *sc = tty_softc(tp);
1050184610Salfred	uint8_t opened;
1051184610Salfred	int error;
1052184610Salfred
1053188413Sthompsa	mtx_assert(sc->sc_mtx, MA_OWNED);
1054184610Salfred
1055184610Salfred	opened = 0;
1056184610Salfred	error = 0;
1057184610Salfred
1058184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1059184610Salfred
1060184610Salfred		/* XXX the TTY layer should call "open()" first! */
1061184610Salfred
1062194228Sthompsa		error = ucom_open(tp);
1063184610Salfred		if (error) {
1064184610Salfred			goto done;
1065184610Salfred		}
1066184610Salfred		opened = 1;
1067184610Salfred	}
1068184610Salfred	DPRINTF("sc = %p\n", sc);
1069184610Salfred
1070184610Salfred	/* Check requested parameters. */
1071184610Salfred	if (t->c_ospeed < 0) {
1072184610Salfred		DPRINTF("negative ospeed\n");
1073184610Salfred		error = EINVAL;
1074184610Salfred		goto done;
1075184610Salfred	}
1076184610Salfred	if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
1077184610Salfred		DPRINTF("mismatch ispeed and ospeed\n");
1078184610Salfred		error = EINVAL;
1079184610Salfred		goto done;
1080184610Salfred	}
1081184610Salfred	t->c_ispeed = t->c_ospeed;
1082184610Salfred
1083194228Sthompsa	if (sc->sc_callback->ucom_pre_param) {
1084184610Salfred		/* Let the lower layer verify the parameters */
1085194228Sthompsa		error = (sc->sc_callback->ucom_pre_param) (sc, t);
1086184610Salfred		if (error) {
1087184610Salfred			DPRINTF("callback error = %d\n", error);
1088184610Salfred			goto done;
1089184610Salfred		}
1090184610Salfred	}
1091184610Salfred
1092184610Salfred	/* Disable transfers */
1093184610Salfred	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
1094184610Salfred
1095184610Salfred	/* Queue baud rate programming command first */
1096194228Sthompsa	ucom_queue_command(sc, ucom_cfg_param, t,
1097188413Sthompsa	    &sc->sc_param_task[0].hdr,
1098188413Sthompsa	    &sc->sc_param_task[1].hdr);
1099184610Salfred
1100184610Salfred	/* Queue transfer enable command last */
1101194228Sthompsa	ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
1102188413Sthompsa	    &sc->sc_start_task[0].hdr,
1103188413Sthompsa	    &sc->sc_start_task[1].hdr);
1104184610Salfred
1105184610Salfred	if (t->c_cflag & CRTS_IFLOW) {
1106184610Salfred		sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
1107184610Salfred	} else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
1108184610Salfred		sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
1109194228Sthompsa		ucom_modem(tp, SER_RTS, 0);
1110184610Salfred	}
1111184610Salfreddone:
1112184610Salfred	if (error) {
1113184610Salfred		if (opened) {
1114194228Sthompsa			ucom_close(tp);
1115184610Salfred		}
1116184610Salfred	}
1117184610Salfred	return (error);
1118184610Salfred}
1119184610Salfred
1120184610Salfredstatic void
1121194228Sthompsaucom_outwakeup(struct tty *tp)
1122184610Salfred{
1123192984Sthompsa	struct ucom_softc *sc = tty_softc(tp);
1124184610Salfred
1125188413Sthompsa	mtx_assert(sc->sc_mtx, MA_OWNED);
1126184610Salfred
1127184610Salfred	DPRINTF("sc = %p\n", sc);
1128184610Salfred
1129184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1130184610Salfred		/* The higher layer is not ready */
1131184610Salfred		return;
1132184610Salfred	}
1133194228Sthompsa	ucom_start_transfers(sc);
1134184610Salfred}
1135184610Salfred
1136184610Salfred/*------------------------------------------------------------------------*
1137194228Sthompsa *	ucom_get_data
1138184610Salfred *
1139184610Salfred * Return values:
1140184610Salfred * 0: No data is available.
1141184610Salfred * Else: Data is available.
1142184610Salfred *------------------------------------------------------------------------*/
1143184610Salfreduint8_t
1144194228Sthompsaucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1145184610Salfred    uint32_t offset, uint32_t len, uint32_t *actlen)
1146184610Salfred{
1147192984Sthompsa	struct usb_page_search res;
1148184610Salfred	struct tty *tp = sc->sc_tty;
1149184610Salfred	uint32_t cnt;
1150184610Salfred	uint32_t offset_orig;
1151184610Salfred
1152188413Sthompsa	mtx_assert(sc->sc_mtx, MA_OWNED);
1153184610Salfred
1154197570Sthompsa	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1155197570Sthompsa		unsigned int temp;
1156197570Sthompsa
1157197570Sthompsa		/* get total TX length */
1158197570Sthompsa
1159197570Sthompsa		temp = ucom_cons_tx_high - ucom_cons_tx_low;
1160197570Sthompsa		temp %= UCOM_CONS_BUFSIZE;
1161197570Sthompsa
1162197570Sthompsa		/* limit TX length */
1163197570Sthompsa
1164197570Sthompsa		if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low))
1165197570Sthompsa			temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
1166197570Sthompsa
1167197570Sthompsa		if (temp > len)
1168197570Sthompsa			temp = len;
1169197570Sthompsa
1170197570Sthompsa		/* copy in data */
1171197570Sthompsa
1172197570Sthompsa		usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
1173197570Sthompsa
1174197570Sthompsa		/* update counters */
1175197570Sthompsa
1176197570Sthompsa		ucom_cons_tx_low += temp;
1177197570Sthompsa		ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
1178197570Sthompsa
1179197570Sthompsa		/* store actual length */
1180197570Sthompsa
1181197570Sthompsa		*actlen = temp;
1182197570Sthompsa
1183197570Sthompsa		return (temp ? 1 : 0);
1184197570Sthompsa	}
1185197570Sthompsa
1186192820Sthompsa	if (tty_gone(tp) ||
1187192820Sthompsa	    !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
1188184610Salfred		actlen[0] = 0;
1189184610Salfred		return (0);		/* multiport device polling */
1190184610Salfred	}
1191184610Salfred	offset_orig = offset;
1192184610Salfred
1193184610Salfred	while (len != 0) {
1194184610Salfred
1195194228Sthompsa		usbd_get_page(pc, offset, &res);
1196184610Salfred
1197184610Salfred		if (res.length > len) {
1198184610Salfred			res.length = len;
1199184610Salfred		}
1200184610Salfred		/* copy data directly into USB buffer */
1201184610Salfred		cnt = ttydisc_getc(tp, res.buffer, res.length);
1202184610Salfred
1203184610Salfred		offset += cnt;
1204184610Salfred		len -= cnt;
1205184610Salfred
1206184610Salfred		if (cnt < res.length) {
1207184610Salfred			/* end of buffer */
1208184610Salfred			break;
1209184610Salfred		}
1210184610Salfred	}
1211184610Salfred
1212184610Salfred	actlen[0] = offset - offset_orig;
1213184610Salfred
1214184610Salfred	DPRINTF("cnt=%d\n", actlen[0]);
1215184610Salfred
1216184610Salfred	if (actlen[0] == 0) {
1217184610Salfred		return (0);
1218184610Salfred	}
1219184610Salfred	return (1);
1220184610Salfred}
1221184610Salfred
1222184610Salfredvoid
1223194228Sthompsaucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1224184610Salfred    uint32_t offset, uint32_t len)
1225184610Salfred{
1226192984Sthompsa	struct usb_page_search res;
1227184610Salfred	struct tty *tp = sc->sc_tty;
1228184610Salfred	char *buf;
1229184610Salfred	uint32_t cnt;
1230184610Salfred
1231188413Sthompsa	mtx_assert(sc->sc_mtx, MA_OWNED);
1232184610Salfred
1233197570Sthompsa	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1234197570Sthompsa		unsigned int temp;
1235197570Sthompsa
1236197570Sthompsa		/* get maximum RX length */
1237197570Sthompsa
1238197570Sthompsa		temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
1239197570Sthompsa		temp %= UCOM_CONS_BUFSIZE;
1240197570Sthompsa
1241197570Sthompsa		/* limit RX length */
1242197570Sthompsa
1243197570Sthompsa		if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high))
1244197570Sthompsa			temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
1245197570Sthompsa
1246197570Sthompsa		if (temp > len)
1247197570Sthompsa			temp = len;
1248197570Sthompsa
1249197570Sthompsa		/* copy out data */
1250197570Sthompsa
1251197570Sthompsa		usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
1252197570Sthompsa
1253197570Sthompsa		/* update counters */
1254197570Sthompsa
1255197570Sthompsa		ucom_cons_rx_high += temp;
1256197570Sthompsa		ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
1257197570Sthompsa
1258197570Sthompsa		return;
1259197570Sthompsa	}
1260197570Sthompsa
1261192820Sthompsa	if (tty_gone(tp))
1262184610Salfred		return;			/* multiport device polling */
1263192820Sthompsa
1264184610Salfred	if (len == 0)
1265184610Salfred		return;			/* no data */
1266184610Salfred
1267184610Salfred	/* set a flag to prevent recursation ? */
1268184610Salfred
1269184610Salfred	while (len > 0) {
1270184610Salfred
1271194228Sthompsa		usbd_get_page(pc, offset, &res);
1272184610Salfred
1273184610Salfred		if (res.length > len) {
1274184610Salfred			res.length = len;
1275184610Salfred		}
1276184610Salfred		len -= res.length;
1277184610Salfred		offset += res.length;
1278184610Salfred
1279184610Salfred		/* pass characters to tty layer */
1280184610Salfred
1281184610Salfred		buf = res.buffer;
1282184610Salfred		cnt = res.length;
1283184610Salfred
1284184610Salfred		/* first check if we can pass the buffer directly */
1285184610Salfred
1286184610Salfred		if (ttydisc_can_bypass(tp)) {
1287184610Salfred			if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1288184610Salfred				DPRINTF("tp=%p, data lost\n", tp);
1289184610Salfred			}
1290184610Salfred			continue;
1291184610Salfred		}
1292184610Salfred		/* need to loop */
1293184610Salfred
1294184610Salfred		for (cnt = 0; cnt != res.length; cnt++) {
1295184610Salfred			if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
1296184610Salfred				/* XXX what should we do? */
1297184610Salfred
1298184610Salfred				DPRINTF("tp=%p, lost %d "
1299184610Salfred				    "chars\n", tp, res.length - cnt);
1300184610Salfred				break;
1301184610Salfred			}
1302184610Salfred		}
1303184610Salfred	}
1304184610Salfred	ttydisc_rint_done(tp);
1305184610Salfred}
1306184610Salfred
1307184610Salfredstatic void
1308194228Sthompsaucom_free(void *xsc)
1309184610Salfred{
1310192984Sthompsa	struct ucom_softc *sc = xsc;
1311184610Salfred
1312188413Sthompsa	mtx_lock(sc->sc_mtx);
1313184610Salfred	sc->sc_ttyfreed = 1;
1314194227Sthompsa	cv_signal(&sc->sc_cv);
1315188413Sthompsa	mtx_unlock(sc->sc_mtx);
1316184610Salfred}
1317197570Sthompsa
1318197570Sthompsastatic cn_probe_t ucom_cnprobe;
1319197570Sthompsastatic cn_init_t ucom_cninit;
1320197570Sthompsastatic cn_term_t ucom_cnterm;
1321197570Sthompsastatic cn_getc_t ucom_cngetc;
1322197570Sthompsastatic cn_putc_t ucom_cnputc;
1323197570Sthompsa
1324197570SthompsaCONSOLE_DRIVER(ucom);
1325197570Sthompsa
1326197570Sthompsastatic void
1327197570Sthompsaucom_cnprobe(struct consdev  *cp)
1328197570Sthompsa{
1329198774Sthompsa	if (ucom_cons_unit != -1)
1330198774Sthompsa		cp->cn_pri = CN_NORMAL;
1331198774Sthompsa	else
1332198774Sthompsa		cp->cn_pri = CN_DEAD;
1333198774Sthompsa
1334198774Sthompsa	strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name));
1335197570Sthompsa}
1336197570Sthompsa
1337197570Sthompsastatic void
1338197570Sthompsaucom_cninit(struct consdev  *cp)
1339197570Sthompsa{
1340197570Sthompsa}
1341197570Sthompsa
1342197570Sthompsastatic void
1343197570Sthompsaucom_cnterm(struct consdev  *cp)
1344197570Sthompsa{
1345197570Sthompsa}
1346197570Sthompsa
1347197570Sthompsastatic int
1348197570Sthompsaucom_cngetc(struct consdev *cd)
1349197570Sthompsa{
1350197570Sthompsa	struct ucom_softc *sc = ucom_cons_softc;
1351197570Sthompsa	int c;
1352197570Sthompsa
1353197570Sthompsa	if (sc == NULL)
1354197570Sthompsa		return (-1);
1355197570Sthompsa
1356197570Sthompsa	mtx_lock(sc->sc_mtx);
1357197570Sthompsa
1358197570Sthompsa	if (ucom_cons_rx_low != ucom_cons_rx_high) {
1359197570Sthompsa		c = ucom_cons_rx_buf[ucom_cons_rx_low];
1360197570Sthompsa		ucom_cons_rx_low ++;
1361197570Sthompsa		ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
1362197570Sthompsa	} else {
1363197570Sthompsa		c = -1;
1364197570Sthompsa	}
1365197570Sthompsa
1366197570Sthompsa	/* start USB transfers */
1367197570Sthompsa	ucom_outwakeup(sc->sc_tty);
1368197570Sthompsa
1369197570Sthompsa	mtx_unlock(sc->sc_mtx);
1370197570Sthompsa
1371197570Sthompsa	/* poll if necessary */
1372197570Sthompsa	if (kdb_active && sc->sc_callback->ucom_poll)
1373197570Sthompsa		(sc->sc_callback->ucom_poll) (sc);
1374197570Sthompsa
1375197570Sthompsa	return (c);
1376197570Sthompsa}
1377197570Sthompsa
1378197570Sthompsastatic void
1379197570Sthompsaucom_cnputc(struct consdev *cd, int c)
1380197570Sthompsa{
1381197570Sthompsa	struct ucom_softc *sc = ucom_cons_softc;
1382197570Sthompsa	unsigned int temp;
1383197570Sthompsa
1384197570Sthompsa	if (sc == NULL)
1385197570Sthompsa		return;
1386197570Sthompsa
1387197570Sthompsa repeat:
1388197570Sthompsa
1389197570Sthompsa	mtx_lock(sc->sc_mtx);
1390197570Sthompsa
1391197570Sthompsa	/* compute maximum TX length */
1392197570Sthompsa
1393197570Sthompsa	temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
1394197570Sthompsa	temp %= UCOM_CONS_BUFSIZE;
1395197570Sthompsa
1396197570Sthompsa	if (temp) {
1397197570Sthompsa		ucom_cons_tx_buf[ucom_cons_tx_high] = c;
1398197570Sthompsa		ucom_cons_tx_high ++;
1399197570Sthompsa		ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
1400197570Sthompsa	}
1401197570Sthompsa
1402197570Sthompsa	/* start USB transfers */
1403197570Sthompsa	ucom_outwakeup(sc->sc_tty);
1404197570Sthompsa
1405197570Sthompsa	mtx_unlock(sc->sc_mtx);
1406197570Sthompsa
1407197570Sthompsa	/* poll if necessary */
1408197570Sthompsa	if (kdb_active && sc->sc_callback->ucom_poll) {
1409197570Sthompsa		(sc->sc_callback->ucom_poll) (sc);
1410197570Sthompsa		/* simple flow control */
1411197570Sthompsa		if (temp == 0)
1412197570Sthompsa			goto repeat;
1413197570Sthompsa	}
1414197570Sthompsa}
1415197570Sthompsa
1416197570Sthompsa#if defined(GDB)
1417197570Sthompsa
1418197570Sthompsa#include <gdb/gdb.h>
1419197570Sthompsa
1420197570Sthompsastatic gdb_probe_f ucom_gdbprobe;
1421197570Sthompsastatic gdb_init_f ucom_gdbinit;
1422197570Sthompsastatic gdb_term_f ucom_gdbterm;
1423197570Sthompsastatic gdb_getc_f ucom_gdbgetc;
1424197570Sthompsastatic gdb_putc_f ucom_gdbputc;
1425197570Sthompsa
1426197570SthompsaGDB_DBGPORT(sio, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc);
1427197570Sthompsa
1428197570Sthompsastatic int
1429197570Sthompsaucom_gdbprobe(void)
1430197570Sthompsa{
1431197570Sthompsa	return ((ucom_cons_softc != NULL) ? 0 : -1);
1432197570Sthompsa}
1433197570Sthompsa
1434197570Sthompsastatic void
1435197570Sthompsaucom_gdbinit(void)
1436197570Sthompsa{
1437197570Sthompsa}
1438197570Sthompsa
1439197570Sthompsastatic void
1440197570Sthompsaucom_gdbterm(void)
1441197570Sthompsa{
1442197570Sthompsa}
1443197570Sthompsa
1444197570Sthompsastatic void
1445197570Sthompsaucom_gdbputc(int c)
1446197570Sthompsa{
1447197570Sthompsa        ucom_cnputc(NULL, c);
1448197570Sthompsa}
1449197570Sthompsa
1450197570Sthompsastatic int
1451197570Sthompsaucom_gdbgetc(void)
1452197570Sthompsa{
1453197570Sthompsa        return (ucom_cngetc(NULL));
1454197570Sthompsa}
1455197570Sthompsa
1456197570Sthompsa#endif
1457