xref: /freebsd-src/sys/dev/usb/serial/usb_serial.c (revision 36a80f4264350a2f4f0686eb91ae7f5943d40327)
102ac6454SAndrew Thompson /*	$NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $	*/
202ac6454SAndrew Thompson 
302ac6454SAndrew Thompson /*-
4eebd9d53SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
5718cf2ccSPedro F. Giffuni  *
602ac6454SAndrew Thompson  * Copyright (c) 2001-2003, 2005, 2008
702ac6454SAndrew Thompson  *	Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
802ac6454SAndrew Thompson  * All rights reserved.
902ac6454SAndrew Thompson  *
1002ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
1102ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
1202ac6454SAndrew Thompson  * are met:
1302ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
1402ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
1502ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
1602ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
1702ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
1802ac6454SAndrew Thompson  *
1902ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2002ac6454SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2102ac6454SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2202ac6454SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2302ac6454SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2402ac6454SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2502ac6454SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2602ac6454SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2702ac6454SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2802ac6454SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2902ac6454SAndrew Thompson  * SUCH DAMAGE.
3002ac6454SAndrew Thompson  */
3102ac6454SAndrew Thompson 
3202ac6454SAndrew Thompson #include <sys/cdefs.h>
3302ac6454SAndrew Thompson /*-
3402ac6454SAndrew Thompson  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
3502ac6454SAndrew Thompson  * All rights reserved.
3602ac6454SAndrew Thompson  *
3702ac6454SAndrew Thompson  * This code is derived from software contributed to The NetBSD Foundation
3802ac6454SAndrew Thompson  * by Lennart Augustsson (lennart@augustsson.net) at
3902ac6454SAndrew Thompson  * Carlstedt Research & Technology.
4002ac6454SAndrew Thompson  *
4102ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
4202ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
4302ac6454SAndrew Thompson  * are met:
4402ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
4502ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
4602ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
4702ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
4802ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
4902ac6454SAndrew Thompson  *
5002ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
5102ac6454SAndrew Thompson  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
5202ac6454SAndrew Thompson  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
5302ac6454SAndrew Thompson  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
5402ac6454SAndrew Thompson  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
5502ac6454SAndrew Thompson  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
5602ac6454SAndrew Thompson  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
5702ac6454SAndrew Thompson  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
5802ac6454SAndrew Thompson  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
5902ac6454SAndrew Thompson  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
6002ac6454SAndrew Thompson  * POSSIBILITY OF SUCH DAMAGE.
6102ac6454SAndrew Thompson  */
6202ac6454SAndrew Thompson 
63ed6d949aSAndrew Thompson #include <sys/stdint.h>
64ed6d949aSAndrew Thompson #include <sys/stddef.h>
65ed6d949aSAndrew Thompson #include <sys/param.h>
66ed6d949aSAndrew Thompson #include <sys/queue.h>
67ed6d949aSAndrew Thompson #include <sys/types.h>
68ed6d949aSAndrew Thompson #include <sys/systm.h>
69ed6d949aSAndrew Thompson #include <sys/kernel.h>
70ed6d949aSAndrew Thompson #include <sys/bus.h>
71ed6d949aSAndrew Thompson #include <sys/module.h>
72ed6d949aSAndrew Thompson #include <sys/lock.h>
73ed6d949aSAndrew Thompson #include <sys/mutex.h>
74ed6d949aSAndrew Thompson #include <sys/condvar.h>
75ed6d949aSAndrew Thompson #include <sys/sysctl.h>
76ed6d949aSAndrew Thompson #include <sys/sx.h>
77ed6d949aSAndrew Thompson #include <sys/unistd.h>
78ed6d949aSAndrew Thompson #include <sys/callout.h>
79ed6d949aSAndrew Thompson #include <sys/malloc.h>
80ed6d949aSAndrew Thompson #include <sys/priv.h>
81655dc9d0SAndrew Thompson #include <sys/cons.h>
82ed6d949aSAndrew Thompson 
835508dc2aSIan Lepore #include <dev/uart/uart_ppstypes.h>
845508dc2aSIan Lepore 
8502ac6454SAndrew Thompson #include <dev/usb/usb.h>
86ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
87ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
8802ac6454SAndrew Thompson 
89a593f6b8SAndrew Thompson #define	USB_DEBUG_VAR ucom_debug
9002ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
9102ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h>
92ed6d949aSAndrew Thompson #include <dev/usb/usb_process.h>
9302ac6454SAndrew Thompson 
9402ac6454SAndrew Thompson #include <dev/usb/serial/usb_serial.h>
9502ac6454SAndrew Thompson 
96655dc9d0SAndrew Thompson #include "opt_gdb.h"
97655dc9d0SAndrew Thompson 
98f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
99f8d2b1f3SPawel Biernacki     "USB ucom");
100655dc9d0SAndrew Thompson 
101bb057644SHans Petter Selasky static int ucom_pps_mode;
102bb057644SHans Petter Selasky 
103bb057644SHans Petter Selasky SYSCTL_INT(_hw_usb_ucom, OID_AUTO, pps_mode, CTLFLAG_RWTUN,
1045508dc2aSIan Lepore     &ucom_pps_mode, 0,
1055508dc2aSIan Lepore     "pulse capture mode: 0/1/2=disabled/CTS/DCD; add 0x10 to invert");
106bb057644SHans Petter Selasky 
1077762e8a1SEdward Tomasz Napierala static int ucom_device_mode_console = 1;
1087762e8a1SEdward Tomasz Napierala 
1097762e8a1SEdward Tomasz Napierala SYSCTL_INT(_hw_usb_ucom, OID_AUTO, device_mode_console, CTLFLAG_RW,
1107762e8a1SEdward Tomasz Napierala     &ucom_device_mode_console, 0,
1117762e8a1SEdward Tomasz Napierala     "set to 1 to mark terminals as consoles when in device mode");
1127762e8a1SEdward Tomasz Napierala 
113b850ecc1SAndrew Thompson #ifdef USB_DEBUG
114a593f6b8SAndrew Thompson static int ucom_debug = 0;
11502ac6454SAndrew Thompson 
116ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RWTUN,
117a593f6b8SAndrew Thompson     &ucom_debug, 0, "ucom debug level");
11802ac6454SAndrew Thompson #endif
11902ac6454SAndrew Thompson 
120655dc9d0SAndrew Thompson #define	UCOM_CONS_BUFSIZE 1024
121655dc9d0SAndrew Thompson 
122655dc9d0SAndrew Thompson static uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE];
123655dc9d0SAndrew Thompson static uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE];
124655dc9d0SAndrew Thompson 
12562d42655SHans Petter Selasky static unsigned ucom_cons_rx_low = 0;
12662d42655SHans Petter Selasky static unsigned ucom_cons_rx_high = 0;
127655dc9d0SAndrew Thompson 
12862d42655SHans Petter Selasky static unsigned ucom_cons_tx_low = 0;
12962d42655SHans Petter Selasky static unsigned ucom_cons_tx_high = 0;
130655dc9d0SAndrew Thompson 
131655dc9d0SAndrew Thompson static int ucom_cons_unit = -1;
132015bb88fSNick Hibma static int ucom_cons_subunit = 0;
1334722ceb7SEd Maste static int ucom_cons_baud = 115200;
134655dc9d0SAndrew Thompson static struct ucom_softc *ucom_cons_softc = NULL;
135655dc9d0SAndrew Thompson 
136af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RWTUN,
137655dc9d0SAndrew Thompson     &ucom_cons_unit, 0, "console unit number");
138af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_subunit, CTLFLAG_RWTUN,
139015bb88fSNick Hibma     &ucom_cons_subunit, 0, "console subunit number");
140af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RWTUN,
141655dc9d0SAndrew Thompson     &ucom_cons_baud, 0, "console baud rate");
142655dc9d0SAndrew Thompson 
143a593f6b8SAndrew Thompson static usb_proc_callback_t ucom_cfg_start_transfers;
144a593f6b8SAndrew Thompson static usb_proc_callback_t ucom_cfg_open;
145a593f6b8SAndrew Thompson static usb_proc_callback_t ucom_cfg_close;
146a593f6b8SAndrew Thompson static usb_proc_callback_t ucom_cfg_line_state;
147a593f6b8SAndrew Thompson static usb_proc_callback_t ucom_cfg_status_change;
148a593f6b8SAndrew Thompson static usb_proc_callback_t ucom_cfg_param;
14902ac6454SAndrew Thompson 
150015bb88fSNick Hibma static int	ucom_unit_alloc(void);
151015bb88fSNick Hibma static void	ucom_unit_free(int);
152015bb88fSNick Hibma static int	ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *);
1538f42c748SHans Petter Selasky static void	ucom_detach_tty(struct ucom_super_softc *, struct ucom_softc *);
15451f3d087SKyle Evans static int	ucom_queue_command(struct ucom_softc *,
155e0a69b51SAndrew Thompson 		    usb_proc_callback_t *, struct termios *pt,
156*36a80f42SKyle Evans 		    struct usb_proc_msg *t0, struct usb_proc_msg *t1, bool wait);
157a593f6b8SAndrew Thompson static void	ucom_shutdown(struct ucom_softc *);
158655dc9d0SAndrew Thompson static void	ucom_ring(struct ucom_softc *, uint8_t);
159a593f6b8SAndrew Thompson static void	ucom_break(struct ucom_softc *, uint8_t);
160a593f6b8SAndrew Thompson static void	ucom_dtr(struct ucom_softc *, uint8_t);
161a593f6b8SAndrew Thompson static void	ucom_rts(struct ucom_softc *, uint8_t);
16202ac6454SAndrew Thompson 
163a593f6b8SAndrew Thompson static tsw_open_t ucom_open;
164a593f6b8SAndrew Thompson static tsw_close_t ucom_close;
165a593f6b8SAndrew Thompson static tsw_ioctl_t ucom_ioctl;
166a593f6b8SAndrew Thompson static tsw_modem_t ucom_modem;
167a593f6b8SAndrew Thompson static tsw_param_t ucom_param;
168a593f6b8SAndrew Thompson static tsw_outwakeup_t ucom_outwakeup;
169d30d96eaSHans Petter Selasky static tsw_inwakeup_t ucom_inwakeup;
170a593f6b8SAndrew Thompson static tsw_free_t ucom_free;
1719ad221a5SIan Lepore static tsw_busy_t ucom_busy;
17202ac6454SAndrew Thompson 
173a593f6b8SAndrew Thompson static struct ttydevsw ucom_class = {
17402ac6454SAndrew Thompson 	.tsw_flags = TF_INITLOCK | TF_CALLOUT,
175a593f6b8SAndrew Thompson 	.tsw_open = ucom_open,
176a593f6b8SAndrew Thompson 	.tsw_close = ucom_close,
177a593f6b8SAndrew Thompson 	.tsw_outwakeup = ucom_outwakeup,
178d30d96eaSHans Petter Selasky 	.tsw_inwakeup = ucom_inwakeup,
179a593f6b8SAndrew Thompson 	.tsw_ioctl = ucom_ioctl,
180a593f6b8SAndrew Thompson 	.tsw_param = ucom_param,
181a593f6b8SAndrew Thompson 	.tsw_modem = ucom_modem,
182a593f6b8SAndrew Thompson 	.tsw_free = ucom_free,
1839ad221a5SIan Lepore 	.tsw_busy = ucom_busy,
18402ac6454SAndrew Thompson };
18502ac6454SAndrew Thompson 
18602ac6454SAndrew Thompson MODULE_DEPEND(ucom, usb, 1, 1, 1);
18702ac6454SAndrew Thompson MODULE_VERSION(ucom, 1);
18802ac6454SAndrew Thompson 
1898f42c748SHans Petter Selasky #define	UCOM_UNIT_MAX 		128	/* maximum number of units */
1906416c259SNick Hibma #define	UCOM_TTY_PREFIX		"U"
1916416c259SNick Hibma 
1928f42c748SHans Petter Selasky static struct unrhdr *ucom_unrhdr;
1938f42c748SHans Petter Selasky static struct mtx ucom_mtx;
1948f42c748SHans Petter Selasky static int ucom_close_refs;
1958f42c748SHans Petter Selasky 
1968f42c748SHans Petter Selasky static void
1978f42c748SHans Petter Selasky ucom_init(void *arg)
1988f42c748SHans Petter Selasky {
1998f42c748SHans Petter Selasky 	DPRINTF("\n");
2008f42c748SHans Petter Selasky 	ucom_unrhdr = new_unrhdr(0, UCOM_UNIT_MAX - 1, NULL);
2018f42c748SHans Petter Selasky 	mtx_init(&ucom_mtx, "UCOM MTX", NULL, MTX_DEF);
2028f42c748SHans Petter Selasky }
2038f42c748SHans Petter Selasky SYSINIT(ucom_init, SI_SUB_KLD - 1, SI_ORDER_ANY, ucom_init, NULL);
2048f42c748SHans Petter Selasky 
2058f42c748SHans Petter Selasky static void
2068f42c748SHans Petter Selasky ucom_uninit(void *arg)
2078f42c748SHans Petter Selasky {
2088f42c748SHans Petter Selasky 	struct unrhdr *hdr;
2098f42c748SHans Petter Selasky 	hdr = ucom_unrhdr;
2108f42c748SHans Petter Selasky 	ucom_unrhdr = NULL;
2118f42c748SHans Petter Selasky 
2128f42c748SHans Petter Selasky 	DPRINTF("\n");
2138f42c748SHans Petter Selasky 
2148f42c748SHans Petter Selasky 	if (hdr != NULL)
2158f42c748SHans Petter Selasky 		delete_unrhdr(hdr);
2168f42c748SHans Petter Selasky 
2178f42c748SHans Petter Selasky 	mtx_destroy(&ucom_mtx);
2188f42c748SHans Petter Selasky }
219d9b6ab3aSHans Petter Selasky SYSUNINIT(ucom_uninit, SI_SUB_KLD - 3, SI_ORDER_ANY, ucom_uninit, NULL);
2208f42c748SHans Petter Selasky 
221015bb88fSNick Hibma /*
222015bb88fSNick Hibma  * Mark a unit number (the X in cuaUX) as in use.
223015bb88fSNick Hibma  *
224015bb88fSNick Hibma  * Note that devices using a different naming scheme (see ucom_tty_name()
225015bb88fSNick Hibma  * callback) still use this unit allocation.
226015bb88fSNick Hibma  */
227015bb88fSNick Hibma static int
228015bb88fSNick Hibma ucom_unit_alloc(void)
22902ac6454SAndrew Thompson {
230015bb88fSNick Hibma 	int unit;
23102ac6454SAndrew Thompson 
2328f42c748SHans Petter Selasky 	/* sanity checks */
2338f42c748SHans Petter Selasky 	if (ucom_unrhdr == NULL) {
2348f42c748SHans Petter Selasky 		DPRINTF("ucom_unrhdr is NULL\n");
2358f42c748SHans Petter Selasky 		return (-1);
23696a6480aSNick Hibma 	}
2378f42c748SHans Petter Selasky 	unit = alloc_unr(ucom_unrhdr);
2388f42c748SHans Petter Selasky 	DPRINTF("unit %d is allocated\n", unit);
2398f42c748SHans Petter Selasky 	return (unit);
24002ac6454SAndrew Thompson }
24102ac6454SAndrew Thompson 
242015bb88fSNick Hibma /*
243015bb88fSNick Hibma  * Mark the unit number as not in use.
244015bb88fSNick Hibma  */
24502ac6454SAndrew Thompson static void
246015bb88fSNick Hibma ucom_unit_free(int unit)
24702ac6454SAndrew Thompson {
2488f42c748SHans Petter Selasky 	/* sanity checks */
2498f42c748SHans Petter Selasky 	if (unit < 0 || unit >= UCOM_UNIT_MAX || ucom_unrhdr == NULL) {
2508f42c748SHans Petter Selasky 		DPRINTF("cannot free unit number\n");
2518f42c748SHans Petter Selasky 		return;
2528f42c748SHans Petter Selasky 	}
2538f42c748SHans Petter Selasky 	DPRINTF("unit %d is freed\n", unit);
2548f42c748SHans Petter Selasky 	free_unr(ucom_unrhdr, unit);
25502ac6454SAndrew Thompson }
25602ac6454SAndrew Thompson 
25702ac6454SAndrew Thompson /*
258015bb88fSNick Hibma  * Setup a group of one or more serial ports.
25902ac6454SAndrew Thompson  *
26002ac6454SAndrew Thompson  * The mutex pointed to by "mtx" is applied before all
26102ac6454SAndrew Thompson  * callbacks are called back. Also "mtx" must be applied
26202ac6454SAndrew Thompson  * before calling into the ucom-layer!
26302ac6454SAndrew Thompson  */
26402ac6454SAndrew Thompson int
265a593f6b8SAndrew Thompson ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
2666d917491SHans Petter Selasky     int subunits, void *parent,
267760bc48eSAndrew Thompson     const struct ucom_callback *callback, struct mtx *mtx)
26802ac6454SAndrew Thompson {
2696d917491SHans Petter Selasky 	int subunit;
27002ac6454SAndrew Thompson 	int error = 0;
27102ac6454SAndrew Thompson 
27202ac6454SAndrew Thompson 	if ((sc == NULL) ||
2736d917491SHans Petter Selasky 	    (subunits <= 0) ||
2748f42c748SHans Petter Selasky 	    (callback == NULL) ||
2758f42c748SHans Petter Selasky 	    (mtx == NULL)) {
27602ac6454SAndrew Thompson 		return (EINVAL);
27702ac6454SAndrew Thompson 	}
27802ac6454SAndrew Thompson 
27996d87d2bSHans Petter Selasky 	/* allocate a uniq unit number */
280015bb88fSNick Hibma 	ssc->sc_unit = ucom_unit_alloc();
281015bb88fSNick Hibma 	if (ssc->sc_unit == -1)
28202ac6454SAndrew Thompson 		return (ENOMEM);
28302ac6454SAndrew Thompson 
28496d87d2bSHans Petter Selasky 	/* generate TTY name string */
28596d87d2bSHans Petter Selasky 	snprintf(ssc->sc_ttyname, sizeof(ssc->sc_ttyname),
28696d87d2bSHans Petter Selasky 	    UCOM_TTY_PREFIX "%d", ssc->sc_unit);
28796d87d2bSHans Petter Selasky 
28896d87d2bSHans Petter Selasky 	/* create USB request handling process */
289a593f6b8SAndrew Thompson 	error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
29002ac6454SAndrew Thompson 	if (error) {
291015bb88fSNick Hibma 		ucom_unit_free(ssc->sc_unit);
29202ac6454SAndrew Thompson 		return (error);
29302ac6454SAndrew Thompson 	}
294015bb88fSNick Hibma 	ssc->sc_subunits = subunits;
295c01fc06eSHans Petter Selasky 	ssc->sc_flag = UCOM_FLAG_ATTACHED |
2967762e8a1SEdward Tomasz Napierala 	    UCOM_FLAG_FREE_UNIT | (ssc->sc_flag & UCOM_FLAG_DEVICE_MODE);
29702ac6454SAndrew Thompson 
298c01fc06eSHans Petter Selasky 	if (callback->ucom_free == NULL)
299c01fc06eSHans Petter Selasky 		ssc->sc_flag |= UCOM_FLAG_WAIT_REFS;
300c01fc06eSHans Petter Selasky 
301c01fc06eSHans Petter Selasky 	/* increment reference count */
3028f42c748SHans Petter Selasky 	ucom_ref(ssc);
3038f42c748SHans Petter Selasky 
304761481dcSNick Hibma 	for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
305015bb88fSNick Hibma 		sc[subunit].sc_subunit = subunit;
306015bb88fSNick Hibma 		sc[subunit].sc_super = ssc;
307015bb88fSNick Hibma 		sc[subunit].sc_mtx = mtx;
308015bb88fSNick Hibma 		sc[subunit].sc_parent = parent;
309015bb88fSNick Hibma 		sc[subunit].sc_callback = callback;
31002ac6454SAndrew Thompson 
311015bb88fSNick Hibma 		error = ucom_attach_tty(ssc, &sc[subunit]);
31202ac6454SAndrew Thompson 		if (error) {
313015bb88fSNick Hibma 			ucom_detach(ssc, &sc[0]);
31402ac6454SAndrew Thompson 			return (error);
31502ac6454SAndrew Thompson 		}
3168f42c748SHans Petter Selasky 		/* increment reference count */
3178f42c748SHans Petter Selasky 		ucom_ref(ssc);
3188f42c748SHans Petter Selasky 
3198f42c748SHans Petter Selasky 		/* set subunit attached */
320015bb88fSNick Hibma 		sc[subunit].sc_flag |= UCOM_FLAG_ATTACHED;
32102ac6454SAndrew Thompson 	}
322015bb88fSNick Hibma 
323761481dcSNick Hibma 	DPRINTF("tp = %p, unit = %d, subunits = %d\n",
324761481dcSNick Hibma 		sc->sc_tty, ssc->sc_unit, ssc->sc_subunits);
325015bb88fSNick Hibma 
32602ac6454SAndrew Thompson 	return (0);
32702ac6454SAndrew Thompson }
32802ac6454SAndrew Thompson 
32902ac6454SAndrew Thompson /*
330c01fc06eSHans Petter Selasky  * The following function will do nothing if the structure pointed to
331c01fc06eSHans Petter Selasky  * by "ssc" and "sc" is zero or has already been detached.
33202ac6454SAndrew Thompson  */
33302ac6454SAndrew Thompson void
334015bb88fSNick Hibma ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc)
33502ac6454SAndrew Thompson {
3366d917491SHans Petter Selasky 	int subunit;
33702ac6454SAndrew Thompson 
338c01fc06eSHans Petter Selasky 	if (!(ssc->sc_flag & UCOM_FLAG_ATTACHED))
339f6c6639aSHans Petter Selasky 		return;		/* not initialized */
340f6c6639aSHans Petter Selasky 
34196d87d2bSHans Petter Selasky 	if (ssc->sc_sysctl_ttyname != NULL) {
34296d87d2bSHans Petter Selasky 		sysctl_remove_oid(ssc->sc_sysctl_ttyname, 1, 0);
34396d87d2bSHans Petter Selasky 		ssc->sc_sysctl_ttyname = NULL;
3443a4a1a7fSHans Petter Selasky 	}
3453a4a1a7fSHans Petter Selasky 
3463a4a1a7fSHans Petter Selasky 	if (ssc->sc_sysctl_ttyports != NULL) {
3473a4a1a7fSHans Petter Selasky 		sysctl_remove_oid(ssc->sc_sysctl_ttyports, 1, 0);
3483a4a1a7fSHans Petter Selasky 		ssc->sc_sysctl_ttyports = NULL;
3493a4a1a7fSHans Petter Selasky 	}
3503a4a1a7fSHans Petter Selasky 
351a593f6b8SAndrew Thompson 	usb_proc_drain(&ssc->sc_tq);
35202ac6454SAndrew Thompson 
353761481dcSNick Hibma 	for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
354015bb88fSNick Hibma 		if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) {
3558f42c748SHans Petter Selasky 			ucom_detach_tty(ssc, &sc[subunit]);
35602ac6454SAndrew Thompson 
357015bb88fSNick Hibma 			/* avoid duplicate detach */
358015bb88fSNick Hibma 			sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED;
35902ac6454SAndrew Thompson 		}
36002ac6454SAndrew Thompson 	}
361a593f6b8SAndrew Thompson 	usb_proc_free(&ssc->sc_tq);
3628f42c748SHans Petter Selasky 
3638f42c748SHans Petter Selasky 	ucom_unref(ssc);
364c01fc06eSHans Petter Selasky 
365c01fc06eSHans Petter Selasky 	if (ssc->sc_flag & UCOM_FLAG_WAIT_REFS)
3668f42c748SHans Petter Selasky 		ucom_drain(ssc);
367c01fc06eSHans Petter Selasky 
368c01fc06eSHans Petter Selasky 	/* make sure we don't detach twice */
369c01fc06eSHans Petter Selasky 	ssc->sc_flag &= ~UCOM_FLAG_ATTACHED;
3708f42c748SHans Petter Selasky }
3718f42c748SHans Petter Selasky 
3728f42c748SHans Petter Selasky void
3738f42c748SHans Petter Selasky ucom_drain(struct ucom_super_softc *ssc)
3748f42c748SHans Petter Selasky {
3758f42c748SHans Petter Selasky 	mtx_lock(&ucom_mtx);
376c01fc06eSHans Petter Selasky 	while (ssc->sc_refs > 0) {
3778f42c748SHans Petter Selasky 		printf("ucom: Waiting for a TTY device to close.\n");
3788f42c748SHans Petter Selasky 		usb_pause_mtx(&ucom_mtx, hz);
3798f42c748SHans Petter Selasky 	}
3808f42c748SHans Petter Selasky 	mtx_unlock(&ucom_mtx);
3818f42c748SHans Petter Selasky }
3828f42c748SHans Petter Selasky 
3838f42c748SHans Petter Selasky void
3848f42c748SHans Petter Selasky ucom_drain_all(void *arg)
3858f42c748SHans Petter Selasky {
3868f42c748SHans Petter Selasky 	mtx_lock(&ucom_mtx);
3878f42c748SHans Petter Selasky 	while (ucom_close_refs > 0) {
3888f42c748SHans Petter Selasky 		printf("ucom: Waiting for all detached TTY "
3898f42c748SHans Petter Selasky 		    "devices to have open fds closed.\n");
3908f42c748SHans Petter Selasky 		usb_pause_mtx(&ucom_mtx, hz);
3918f42c748SHans Petter Selasky 	}
3928f42c748SHans Petter Selasky 	mtx_unlock(&ucom_mtx);
39302ac6454SAndrew Thompson }
39402ac6454SAndrew Thompson 
3957762e8a1SEdward Tomasz Napierala static cn_probe_t ucom_cnprobe;
3967762e8a1SEdward Tomasz Napierala static cn_init_t ucom_cninit;
3977762e8a1SEdward Tomasz Napierala static cn_term_t ucom_cnterm;
3987762e8a1SEdward Tomasz Napierala static cn_getc_t ucom_cngetc;
3997762e8a1SEdward Tomasz Napierala static cn_putc_t ucom_cnputc;
4007762e8a1SEdward Tomasz Napierala static cn_grab_t ucom_cngrab;
4017762e8a1SEdward Tomasz Napierala static cn_ungrab_t ucom_cnungrab;
4027762e8a1SEdward Tomasz Napierala 
4037762e8a1SEdward Tomasz Napierala const struct consdev_ops ucom_cnops = {
4047762e8a1SEdward Tomasz Napierala         .cn_probe       = ucom_cnprobe,
4057762e8a1SEdward Tomasz Napierala         .cn_init        = ucom_cninit,
4067762e8a1SEdward Tomasz Napierala         .cn_term        = ucom_cnterm,
4077762e8a1SEdward Tomasz Napierala         .cn_getc        = ucom_cngetc,
4087762e8a1SEdward Tomasz Napierala         .cn_putc        = ucom_cnputc,
4097762e8a1SEdward Tomasz Napierala         .cn_grab        = ucom_cngrab,
4107762e8a1SEdward Tomasz Napierala         .cn_ungrab      = ucom_cnungrab,
4117762e8a1SEdward Tomasz Napierala };
4127762e8a1SEdward Tomasz Napierala 
41302ac6454SAndrew Thompson static int
414015bb88fSNick Hibma ucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
41502ac6454SAndrew Thompson {
41602ac6454SAndrew Thompson 	struct tty *tp;
417761481dcSNick Hibma 	char buf[32];			/* temporary TTY device name buffer */
41802ac6454SAndrew Thompson 
419a593f6b8SAndrew Thompson 	tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx);
420015bb88fSNick Hibma 	if (tp == NULL)
421015bb88fSNick Hibma 		return (ENOMEM);
42202ac6454SAndrew Thompson 
42302ac6454SAndrew Thompson 	/* Check if the client has a custom TTY name */
424015bb88fSNick Hibma 	buf[0] = '\0';
425a593f6b8SAndrew Thompson 	if (sc->sc_callback->ucom_tty_name) {
426a593f6b8SAndrew Thompson 		sc->sc_callback->ucom_tty_name(sc, buf,
427015bb88fSNick Hibma 		    sizeof(buf), ssc->sc_unit, sc->sc_subunit);
42802ac6454SAndrew Thompson 	}
42902ac6454SAndrew Thompson 	if (buf[0] == 0) {
43002ac6454SAndrew Thompson 		/* Use default TTY name */
431015bb88fSNick Hibma 		if (ssc->sc_subunits > 1) {
43202ac6454SAndrew Thompson 			/* multiple modems in one */
4336416c259SNick Hibma 			snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u.%u",
434015bb88fSNick Hibma 			    ssc->sc_unit, sc->sc_subunit);
43502ac6454SAndrew Thompson 		} else {
43602ac6454SAndrew Thompson 			/* single modem */
4376416c259SNick Hibma 			snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u",
4386416c259SNick Hibma 			    ssc->sc_unit);
43902ac6454SAndrew Thompson 		}
44002ac6454SAndrew Thompson 	}
44102ac6454SAndrew Thompson 	tty_makedev(tp, NULL, "%s", buf);
44202ac6454SAndrew Thompson 
44302ac6454SAndrew Thompson 	sc->sc_tty = tp;
44402ac6454SAndrew Thompson 
445bb057644SHans Petter Selasky 	sc->sc_pps.ppscap = PPS_CAPTUREBOTH;
44628315e27SIan Lepore 	sc->sc_pps.driver_abi = PPS_ABI_VERSION;
44728315e27SIan Lepore 	sc->sc_pps.driver_mtx = sc->sc_mtx;
44828315e27SIan Lepore 	pps_init_abi(&sc->sc_pps);
449bb057644SHans Petter Selasky 
45002ac6454SAndrew Thompson 	DPRINTF("ttycreate: %s\n", buf);
45102ac6454SAndrew Thompson 
452655dc9d0SAndrew Thompson 	/* Check if this device should be a console */
453655dc9d0SAndrew Thompson 	if ((ucom_cons_softc == NULL) &&
454015bb88fSNick Hibma 	    (ssc->sc_unit == ucom_cons_unit) &&
455015bb88fSNick Hibma 	    (sc->sc_subunit == ucom_cons_subunit)) {
4563e2d8835SHans Petter Selasky 		DPRINTF("unit %d subunit %d is console",
4573e2d8835SHans Petter Selasky 		    ssc->sc_unit, sc->sc_subunit);
458015bb88fSNick Hibma 
459655dc9d0SAndrew Thompson 		ucom_cons_softc = sc;
460655dc9d0SAndrew Thompson 
4613e2d8835SHans Petter Selasky 		tty_init_console(tp, ucom_cons_baud);
462655dc9d0SAndrew Thompson 
4638f42c748SHans Petter Selasky 		UCOM_MTX_LOCK(ucom_cons_softc);
464655dc9d0SAndrew Thompson 		ucom_cons_rx_low = 0;
465655dc9d0SAndrew Thompson 		ucom_cons_rx_high = 0;
466655dc9d0SAndrew Thompson 		ucom_cons_tx_low = 0;
467655dc9d0SAndrew Thompson 		ucom_cons_tx_high = 0;
468655dc9d0SAndrew Thompson 		sc->sc_flag |= UCOM_FLAG_CONSOLE;
469655dc9d0SAndrew Thompson 		ucom_open(ucom_cons_softc->sc_tty);
4703e2d8835SHans Petter Selasky 		ucom_param(ucom_cons_softc->sc_tty, &tp->t_termios_init_in);
4718f42c748SHans Petter Selasky 		UCOM_MTX_UNLOCK(ucom_cons_softc);
472655dc9d0SAndrew Thompson 	}
473015bb88fSNick Hibma 
4747762e8a1SEdward Tomasz Napierala 	if ((ssc->sc_flag & UCOM_FLAG_DEVICE_MODE) != 0 &&
4757762e8a1SEdward Tomasz Napierala 	    ucom_device_mode_console > 0 &&
4767762e8a1SEdward Tomasz Napierala 	    ucom_cons_softc == NULL) {
4777762e8a1SEdward Tomasz Napierala 		struct consdev *cp;
4787762e8a1SEdward Tomasz Napierala 
4797762e8a1SEdward Tomasz Napierala 		cp = malloc(sizeof(struct consdev), M_USBDEV,
4807762e8a1SEdward Tomasz Napierala 		    M_WAITOK|M_ZERO);
4817762e8a1SEdward Tomasz Napierala 		cp->cn_ops = &ucom_cnops;
4827762e8a1SEdward Tomasz Napierala 		cp->cn_arg = NULL;
4837762e8a1SEdward Tomasz Napierala 		cp->cn_pri = CN_NORMAL;
4847762e8a1SEdward Tomasz Napierala 		strlcpy(cp->cn_name, "tty", sizeof(cp->cn_name));
4857762e8a1SEdward Tomasz Napierala 		strlcat(cp->cn_name, buf, sizeof(cp->cn_name));
4867762e8a1SEdward Tomasz Napierala 
4877762e8a1SEdward Tomasz Napierala 		sc->sc_consdev = cp;
4887762e8a1SEdward Tomasz Napierala 
4897762e8a1SEdward Tomasz Napierala 		cnadd(cp);
4907762e8a1SEdward Tomasz Napierala 	}
4917762e8a1SEdward Tomasz Napierala 
492015bb88fSNick Hibma 	return (0);
49302ac6454SAndrew Thompson }
49402ac6454SAndrew Thompson 
49502ac6454SAndrew Thompson static void
4968f42c748SHans Petter Selasky ucom_detach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
49702ac6454SAndrew Thompson {
49802ac6454SAndrew Thompson 	struct tty *tp = sc->sc_tty;
49902ac6454SAndrew Thompson 
50002ac6454SAndrew Thompson 	DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
50102ac6454SAndrew Thompson 
5027762e8a1SEdward Tomasz Napierala 	if (sc->sc_consdev != NULL) {
5037762e8a1SEdward Tomasz Napierala 		cnremove(sc->sc_consdev);
5047762e8a1SEdward Tomasz Napierala 		free(sc->sc_consdev, M_USBDEV);
5057762e8a1SEdward Tomasz Napierala 		sc->sc_consdev = NULL;
5067762e8a1SEdward Tomasz Napierala 	}
5077762e8a1SEdward Tomasz Napierala 
508655dc9d0SAndrew Thompson 	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
5098f42c748SHans Petter Selasky 		UCOM_MTX_LOCK(ucom_cons_softc);
510655dc9d0SAndrew Thompson 		ucom_close(ucom_cons_softc->sc_tty);
511015bb88fSNick Hibma 		sc->sc_flag &= ~UCOM_FLAG_CONSOLE;
5128f42c748SHans Petter Selasky 		UCOM_MTX_UNLOCK(ucom_cons_softc);
513655dc9d0SAndrew Thompson 		ucom_cons_softc = NULL;
514655dc9d0SAndrew Thompson 	}
515655dc9d0SAndrew Thompson 
51602ac6454SAndrew Thompson 	/* the config thread has been stopped when we get here */
51702ac6454SAndrew Thompson 
5188f42c748SHans Petter Selasky 	UCOM_MTX_LOCK(sc);
51902ac6454SAndrew Thompson 	sc->sc_flag |= UCOM_FLAG_GONE;
520655dc9d0SAndrew Thompson 	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY);
5218f42c748SHans Petter Selasky 	UCOM_MTX_UNLOCK(sc);
5228f42c748SHans Petter Selasky 
52302ac6454SAndrew Thompson 	if (tp) {
5248f42c748SHans Petter Selasky 		mtx_lock(&ucom_mtx);
5258f42c748SHans Petter Selasky 		ucom_close_refs++;
5268f42c748SHans Petter Selasky 		mtx_unlock(&ucom_mtx);
5278f42c748SHans Petter Selasky 
52802ac6454SAndrew Thompson 		tty_lock(tp);
52902ac6454SAndrew Thompson 
530a593f6b8SAndrew Thompson 		ucom_close(tp);	/* close, if any */
53102ac6454SAndrew Thompson 
53202ac6454SAndrew Thompson 		tty_rel_gone(tp);
53302ac6454SAndrew Thompson 
5348f42c748SHans Petter Selasky 		UCOM_MTX_LOCK(sc);
53502ac6454SAndrew Thompson 		/*
53602ac6454SAndrew Thompson 		 * make sure that read and write transfers are stopped
53702ac6454SAndrew Thompson 		 */
5388f42c748SHans Petter Selasky 		if (sc->sc_callback->ucom_stop_read)
539a593f6b8SAndrew Thompson 			(sc->sc_callback->ucom_stop_read) (sc);
5408f42c748SHans Petter Selasky 		if (sc->sc_callback->ucom_stop_write)
541a593f6b8SAndrew Thompson 			(sc->sc_callback->ucom_stop_write) (sc);
5428f42c748SHans Petter Selasky 		UCOM_MTX_UNLOCK(sc);
54302ac6454SAndrew Thompson 	}
54402ac6454SAndrew Thompson }
54502ac6454SAndrew Thompson 
5466416c259SNick Hibma void
5476416c259SNick Hibma ucom_set_pnpinfo_usb(struct ucom_super_softc *ssc, device_t dev)
5486416c259SNick Hibma {
5496416c259SNick Hibma 	char buf[64];
5506416c259SNick Hibma 	uint8_t iface_index;
5516416c259SNick Hibma 	struct usb_attach_arg *uaa;
5526416c259SNick Hibma 
55396d87d2bSHans Petter Selasky 	snprintf(buf, sizeof(buf), "ttyname=" UCOM_TTY_PREFIX
55496d87d2bSHans Petter Selasky 	    "%d ttyports=%d", ssc->sc_unit, ssc->sc_subunits);
5556416c259SNick Hibma 
5563a4a1a7fSHans Petter Selasky 	/* Store the PNP info in the first interface for the device */
5576416c259SNick Hibma 	uaa = device_get_ivars(dev);
5586416c259SNick Hibma 	iface_index = uaa->info.bIfaceIndex;
5596416c259SNick Hibma 
5606416c259SNick Hibma 	if (usbd_set_pnpinfo(uaa->device, iface_index, buf) != 0)
5616416c259SNick Hibma 		device_printf(dev, "Could not set PNP info\n");
5623a4a1a7fSHans Petter Selasky 
5633a4a1a7fSHans Petter Selasky 	/*
56496d87d2bSHans Petter Selasky 	 * The following information is also replicated in the PNP-info
5653a4a1a7fSHans Petter Selasky 	 * string which is registered above:
5663a4a1a7fSHans Petter Selasky 	 */
56796d87d2bSHans Petter Selasky 	if (ssc->sc_sysctl_ttyname == NULL) {
56896d87d2bSHans Petter Selasky 		ssc->sc_sysctl_ttyname = SYSCTL_ADD_STRING(NULL,
5693a4a1a7fSHans Petter Selasky 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
57096d87d2bSHans Petter Selasky 		    OID_AUTO, "ttyname", CTLFLAG_RD, ssc->sc_ttyname, 0,
57196d87d2bSHans Petter Selasky 		    "TTY device basename");
5723a4a1a7fSHans Petter Selasky 	}
5733a4a1a7fSHans Petter Selasky 	if (ssc->sc_sysctl_ttyports == NULL) {
5743a4a1a7fSHans Petter Selasky 		ssc->sc_sysctl_ttyports = SYSCTL_ADD_INT(NULL,
5753a4a1a7fSHans Petter Selasky 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
5763a4a1a7fSHans Petter Selasky 		    OID_AUTO, "ttyports", CTLFLAG_RD,
5773a4a1a7fSHans Petter Selasky 		    NULL, ssc->sc_subunits, "Number of ports");
5783a4a1a7fSHans Petter Selasky 	}
5796416c259SNick Hibma }
5806416c259SNick Hibma 
5817762e8a1SEdward Tomasz Napierala void
5827762e8a1SEdward Tomasz Napierala ucom_set_usb_mode(struct ucom_super_softc *ssc, enum usb_hc_mode usb_mode)
5837762e8a1SEdward Tomasz Napierala {
5847762e8a1SEdward Tomasz Napierala 
5857762e8a1SEdward Tomasz Napierala 	switch (usb_mode) {
5867762e8a1SEdward Tomasz Napierala 	case USB_MODE_DEVICE:
5877762e8a1SEdward Tomasz Napierala 		ssc->sc_flag |= UCOM_FLAG_DEVICE_MODE;
5887762e8a1SEdward Tomasz Napierala 		break;
5897762e8a1SEdward Tomasz Napierala 	default:
5907762e8a1SEdward Tomasz Napierala 		ssc->sc_flag &= ~UCOM_FLAG_DEVICE_MODE;
5917762e8a1SEdward Tomasz Napierala 		break;
5927762e8a1SEdward Tomasz Napierala 	}
5937762e8a1SEdward Tomasz Napierala }
5947762e8a1SEdward Tomasz Napierala 
595*36a80f42SKyle Evans static void
596*36a80f42SKyle Evans ucom_command_barrier_cb(struct usb_proc_msg *msg __unused)
597*36a80f42SKyle Evans {
598*36a80f42SKyle Evans 	/* NOP */
599*36a80f42SKyle Evans }
600*36a80f42SKyle Evans 
601*36a80f42SKyle Evans /*
602*36a80f42SKyle Evans  * ucom_command_barrier inserts a dummy task and waits for it so that we can be
603*36a80f42SKyle Evans  * certain that previously enqueued tasks are finished before returning back to
604*36a80f42SKyle Evans  * the tty layer.
605*36a80f42SKyle Evans  */
606*36a80f42SKyle Evans static int
607*36a80f42SKyle Evans ucom_command_barrier(struct ucom_softc *sc)
608*36a80f42SKyle Evans {
609*36a80f42SKyle Evans 	struct ucom_super_softc *ssc = sc->sc_super;
610*36a80f42SKyle Evans 	struct usb_proc_msg dummy = { .pm_callback = ucom_command_barrier_cb };
611*36a80f42SKyle Evans 	struct usb_proc_msg *task;
612*36a80f42SKyle Evans 	int error;
613*36a80f42SKyle Evans 
614*36a80f42SKyle Evans 	UCOM_MTX_ASSERT(sc, MA_OWNED);
615*36a80f42SKyle Evans 
616*36a80f42SKyle Evans 	if (usb_proc_is_gone(&ssc->sc_tq)) {
617*36a80f42SKyle Evans 		DPRINTF("proc is gone\n");
618*36a80f42SKyle Evans 		return (ENXIO);         /* nothing to do */
619*36a80f42SKyle Evans 	}
620*36a80f42SKyle Evans 
621*36a80f42SKyle Evans 	task = usb_proc_msignal(&ssc->sc_tq, &dummy, &dummy);
622*36a80f42SKyle Evans 	error = usb_proc_mwait_sig(&ssc->sc_tq, task, task);
623*36a80f42SKyle Evans 	if (error == 0 && sc->sc_tty != NULL && tty_gone(sc->sc_tty))
624*36a80f42SKyle Evans 		error = ENXIO;
625*36a80f42SKyle Evans 	return (error);
626*36a80f42SKyle Evans }
627*36a80f42SKyle Evans 
62851f3d087SKyle Evans static int
629a593f6b8SAndrew Thompson ucom_queue_command(struct ucom_softc *sc,
630e0a69b51SAndrew Thompson     usb_proc_callback_t *fn, struct termios *pt,
631*36a80f42SKyle Evans     struct usb_proc_msg *t0, struct usb_proc_msg *t1, bool wait)
63202ac6454SAndrew Thompson {
633760bc48eSAndrew Thompson 	struct ucom_super_softc *ssc = sc->sc_super;
634760bc48eSAndrew Thompson 	struct ucom_param_task *task;
635729eb176SKyle Evans 	int error;
63602ac6454SAndrew Thompson 
6378f42c748SHans Petter Selasky 	UCOM_MTX_ASSERT(sc, MA_OWNED);
63802ac6454SAndrew Thompson 
639a593f6b8SAndrew Thompson 	if (usb_proc_is_gone(&ssc->sc_tq)) {
64002ac6454SAndrew Thompson 		DPRINTF("proc is gone\n");
64151f3d087SKyle Evans 		return (ENXIO);         /* nothing to do */
64202ac6454SAndrew Thompson 	}
64302ac6454SAndrew Thompson 	/*
64402ac6454SAndrew Thompson 	 * NOTE: The task cannot get executed before we drop the
64502ac6454SAndrew Thompson 	 * "sc_mtx" mutex. It is safe to update fields in the message
64602ac6454SAndrew Thompson 	 * structure after that the message got queued.
64702ac6454SAndrew Thompson 	 */
648760bc48eSAndrew Thompson 	task = (struct ucom_param_task *)
649a593f6b8SAndrew Thompson 	  usb_proc_msignal(&ssc->sc_tq, t0, t1);
65002ac6454SAndrew Thompson 
65102ac6454SAndrew Thompson 	/* Setup callback and softc pointers */
65202ac6454SAndrew Thompson 	task->hdr.pm_callback = fn;
65302ac6454SAndrew Thompson 	task->sc = sc;
65402ac6454SAndrew Thompson 
65502ac6454SAndrew Thompson 	/*
65602ac6454SAndrew Thompson 	 * Make a copy of the termios. This field is only present if
65702ac6454SAndrew Thompson 	 * the "pt" field is not NULL.
65802ac6454SAndrew Thompson 	 */
65902ac6454SAndrew Thompson 	if (pt != NULL)
66002ac6454SAndrew Thompson 		task->termios_copy = *pt;
66102ac6454SAndrew Thompson 
66202ac6454SAndrew Thompson 	/*
663cbc53503SDave Baukus 	 * Closing or opening the device should be synchronous.
66402ac6454SAndrew Thompson 	 */
665*36a80f42SKyle Evans 	if (wait) {
666729eb176SKyle Evans 		error = usb_proc_mwait_sig(&ssc->sc_tq, t0, t1);
667729eb176SKyle Evans 
668729eb176SKyle Evans 		/* usb_proc_mwait_sig may have dropped the tty lock. */
669729eb176SKyle Evans 		if (error == 0 && sc->sc_tty != NULL && tty_gone(sc->sc_tty))
670729eb176SKyle Evans 			error = ENXIO;
671729eb176SKyle Evans 	} else {
672729eb176SKyle Evans 		error = 0;
673729eb176SKyle Evans 	}
67402ac6454SAndrew Thompson 
675afa07055SAndrew Thompson 	/*
676afa07055SAndrew Thompson 	 * In case of multiple configure requests,
677afa07055SAndrew Thompson 	 * keep track of the last one!
678afa07055SAndrew Thompson 	 */
679a593f6b8SAndrew Thompson 	if (fn == ucom_cfg_start_transfers)
680afa07055SAndrew Thompson 		sc->sc_last_start_xfer = &task->hdr;
68151f3d087SKyle Evans 
682729eb176SKyle Evans 	return (error);
68302ac6454SAndrew Thompson }
68402ac6454SAndrew Thompson 
68502ac6454SAndrew Thompson static void
686a593f6b8SAndrew Thompson ucom_shutdown(struct ucom_softc *sc)
68702ac6454SAndrew Thompson {
68802ac6454SAndrew Thompson 	struct tty *tp = sc->sc_tty;
68902ac6454SAndrew Thompson 
6908f42c748SHans Petter Selasky 	UCOM_MTX_ASSERT(sc, MA_OWNED);
69102ac6454SAndrew Thompson 
69202ac6454SAndrew Thompson 	DPRINTF("\n");
69302ac6454SAndrew Thompson 
69402ac6454SAndrew Thompson 	/*
69502ac6454SAndrew Thompson 	 * Hang up if necessary:
69602ac6454SAndrew Thompson 	 */
69702ac6454SAndrew Thompson 	if (tp->t_termios.c_cflag & HUPCL) {
698a593f6b8SAndrew Thompson 		ucom_modem(tp, 0, SER_DTR);
69902ac6454SAndrew Thompson 	}
70002ac6454SAndrew Thompson }
70102ac6454SAndrew Thompson 
70202ac6454SAndrew Thompson /*
70302ac6454SAndrew Thompson  * Return values:
70402ac6454SAndrew Thompson  *    0: normal
70502ac6454SAndrew Thompson  * else: taskqueue is draining or gone
70602ac6454SAndrew Thompson  */
70702ac6454SAndrew Thompson uint8_t
708a593f6b8SAndrew Thompson ucom_cfg_is_gone(struct ucom_softc *sc)
70902ac6454SAndrew Thompson {
710760bc48eSAndrew Thompson 	struct ucom_super_softc *ssc = sc->sc_super;
71102ac6454SAndrew Thompson 
712a593f6b8SAndrew Thompson 	return (usb_proc_is_gone(&ssc->sc_tq));
71302ac6454SAndrew Thompson }
71402ac6454SAndrew Thompson 
71502ac6454SAndrew Thompson static void
716a593f6b8SAndrew Thompson ucom_cfg_start_transfers(struct usb_proc_msg *_task)
71702ac6454SAndrew Thompson {
718760bc48eSAndrew Thompson 	struct ucom_cfg_task *task =
719760bc48eSAndrew Thompson 	    (struct ucom_cfg_task *)_task;
720760bc48eSAndrew Thompson 	struct ucom_softc *sc = task->sc;
72102ac6454SAndrew Thompson 
72202ac6454SAndrew Thompson 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
72302ac6454SAndrew Thompson 		return;
72402ac6454SAndrew Thompson 	}
72502ac6454SAndrew Thompson 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
72602ac6454SAndrew Thompson 		/* TTY device closed */
72702ac6454SAndrew Thompson 		return;
72802ac6454SAndrew Thompson 	}
729afa07055SAndrew Thompson 
730afa07055SAndrew Thompson 	if (_task == sc->sc_last_start_xfer)
73102ac6454SAndrew Thompson 		sc->sc_flag |= UCOM_FLAG_GP_DATA;
73202ac6454SAndrew Thompson 
733a593f6b8SAndrew Thompson 	if (sc->sc_callback->ucom_start_read) {
734a593f6b8SAndrew Thompson 		(sc->sc_callback->ucom_start_read) (sc);
73502ac6454SAndrew Thompson 	}
736a593f6b8SAndrew Thompson 	if (sc->sc_callback->ucom_start_write) {
737a593f6b8SAndrew Thompson 		(sc->sc_callback->ucom_start_write) (sc);
73802ac6454SAndrew Thompson 	}
73902ac6454SAndrew Thompson }
74002ac6454SAndrew Thompson 
74102ac6454SAndrew Thompson static void
742a593f6b8SAndrew Thompson ucom_start_transfers(struct ucom_softc *sc)
74302ac6454SAndrew Thompson {
74402ac6454SAndrew Thompson 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
74502ac6454SAndrew Thompson 		return;
74602ac6454SAndrew Thompson 	}
74702ac6454SAndrew Thompson 	/*
74802ac6454SAndrew Thompson 	 * Make sure that data transfers are started in both
74902ac6454SAndrew Thompson 	 * directions:
75002ac6454SAndrew Thompson 	 */
751a593f6b8SAndrew Thompson 	if (sc->sc_callback->ucom_start_read) {
752a593f6b8SAndrew Thompson 		(sc->sc_callback->ucom_start_read) (sc);
75302ac6454SAndrew Thompson 	}
754a593f6b8SAndrew Thompson 	if (sc->sc_callback->ucom_start_write) {
755a593f6b8SAndrew Thompson 		(sc->sc_callback->ucom_start_write) (sc);
75602ac6454SAndrew Thompson 	}
75702ac6454SAndrew Thompson }
75802ac6454SAndrew Thompson 
75902ac6454SAndrew Thompson static void
760a593f6b8SAndrew Thompson ucom_cfg_open(struct usb_proc_msg *_task)
76102ac6454SAndrew Thompson {
762760bc48eSAndrew Thompson 	struct ucom_cfg_task *task =
763760bc48eSAndrew Thompson 	    (struct ucom_cfg_task *)_task;
764760bc48eSAndrew Thompson 	struct ucom_softc *sc = task->sc;
76502ac6454SAndrew Thompson 
76602ac6454SAndrew Thompson 	DPRINTF("\n");
76702ac6454SAndrew Thompson 
76802ac6454SAndrew Thompson 	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
76902ac6454SAndrew Thompson 		/* already opened */
77002ac6454SAndrew Thompson 
77102ac6454SAndrew Thompson 	} else {
77202ac6454SAndrew Thompson 		sc->sc_flag |= UCOM_FLAG_LL_READY;
77302ac6454SAndrew Thompson 
774a593f6b8SAndrew Thompson 		if (sc->sc_callback->ucom_cfg_open) {
775a593f6b8SAndrew Thompson 			(sc->sc_callback->ucom_cfg_open) (sc);
77602ac6454SAndrew Thompson 
77702ac6454SAndrew Thompson 			/* wait a little */
778a593f6b8SAndrew Thompson 			usb_pause_mtx(sc->sc_mtx, hz / 10);
77902ac6454SAndrew Thompson 		}
78002ac6454SAndrew Thompson 	}
78102ac6454SAndrew Thompson }
78202ac6454SAndrew Thompson 
78302ac6454SAndrew Thompson static int
784a593f6b8SAndrew Thompson ucom_open(struct tty *tp)
78502ac6454SAndrew Thompson {
786760bc48eSAndrew Thompson 	struct ucom_softc *sc = tty_softc(tp);
78702ac6454SAndrew Thompson 	int error;
78802ac6454SAndrew Thompson 
7898f42c748SHans Petter Selasky 	UCOM_MTX_ASSERT(sc, MA_OWNED);
79002ac6454SAndrew Thompson 
79102ac6454SAndrew Thompson 	if (sc->sc_flag & UCOM_FLAG_GONE) {
79202ac6454SAndrew Thompson 		return (ENXIO);
79302ac6454SAndrew Thompson 	}
79402ac6454SAndrew Thompson 	if (sc->sc_flag & UCOM_FLAG_HL_READY) {
79502ac6454SAndrew Thompson 		/* already opened */
79602ac6454SAndrew Thompson 		return (0);
79702ac6454SAndrew Thompson 	}
79802ac6454SAndrew Thompson 	DPRINTF("tp = %p\n", tp);
79902ac6454SAndrew Thompson 
800a593f6b8SAndrew Thompson 	if (sc->sc_callback->ucom_pre_open) {
80102ac6454SAndrew Thompson 		/*
80202ac6454SAndrew Thompson 		 * give the lower layer a chance to disallow TTY open, for
80302ac6454SAndrew Thompson 		 * example if the device is not present:
80402ac6454SAndrew Thompson 		 */
805a593f6b8SAndrew Thompson 		error = (sc->sc_callback->ucom_pre_open) (sc);
80651f3d087SKyle Evans 		if (error != 0)
80751f3d087SKyle Evans 			goto out;
80802ac6454SAndrew Thompson 	}
80902ac6454SAndrew Thompson 	sc->sc_flag |= UCOM_FLAG_HL_READY;
81002ac6454SAndrew Thompson 
81102ac6454SAndrew Thompson 	/* Disable transfers */
81202ac6454SAndrew Thompson 	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
81302ac6454SAndrew Thompson 
81402ac6454SAndrew Thompson 	sc->sc_lsr = 0;
81502ac6454SAndrew Thompson 	sc->sc_msr = 0;
81602ac6454SAndrew Thompson 	sc->sc_mcr = 0;
81702ac6454SAndrew Thompson 
81802ac6454SAndrew Thompson 	/* reset programmed line state */
81902ac6454SAndrew Thompson 	sc->sc_pls_curr = 0;
82002ac6454SAndrew Thompson 	sc->sc_pls_set = 0;
82102ac6454SAndrew Thompson 	sc->sc_pls_clr = 0;
82202ac6454SAndrew Thompson 
823d30d96eaSHans Petter Selasky 	/* reset jitter buffer */
824d30d96eaSHans Petter Selasky 	sc->sc_jitterbuf_in = 0;
825d30d96eaSHans Petter Selasky 	sc->sc_jitterbuf_out = 0;
826d30d96eaSHans Petter Selasky 
82751f3d087SKyle Evans 	error = ucom_queue_command(sc, ucom_cfg_open, NULL,
82802ac6454SAndrew Thompson 	    &sc->sc_open_task[0].hdr,
829*36a80f42SKyle Evans 	    &sc->sc_open_task[1].hdr, true);
83051f3d087SKyle Evans 	if (error != 0)
83151f3d087SKyle Evans 		goto out;
83202ac6454SAndrew Thompson 
833*36a80f42SKyle Evans 	/*
834*36a80f42SKyle Evans 	 * Queue transfer enable command last, we'll have a barrier later so we
835*36a80f42SKyle Evans 	 * don't need to wait on this to complete specifically.
836*36a80f42SKyle Evans 	 */
83751f3d087SKyle Evans 	error = ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
83802ac6454SAndrew Thompson 	    &sc->sc_start_task[0].hdr,
839*36a80f42SKyle Evans 	    &sc->sc_start_task[1].hdr, true);
84051f3d087SKyle Evans 	if (error != 0)
84151f3d087SKyle Evans 		goto out;
84202ac6454SAndrew Thompson 
843705aad98SStephen Hurd 	if (sc->sc_tty == NULL || (sc->sc_tty->t_termios.c_cflag & CNO_RTSDTR) == 0)
844a593f6b8SAndrew Thompson 		ucom_modem(tp, SER_DTR | SER_RTS, 0);
84502ac6454SAndrew Thompson 
846655dc9d0SAndrew Thompson 	ucom_ring(sc, 0);
847655dc9d0SAndrew Thompson 
848a593f6b8SAndrew Thompson 	ucom_break(sc, 0);
84902ac6454SAndrew Thompson 
850a593f6b8SAndrew Thompson 	ucom_status_change(sc);
85102ac6454SAndrew Thompson 
852*36a80f42SKyle Evans 	error = ucom_command_barrier(sc);
85351f3d087SKyle Evans out:
85451f3d087SKyle Evans 	return (error);
85502ac6454SAndrew Thompson }
85602ac6454SAndrew Thompson 
85702ac6454SAndrew Thompson static void
858a593f6b8SAndrew Thompson ucom_cfg_close(struct usb_proc_msg *_task)
85902ac6454SAndrew Thompson {
860760bc48eSAndrew Thompson 	struct ucom_cfg_task *task =
861760bc48eSAndrew Thompson 	    (struct ucom_cfg_task *)_task;
862760bc48eSAndrew Thompson 	struct ucom_softc *sc = task->sc;
86302ac6454SAndrew Thompson 
86402ac6454SAndrew Thompson 	DPRINTF("\n");
86502ac6454SAndrew Thompson 
86602ac6454SAndrew Thompson 	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
8672b85e080SAndrew Thompson 		sc->sc_flag &= ~UCOM_FLAG_LL_READY;
868a593f6b8SAndrew Thompson 		if (sc->sc_callback->ucom_cfg_close)
869a593f6b8SAndrew Thompson 			(sc->sc_callback->ucom_cfg_close) (sc);
87002ac6454SAndrew Thompson 	} else {
87102ac6454SAndrew Thompson 		/* already closed */
87202ac6454SAndrew Thompson 	}
87302ac6454SAndrew Thompson }
87402ac6454SAndrew Thompson 
87502ac6454SAndrew Thompson static void
876a593f6b8SAndrew Thompson ucom_close(struct tty *tp)
87702ac6454SAndrew Thompson {
878760bc48eSAndrew Thompson 	struct ucom_softc *sc = tty_softc(tp);
87902ac6454SAndrew Thompson 
8808f42c748SHans Petter Selasky 	UCOM_MTX_ASSERT(sc, MA_OWNED);
88102ac6454SAndrew Thompson 
88202ac6454SAndrew Thompson 	DPRINTF("tp=%p\n", tp);
88302ac6454SAndrew Thompson 
88402ac6454SAndrew Thompson 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
88502ac6454SAndrew Thompson 		DPRINTF("tp=%p already closed\n", tp);
88602ac6454SAndrew Thompson 		return;
88702ac6454SAndrew Thompson 	}
888a593f6b8SAndrew Thompson 	ucom_shutdown(sc);
88902ac6454SAndrew Thompson 
89051f3d087SKyle Evans 	(void)ucom_queue_command(sc, ucom_cfg_close, NULL,
89102ac6454SAndrew Thompson 	    &sc->sc_close_task[0].hdr,
892*36a80f42SKyle Evans 	    &sc->sc_close_task[1].hdr, true);
89302ac6454SAndrew Thompson 
8942b85e080SAndrew Thompson 	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
89502ac6454SAndrew Thompson 
896a593f6b8SAndrew Thompson 	if (sc->sc_callback->ucom_stop_read) {
897a593f6b8SAndrew Thompson 		(sc->sc_callback->ucom_stop_read) (sc);
89802ac6454SAndrew Thompson 	}
89902ac6454SAndrew Thompson }
90002ac6454SAndrew Thompson 
901d30d96eaSHans Petter Selasky static void
902d30d96eaSHans Petter Selasky ucom_inwakeup(struct tty *tp)
903d30d96eaSHans Petter Selasky {
904d30d96eaSHans Petter Selasky 	struct ucom_softc *sc = tty_softc(tp);
905d30d96eaSHans Petter Selasky 	uint16_t pos;
906d30d96eaSHans Petter Selasky 
907d30d96eaSHans Petter Selasky 	if (sc == NULL)
908d30d96eaSHans Petter Selasky 		return;
909d30d96eaSHans Petter Selasky 
9109a630052SHans Petter Selasky 	UCOM_MTX_ASSERT(sc, MA_OWNED);
9113b8e9843SHans Petter Selasky 
9129a630052SHans Petter Selasky 	DPRINTF("tp=%p\n", tp);
913d30d96eaSHans Petter Selasky 
914d30d96eaSHans Petter Selasky 	if (ttydisc_can_bypass(tp) != 0 ||
9153d6709a5SHans Petter Selasky 	    (sc->sc_flag & UCOM_FLAG_HL_READY) == 0 ||
9163d6709a5SHans Petter Selasky 	    (sc->sc_flag & UCOM_FLAG_INWAKEUP) != 0) {
917d30d96eaSHans Petter Selasky 		return;
918d30d96eaSHans Petter Selasky 	}
919d30d96eaSHans Petter Selasky 
9203d6709a5SHans Petter Selasky 	/* prevent recursion */
9213d6709a5SHans Petter Selasky 	sc->sc_flag |= UCOM_FLAG_INWAKEUP;
9223d6709a5SHans Petter Selasky 
923d30d96eaSHans Petter Selasky 	pos = sc->sc_jitterbuf_out;
924d30d96eaSHans Petter Selasky 
925d30d96eaSHans Petter Selasky 	while (sc->sc_jitterbuf_in != pos) {
926d30d96eaSHans Petter Selasky 		int c;
927d30d96eaSHans Petter Selasky 
928d30d96eaSHans Petter Selasky 		c = (char)sc->sc_jitterbuf[pos];
929d30d96eaSHans Petter Selasky 
930d30d96eaSHans Petter Selasky 		if (ttydisc_rint(tp, c, 0) == -1)
931d30d96eaSHans Petter Selasky 			break;
932d30d96eaSHans Petter Selasky 		pos++;
933d30d96eaSHans Petter Selasky 		if (pos >= UCOM_JITTERBUF_SIZE)
934d30d96eaSHans Petter Selasky 			pos -= UCOM_JITTERBUF_SIZE;
935d30d96eaSHans Petter Selasky 	}
936d30d96eaSHans Petter Selasky 
937d30d96eaSHans Petter Selasky 	sc->sc_jitterbuf_out = pos;
938d30d96eaSHans Petter Selasky 
939d30d96eaSHans Petter Selasky 	/* clear RTS in async fashion */
940d30d96eaSHans Petter Selasky 	if ((sc->sc_jitterbuf_in == pos) &&
941d30d96eaSHans Petter Selasky 	    (sc->sc_flag & UCOM_FLAG_RTS_IFLOW))
942d30d96eaSHans Petter Selasky 		ucom_rts(sc, 0);
9433d6709a5SHans Petter Selasky 
9443d6709a5SHans Petter Selasky 	sc->sc_flag &= ~UCOM_FLAG_INWAKEUP;
945d30d96eaSHans Petter Selasky }
946d30d96eaSHans Petter Selasky 
94702ac6454SAndrew Thompson static int
948a593f6b8SAndrew Thompson ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
94902ac6454SAndrew Thompson {
950760bc48eSAndrew Thompson 	struct ucom_softc *sc = tty_softc(tp);
95102ac6454SAndrew Thompson 	int error;
95202ac6454SAndrew Thompson 
9538f42c748SHans Petter Selasky 	UCOM_MTX_ASSERT(sc, MA_OWNED);
95402ac6454SAndrew Thompson 
95502ac6454SAndrew Thompson 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
95602ac6454SAndrew Thompson 		return (EIO);
95702ac6454SAndrew Thompson 	}
95802ac6454SAndrew Thompson 	DPRINTF("cmd = 0x%08lx\n", cmd);
95902ac6454SAndrew Thompson 
96002ac6454SAndrew Thompson 	switch (cmd) {
961655dc9d0SAndrew Thompson #if 0
962655dc9d0SAndrew Thompson 	case TIOCSRING:
963655dc9d0SAndrew Thompson 		ucom_ring(sc, 1);
964655dc9d0SAndrew Thompson 		error = 0;
965655dc9d0SAndrew Thompson 		break;
966655dc9d0SAndrew Thompson 	case TIOCCRING:
967655dc9d0SAndrew Thompson 		ucom_ring(sc, 0);
968655dc9d0SAndrew Thompson 		error = 0;
969655dc9d0SAndrew Thompson 		break;
970655dc9d0SAndrew Thompson #endif
97102ac6454SAndrew Thompson 	case TIOCSBRK:
972a593f6b8SAndrew Thompson 		ucom_break(sc, 1);
973*36a80f42SKyle Evans 		error = ucom_command_barrier(sc);
974*36a80f42SKyle Evans 		if (error == ENXIO)
975*36a80f42SKyle Evans 			error = ENODEV;
97602ac6454SAndrew Thompson 		break;
97702ac6454SAndrew Thompson 	case TIOCCBRK:
978a593f6b8SAndrew Thompson 		ucom_break(sc, 0);
979*36a80f42SKyle Evans 		error = ucom_command_barrier(sc);
980*36a80f42SKyle Evans 		if (error == ENXIO)
981*36a80f42SKyle Evans 			error = ENODEV;
98202ac6454SAndrew Thompson 		break;
98302ac6454SAndrew Thompson 	default:
984a593f6b8SAndrew Thompson 		if (sc->sc_callback->ucom_ioctl) {
985a593f6b8SAndrew Thompson 			error = (sc->sc_callback->ucom_ioctl)
98602ac6454SAndrew Thompson 			    (sc, cmd, data, 0, td);
98702ac6454SAndrew Thompson 		} else {
98802ac6454SAndrew Thompson 			error = ENOIOCTL;
98902ac6454SAndrew Thompson 		}
990bb057644SHans Petter Selasky 		if (error == ENOIOCTL)
991bb057644SHans Petter Selasky 			error = pps_ioctl(cmd, data, &sc->sc_pps);
99202ac6454SAndrew Thompson 		break;
99302ac6454SAndrew Thompson 	}
99402ac6454SAndrew Thompson 	return (error);
99502ac6454SAndrew Thompson }
99602ac6454SAndrew Thompson 
99702ac6454SAndrew Thompson static int
998a593f6b8SAndrew Thompson ucom_modem(struct tty *tp, int sigon, int sigoff)
99902ac6454SAndrew Thompson {
1000760bc48eSAndrew Thompson 	struct ucom_softc *sc = tty_softc(tp);
100102ac6454SAndrew Thompson 	uint8_t onoff;
100202ac6454SAndrew Thompson 
10038f42c748SHans Petter Selasky 	UCOM_MTX_ASSERT(sc, MA_OWNED);
100402ac6454SAndrew Thompson 
100502ac6454SAndrew Thompson 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
100602ac6454SAndrew Thompson 		return (0);
100702ac6454SAndrew Thompson 	}
100802ac6454SAndrew Thompson 	if ((sigon == 0) && (sigoff == 0)) {
100902ac6454SAndrew Thompson 		if (sc->sc_mcr & SER_DTR) {
101002ac6454SAndrew Thompson 			sigon |= SER_DTR;
101102ac6454SAndrew Thompson 		}
101202ac6454SAndrew Thompson 		if (sc->sc_mcr & SER_RTS) {
101302ac6454SAndrew Thompson 			sigon |= SER_RTS;
101402ac6454SAndrew Thompson 		}
101502ac6454SAndrew Thompson 		if (sc->sc_msr & SER_CTS) {
101602ac6454SAndrew Thompson 			sigon |= SER_CTS;
101702ac6454SAndrew Thompson 		}
101802ac6454SAndrew Thompson 		if (sc->sc_msr & SER_DCD) {
101902ac6454SAndrew Thompson 			sigon |= SER_DCD;
102002ac6454SAndrew Thompson 		}
102102ac6454SAndrew Thompson 		if (sc->sc_msr & SER_DSR) {
102202ac6454SAndrew Thompson 			sigon |= SER_DSR;
102302ac6454SAndrew Thompson 		}
102402ac6454SAndrew Thompson 		if (sc->sc_msr & SER_RI) {
102502ac6454SAndrew Thompson 			sigon |= SER_RI;
102602ac6454SAndrew Thompson 		}
102702ac6454SAndrew Thompson 		return (sigon);
102802ac6454SAndrew Thompson 	}
102902ac6454SAndrew Thompson 	if (sigon & SER_DTR) {
103002ac6454SAndrew Thompson 		sc->sc_mcr |= SER_DTR;
103102ac6454SAndrew Thompson 	}
103202ac6454SAndrew Thompson 	if (sigoff & SER_DTR) {
103302ac6454SAndrew Thompson 		sc->sc_mcr &= ~SER_DTR;
103402ac6454SAndrew Thompson 	}
103502ac6454SAndrew Thompson 	if (sigon & SER_RTS) {
103602ac6454SAndrew Thompson 		sc->sc_mcr |= SER_RTS;
103702ac6454SAndrew Thompson 	}
103802ac6454SAndrew Thompson 	if (sigoff & SER_RTS) {
103902ac6454SAndrew Thompson 		sc->sc_mcr &= ~SER_RTS;
104002ac6454SAndrew Thompson 	}
104102ac6454SAndrew Thompson 	onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
1042a593f6b8SAndrew Thompson 	ucom_dtr(sc, onoff);
104302ac6454SAndrew Thompson 
104402ac6454SAndrew Thompson 	onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
1045a593f6b8SAndrew Thompson 	ucom_rts(sc, onoff);
104602ac6454SAndrew Thompson 
104702ac6454SAndrew Thompson 	return (0);
104802ac6454SAndrew Thompson }
104902ac6454SAndrew Thompson 
105002ac6454SAndrew Thompson static void
1051a593f6b8SAndrew Thompson ucom_cfg_line_state(struct usb_proc_msg *_task)
105202ac6454SAndrew Thompson {
1053760bc48eSAndrew Thompson 	struct ucom_cfg_task *task =
1054760bc48eSAndrew Thompson 	    (struct ucom_cfg_task *)_task;
1055760bc48eSAndrew Thompson 	struct ucom_softc *sc = task->sc;
105602ac6454SAndrew Thompson 	uint8_t notch_bits;
105702ac6454SAndrew Thompson 	uint8_t any_bits;
105802ac6454SAndrew Thompson 	uint8_t prev_value;
105902ac6454SAndrew Thompson 	uint8_t last_value;
106002ac6454SAndrew Thompson 	uint8_t mask;
106102ac6454SAndrew Thompson 
106202ac6454SAndrew Thompson 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
106302ac6454SAndrew Thompson 		return;
106402ac6454SAndrew Thompson 	}
106502ac6454SAndrew Thompson 
106602ac6454SAndrew Thompson 	mask = 0;
106702ac6454SAndrew Thompson 	/* compute callback mask */
1068a593f6b8SAndrew Thompson 	if (sc->sc_callback->ucom_cfg_set_dtr)
106902ac6454SAndrew Thompson 		mask |= UCOM_LS_DTR;
1070a593f6b8SAndrew Thompson 	if (sc->sc_callback->ucom_cfg_set_rts)
107102ac6454SAndrew Thompson 		mask |= UCOM_LS_RTS;
1072a593f6b8SAndrew Thompson 	if (sc->sc_callback->ucom_cfg_set_break)
107302ac6454SAndrew Thompson 		mask |= UCOM_LS_BREAK;
1074655dc9d0SAndrew Thompson 	if (sc->sc_callback->ucom_cfg_set_ring)
1075655dc9d0SAndrew Thompson 		mask |= UCOM_LS_RING;
107602ac6454SAndrew Thompson 
107702ac6454SAndrew Thompson 	/* compute the bits we are to program */
107802ac6454SAndrew Thompson 	notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
107902ac6454SAndrew Thompson 	any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
108002ac6454SAndrew Thompson 	prev_value = sc->sc_pls_curr ^ notch_bits;
108102ac6454SAndrew Thompson 	last_value = sc->sc_pls_curr;
108202ac6454SAndrew Thompson 
108302ac6454SAndrew Thompson 	/* reset programmed line state */
108402ac6454SAndrew Thompson 	sc->sc_pls_curr = 0;
108502ac6454SAndrew Thompson 	sc->sc_pls_set = 0;
108602ac6454SAndrew Thompson 	sc->sc_pls_clr = 0;
108702ac6454SAndrew Thompson 
1088a164074fSEitan Adler 	/* ensure that we don't lose any levels */
108902ac6454SAndrew Thompson 	if (notch_bits & UCOM_LS_DTR)
1090a593f6b8SAndrew Thompson 		sc->sc_callback->ucom_cfg_set_dtr(sc,
109102ac6454SAndrew Thompson 		    (prev_value & UCOM_LS_DTR) ? 1 : 0);
109202ac6454SAndrew Thompson 	if (notch_bits & UCOM_LS_RTS)
1093a593f6b8SAndrew Thompson 		sc->sc_callback->ucom_cfg_set_rts(sc,
109402ac6454SAndrew Thompson 		    (prev_value & UCOM_LS_RTS) ? 1 : 0);
109502ac6454SAndrew Thompson 	if (notch_bits & UCOM_LS_BREAK)
1096a593f6b8SAndrew Thompson 		sc->sc_callback->ucom_cfg_set_break(sc,
109702ac6454SAndrew Thompson 		    (prev_value & UCOM_LS_BREAK) ? 1 : 0);
1098655dc9d0SAndrew Thompson 	if (notch_bits & UCOM_LS_RING)
1099655dc9d0SAndrew Thompson 		sc->sc_callback->ucom_cfg_set_ring(sc,
1100655dc9d0SAndrew Thompson 		    (prev_value & UCOM_LS_RING) ? 1 : 0);
110102ac6454SAndrew Thompson 
110202ac6454SAndrew Thompson 	/* set last value */
110302ac6454SAndrew Thompson 	if (any_bits & UCOM_LS_DTR)
1104a593f6b8SAndrew Thompson 		sc->sc_callback->ucom_cfg_set_dtr(sc,
110502ac6454SAndrew Thompson 		    (last_value & UCOM_LS_DTR) ? 1 : 0);
110602ac6454SAndrew Thompson 	if (any_bits & UCOM_LS_RTS)
1107a593f6b8SAndrew Thompson 		sc->sc_callback->ucom_cfg_set_rts(sc,
110802ac6454SAndrew Thompson 		    (last_value & UCOM_LS_RTS) ? 1 : 0);
110902ac6454SAndrew Thompson 	if (any_bits & UCOM_LS_BREAK)
1110a593f6b8SAndrew Thompson 		sc->sc_callback->ucom_cfg_set_break(sc,
111102ac6454SAndrew Thompson 		    (last_value & UCOM_LS_BREAK) ? 1 : 0);
1112655dc9d0SAndrew Thompson 	if (any_bits & UCOM_LS_RING)
1113655dc9d0SAndrew Thompson 		sc->sc_callback->ucom_cfg_set_ring(sc,
1114655dc9d0SAndrew Thompson 		    (last_value & UCOM_LS_RING) ? 1 : 0);
111502ac6454SAndrew Thompson }
111602ac6454SAndrew Thompson 
111702ac6454SAndrew Thompson static void
1118a593f6b8SAndrew Thompson ucom_line_state(struct ucom_softc *sc,
111902ac6454SAndrew Thompson     uint8_t set_bits, uint8_t clear_bits)
112002ac6454SAndrew Thompson {
11218f42c748SHans Petter Selasky 	UCOM_MTX_ASSERT(sc, MA_OWNED);
112202ac6454SAndrew Thompson 
112302ac6454SAndrew Thompson 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
112402ac6454SAndrew Thompson 		return;
112502ac6454SAndrew Thompson 	}
112602ac6454SAndrew Thompson 
112702ac6454SAndrew Thompson 	DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
112802ac6454SAndrew Thompson 
112902ac6454SAndrew Thompson 	/* update current programmed line state */
113002ac6454SAndrew Thompson 	sc->sc_pls_curr |= set_bits;
113102ac6454SAndrew Thompson 	sc->sc_pls_curr &= ~clear_bits;
113202ac6454SAndrew Thompson 	sc->sc_pls_set |= set_bits;
113302ac6454SAndrew Thompson 	sc->sc_pls_clr |= clear_bits;
113402ac6454SAndrew Thompson 
113551f3d087SKyle Evans 	/*
113651f3d087SKyle Evans 	 * defer driver programming - we don't propagate any error from
113751f3d087SKyle Evans 	 * this call because we'll catch such errors further up the call stack.
113851f3d087SKyle Evans 	 */
113951f3d087SKyle Evans 	(void)ucom_queue_command(sc, ucom_cfg_line_state, NULL,
114002ac6454SAndrew Thompson 	    &sc->sc_line_state_task[0].hdr,
1141*36a80f42SKyle Evans 	    &sc->sc_line_state_task[1].hdr, false);
114202ac6454SAndrew Thompson }
114302ac6454SAndrew Thompson 
114402ac6454SAndrew Thompson static void
1145655dc9d0SAndrew Thompson ucom_ring(struct ucom_softc *sc, uint8_t onoff)
1146655dc9d0SAndrew Thompson {
1147655dc9d0SAndrew Thompson 	DPRINTF("onoff = %d\n", onoff);
1148655dc9d0SAndrew Thompson 
1149655dc9d0SAndrew Thompson 	if (onoff)
1150655dc9d0SAndrew Thompson 		ucom_line_state(sc, UCOM_LS_RING, 0);
1151655dc9d0SAndrew Thompson 	else
1152655dc9d0SAndrew Thompson 		ucom_line_state(sc, 0, UCOM_LS_RING);
1153655dc9d0SAndrew Thompson }
1154655dc9d0SAndrew Thompson 
1155655dc9d0SAndrew Thompson static void
1156a593f6b8SAndrew Thompson ucom_break(struct ucom_softc *sc, uint8_t onoff)
115702ac6454SAndrew Thompson {
115802ac6454SAndrew Thompson 	DPRINTF("onoff = %d\n", onoff);
115902ac6454SAndrew Thompson 
116002ac6454SAndrew Thompson 	if (onoff)
1161a593f6b8SAndrew Thompson 		ucom_line_state(sc, UCOM_LS_BREAK, 0);
116202ac6454SAndrew Thompson 	else
1163a593f6b8SAndrew Thompson 		ucom_line_state(sc, 0, UCOM_LS_BREAK);
116402ac6454SAndrew Thompson }
116502ac6454SAndrew Thompson 
116602ac6454SAndrew Thompson static void
1167a593f6b8SAndrew Thompson ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
116802ac6454SAndrew Thompson {
116902ac6454SAndrew Thompson 	DPRINTF("onoff = %d\n", onoff);
117002ac6454SAndrew Thompson 
117102ac6454SAndrew Thompson 	if (onoff)
1172a593f6b8SAndrew Thompson 		ucom_line_state(sc, UCOM_LS_DTR, 0);
117302ac6454SAndrew Thompson 	else
1174a593f6b8SAndrew Thompson 		ucom_line_state(sc, 0, UCOM_LS_DTR);
117502ac6454SAndrew Thompson }
117602ac6454SAndrew Thompson 
117702ac6454SAndrew Thompson static void
1178a593f6b8SAndrew Thompson ucom_rts(struct ucom_softc *sc, uint8_t onoff)
117902ac6454SAndrew Thompson {
118002ac6454SAndrew Thompson 	DPRINTF("onoff = %d\n", onoff);
118102ac6454SAndrew Thompson 
118202ac6454SAndrew Thompson 	if (onoff)
1183a593f6b8SAndrew Thompson 		ucom_line_state(sc, UCOM_LS_RTS, 0);
118402ac6454SAndrew Thompson 	else
1185a593f6b8SAndrew Thompson 		ucom_line_state(sc, 0, UCOM_LS_RTS);
118602ac6454SAndrew Thompson }
118702ac6454SAndrew Thompson 
118802ac6454SAndrew Thompson static void
1189a593f6b8SAndrew Thompson ucom_cfg_status_change(struct usb_proc_msg *_task)
119002ac6454SAndrew Thompson {
1191760bc48eSAndrew Thompson 	struct ucom_cfg_task *task =
1192760bc48eSAndrew Thompson 	    (struct ucom_cfg_task *)_task;
1193760bc48eSAndrew Thompson 	struct ucom_softc *sc = task->sc;
119402ac6454SAndrew Thompson 	struct tty *tp;
11955508dc2aSIan Lepore 	int onoff;
119602ac6454SAndrew Thompson 	uint8_t new_msr;
119702ac6454SAndrew Thompson 	uint8_t new_lsr;
1198bb057644SHans Petter Selasky 	uint8_t msr_delta;
11997ee713e8SHans Petter Selasky 	uint8_t lsr_delta;
12005508dc2aSIan Lepore 	uint8_t pps_signal;
120102ac6454SAndrew Thompson 
120202ac6454SAndrew Thompson 	tp = sc->sc_tty;
120302ac6454SAndrew Thompson 
12048f42c748SHans Petter Selasky 	UCOM_MTX_ASSERT(sc, MA_OWNED);
120502ac6454SAndrew Thompson 
120602ac6454SAndrew Thompson 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
120702ac6454SAndrew Thompson 		return;
120802ac6454SAndrew Thompson 	}
1209a593f6b8SAndrew Thompson 	if (sc->sc_callback->ucom_cfg_get_status == NULL) {
121002ac6454SAndrew Thompson 		return;
121102ac6454SAndrew Thompson 	}
121202ac6454SAndrew Thompson 	/* get status */
121302ac6454SAndrew Thompson 
121402ac6454SAndrew Thompson 	new_msr = 0;
121502ac6454SAndrew Thompson 	new_lsr = 0;
121602ac6454SAndrew Thompson 
1217a593f6b8SAndrew Thompson 	(sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
121802ac6454SAndrew Thompson 
121902ac6454SAndrew Thompson 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
122002ac6454SAndrew Thompson 		/* TTY device closed */
122102ac6454SAndrew Thompson 		return;
122202ac6454SAndrew Thompson 	}
1223bb057644SHans Petter Selasky 	msr_delta = (sc->sc_msr ^ new_msr);
12247ee713e8SHans Petter Selasky 	lsr_delta = (sc->sc_lsr ^ new_lsr);
122502ac6454SAndrew Thompson 
122602ac6454SAndrew Thompson 	sc->sc_msr = new_msr;
122702ac6454SAndrew Thompson 	sc->sc_lsr = new_lsr;
122802ac6454SAndrew Thompson 
1229efccd9f0SHans Petter Selasky 	/*
12305508dc2aSIan Lepore 	 * Time pulse counting support.
1231efccd9f0SHans Petter Selasky 	 */
12325508dc2aSIan Lepore 	switch(ucom_pps_mode & UART_PPS_SIGNAL_MASK) {
12335508dc2aSIan Lepore 	case UART_PPS_CTS:
12345508dc2aSIan Lepore 		pps_signal = SER_CTS;
1235bb057644SHans Petter Selasky 		break;
12365508dc2aSIan Lepore 	case UART_PPS_DCD:
12375508dc2aSIan Lepore 		pps_signal = SER_DCD;
1238bb057644SHans Petter Selasky 		break;
1239bb057644SHans Petter Selasky 	default:
12405508dc2aSIan Lepore 		pps_signal = 0;
1241bb057644SHans Petter Selasky 		break;
1242bb057644SHans Petter Selasky 	}
124302ac6454SAndrew Thompson 
12445508dc2aSIan Lepore 	if ((sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) &&
12455508dc2aSIan Lepore 	    (msr_delta & pps_signal)) {
12465508dc2aSIan Lepore 		pps_capture(&sc->sc_pps);
12475508dc2aSIan Lepore 		onoff = (sc->sc_msr & pps_signal) ? 1 : 0;
12485508dc2aSIan Lepore 		if (ucom_pps_mode & UART_PPS_INVERT_PULSE)
12495508dc2aSIan Lepore 			onoff = !onoff;
12505508dc2aSIan Lepore 		pps_event(&sc->sc_pps, onoff ? PPS_CAPTUREASSERT :
12515508dc2aSIan Lepore 		    PPS_CAPTURECLEAR);
12525508dc2aSIan Lepore 	}
12535508dc2aSIan Lepore 
1254bb057644SHans Petter Selasky 	if (msr_delta & SER_DCD) {
12555508dc2aSIan Lepore 		onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
125602ac6454SAndrew Thompson 
125702ac6454SAndrew Thompson 		DPRINTF("DCD changed to %d\n", onoff);
125802ac6454SAndrew Thompson 
125902ac6454SAndrew Thompson 		ttydisc_modem(tp, onoff);
126002ac6454SAndrew Thompson 	}
12617ee713e8SHans Petter Selasky 
12627ee713e8SHans Petter Selasky 	if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
12637ee713e8SHans Petter Selasky 		DPRINTF("BREAK detected\n");
12647ee713e8SHans Petter Selasky 
12657ee713e8SHans Petter Selasky 		ttydisc_rint(tp, 0, TRE_BREAK);
12667ee713e8SHans Petter Selasky 		ttydisc_rint_done(tp);
12677ee713e8SHans Petter Selasky 	}
12687ee713e8SHans Petter Selasky 
12697ee713e8SHans Petter Selasky 	if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) {
12707ee713e8SHans Petter Selasky 		DPRINTF("Frame error detected\n");
12717ee713e8SHans Petter Selasky 
12727ee713e8SHans Petter Selasky 		ttydisc_rint(tp, 0, TRE_FRAMING);
12737ee713e8SHans Petter Selasky 		ttydisc_rint_done(tp);
12747ee713e8SHans Petter Selasky 	}
12757ee713e8SHans Petter Selasky 
12767ee713e8SHans Petter Selasky 	if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) {
12777ee713e8SHans Petter Selasky 		DPRINTF("Parity error detected\n");
12787ee713e8SHans Petter Selasky 
12797ee713e8SHans Petter Selasky 		ttydisc_rint(tp, 0, TRE_PARITY);
12807ee713e8SHans Petter Selasky 		ttydisc_rint_done(tp);
12817ee713e8SHans Petter Selasky 	}
128202ac6454SAndrew Thompson }
128302ac6454SAndrew Thompson 
128402ac6454SAndrew Thompson void
1285a593f6b8SAndrew Thompson ucom_status_change(struct ucom_softc *sc)
128602ac6454SAndrew Thompson {
12878f42c748SHans Petter Selasky 	UCOM_MTX_ASSERT(sc, MA_OWNED);
128802ac6454SAndrew Thompson 
1289655dc9d0SAndrew Thompson 	if (sc->sc_flag & UCOM_FLAG_CONSOLE)
1290655dc9d0SAndrew Thompson 		return;		/* not supported */
1291655dc9d0SAndrew Thompson 
129202ac6454SAndrew Thompson 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
129302ac6454SAndrew Thompson 		return;
129402ac6454SAndrew Thompson 	}
129502ac6454SAndrew Thompson 	DPRINTF("\n");
129602ac6454SAndrew Thompson 
129751f3d087SKyle Evans 	(void)ucom_queue_command(sc, ucom_cfg_status_change, NULL,
129802ac6454SAndrew Thompson 	    &sc->sc_status_task[0].hdr,
1299*36a80f42SKyle Evans 	    &sc->sc_status_task[1].hdr, true);
130002ac6454SAndrew Thompson }
130102ac6454SAndrew Thompson 
130202ac6454SAndrew Thompson static void
1303a593f6b8SAndrew Thompson ucom_cfg_param(struct usb_proc_msg *_task)
130402ac6454SAndrew Thompson {
1305760bc48eSAndrew Thompson 	struct ucom_param_task *task =
1306760bc48eSAndrew Thompson 	    (struct ucom_param_task *)_task;
1307760bc48eSAndrew Thompson 	struct ucom_softc *sc = task->sc;
130802ac6454SAndrew Thompson 
130902ac6454SAndrew Thompson 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
131002ac6454SAndrew Thompson 		return;
131102ac6454SAndrew Thompson 	}
1312a593f6b8SAndrew Thompson 	if (sc->sc_callback->ucom_cfg_param == NULL) {
131302ac6454SAndrew Thompson 		return;
131402ac6454SAndrew Thompson 	}
131502ac6454SAndrew Thompson 
1316a593f6b8SAndrew Thompson 	(sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
131702ac6454SAndrew Thompson 
131802ac6454SAndrew Thompson 	/* wait a little */
1319a593f6b8SAndrew Thompson 	usb_pause_mtx(sc->sc_mtx, hz / 10);
132002ac6454SAndrew Thompson }
132102ac6454SAndrew Thompson 
132202ac6454SAndrew Thompson static int
1323a593f6b8SAndrew Thompson ucom_param(struct tty *tp, struct termios *t)
132402ac6454SAndrew Thompson {
1325760bc48eSAndrew Thompson 	struct ucom_softc *sc = tty_softc(tp);
132602ac6454SAndrew Thompson 	uint8_t opened;
132702ac6454SAndrew Thompson 	int error;
132802ac6454SAndrew Thompson 
13298f42c748SHans Petter Selasky 	UCOM_MTX_ASSERT(sc, MA_OWNED);
133002ac6454SAndrew Thompson 
133102ac6454SAndrew Thompson 	opened = 0;
133202ac6454SAndrew Thompson 	error = 0;
133302ac6454SAndrew Thompson 
133402ac6454SAndrew Thompson 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
133502ac6454SAndrew Thompson 		/* XXX the TTY layer should call "open()" first! */
13363e2d8835SHans Petter Selasky 		/*
13373e2d8835SHans Petter Selasky 		 * Not quite: Its ordering is partly backwards, but
13383e2d8835SHans Petter Selasky 		 * some parameters must be set early in ttydev_open(),
13393e2d8835SHans Petter Selasky 		 * possibly before calling ttydevsw_open().
13403e2d8835SHans Petter Selasky 		 */
1341a593f6b8SAndrew Thompson 		error = ucom_open(tp);
13423e2d8835SHans Petter Selasky 		if (error)
134302ac6454SAndrew Thompson 			goto done;
13443e2d8835SHans Petter Selasky 
134502ac6454SAndrew Thompson 		opened = 1;
134602ac6454SAndrew Thompson 	}
134702ac6454SAndrew Thompson 	DPRINTF("sc = %p\n", sc);
134802ac6454SAndrew Thompson 
134902ac6454SAndrew Thompson 	/* Check requested parameters. */
135002ac6454SAndrew Thompson 	if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
13513e2d8835SHans Petter Selasky 		/* XXX c_ospeed == 0 is perfectly valid. */
135202ac6454SAndrew Thompson 		DPRINTF("mismatch ispeed and ospeed\n");
135302ac6454SAndrew Thompson 		error = EINVAL;
135402ac6454SAndrew Thompson 		goto done;
135502ac6454SAndrew Thompson 	}
135602ac6454SAndrew Thompson 	t->c_ispeed = t->c_ospeed;
135702ac6454SAndrew Thompson 
1358a593f6b8SAndrew Thompson 	if (sc->sc_callback->ucom_pre_param) {
135902ac6454SAndrew Thompson 		/* Let the lower layer verify the parameters */
1360a593f6b8SAndrew Thompson 		error = (sc->sc_callback->ucom_pre_param) (sc, t);
136102ac6454SAndrew Thompson 		if (error) {
136202ac6454SAndrew Thompson 			DPRINTF("callback error = %d\n", error);
136302ac6454SAndrew Thompson 			goto done;
136402ac6454SAndrew Thompson 		}
136502ac6454SAndrew Thompson 	}
136602ac6454SAndrew Thompson 
136702ac6454SAndrew Thompson 	/* Disable transfers */
136802ac6454SAndrew Thompson 	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
136902ac6454SAndrew Thompson 
137002ac6454SAndrew Thompson 	/* Queue baud rate programming command first */
137151f3d087SKyle Evans 	error = ucom_queue_command(sc, ucom_cfg_param, t,
137202ac6454SAndrew Thompson 	    &sc->sc_param_task[0].hdr,
1373*36a80f42SKyle Evans 	    &sc->sc_param_task[1].hdr, true);
137451f3d087SKyle Evans 	if (error != 0)
137551f3d087SKyle Evans 		goto done;
137602ac6454SAndrew Thompson 
137702ac6454SAndrew Thompson 	/* Queue transfer enable command last */
137851f3d087SKyle Evans 	error = ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
137902ac6454SAndrew Thompson 	    &sc->sc_start_task[0].hdr,
1380*36a80f42SKyle Evans 	    &sc->sc_start_task[1].hdr, true);
138151f3d087SKyle Evans 	if (error != 0)
138251f3d087SKyle Evans 		goto done;
138302ac6454SAndrew Thompson 
138402ac6454SAndrew Thompson 	if (t->c_cflag & CRTS_IFLOW) {
138502ac6454SAndrew Thompson 		sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
138602ac6454SAndrew Thompson 	} else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
138702ac6454SAndrew Thompson 		sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
1388a593f6b8SAndrew Thompson 		ucom_modem(tp, SER_RTS, 0);
138902ac6454SAndrew Thompson 	}
1390*36a80f42SKyle Evans 
1391*36a80f42SKyle Evans 	error = ucom_command_barrier(sc);
139202ac6454SAndrew Thompson done:
139302ac6454SAndrew Thompson 	if (error) {
139402ac6454SAndrew Thompson 		if (opened) {
1395a593f6b8SAndrew Thompson 			ucom_close(tp);
139602ac6454SAndrew Thompson 		}
139702ac6454SAndrew Thompson 	}
139802ac6454SAndrew Thompson 	return (error);
139902ac6454SAndrew Thompson }
140002ac6454SAndrew Thompson 
140102ac6454SAndrew Thompson static void
1402a593f6b8SAndrew Thompson ucom_outwakeup(struct tty *tp)
140302ac6454SAndrew Thompson {
1404760bc48eSAndrew Thompson 	struct ucom_softc *sc = tty_softc(tp);
140502ac6454SAndrew Thompson 
14068f42c748SHans Petter Selasky 	UCOM_MTX_ASSERT(sc, MA_OWNED);
140702ac6454SAndrew Thompson 
140802ac6454SAndrew Thompson 	DPRINTF("sc = %p\n", sc);
140902ac6454SAndrew Thompson 
141002ac6454SAndrew Thompson 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
141102ac6454SAndrew Thompson 		/* The higher layer is not ready */
141202ac6454SAndrew Thompson 		return;
141302ac6454SAndrew Thompson 	}
1414a593f6b8SAndrew Thompson 	ucom_start_transfers(sc);
141502ac6454SAndrew Thompson }
141602ac6454SAndrew Thompson 
14179ad221a5SIan Lepore static bool
14189ad221a5SIan Lepore ucom_busy(struct tty *tp)
14199ad221a5SIan Lepore {
14209ad221a5SIan Lepore 	struct ucom_softc *sc = tty_softc(tp);
14219ad221a5SIan Lepore 	const uint8_t txidle = ULSR_TXRDY | ULSR_TSRE;
14229ad221a5SIan Lepore 
14239ad221a5SIan Lepore 	UCOM_MTX_ASSERT(sc, MA_OWNED);
14249ad221a5SIan Lepore 
14259ad221a5SIan Lepore 	DPRINTFN(3, "sc = %p lsr 0x%02x\n", sc, sc->sc_lsr);
14269ad221a5SIan Lepore 
14279ad221a5SIan Lepore 	/*
14289ad221a5SIan Lepore 	 * If the driver maintains the txidle bits in LSR, we can use them to
14299ad221a5SIan Lepore 	 * determine whether the transmitter is busy or idle.  Otherwise we have
14309ad221a5SIan Lepore 	 * to assume it is idle to avoid hanging forever on tcdrain(3).
14319ad221a5SIan Lepore 	 */
14329ad221a5SIan Lepore 	if (sc->sc_flag & UCOM_FLAG_LSRTXIDLE)
14339ad221a5SIan Lepore 		return ((sc->sc_lsr & txidle) != txidle);
14349ad221a5SIan Lepore 	else
14359ad221a5SIan Lepore 		return (false);
14369ad221a5SIan Lepore }
14379ad221a5SIan Lepore 
143802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1439a593f6b8SAndrew Thompson  *	ucom_get_data
144002ac6454SAndrew Thompson  *
144102ac6454SAndrew Thompson  * Return values:
144202ac6454SAndrew Thompson  * 0: No data is available.
144302ac6454SAndrew Thompson  * Else: Data is available.
144402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
144502ac6454SAndrew Thompson uint8_t
1446a593f6b8SAndrew Thompson ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
144702ac6454SAndrew Thompson     uint32_t offset, uint32_t len, uint32_t *actlen)
144802ac6454SAndrew Thompson {
1449760bc48eSAndrew Thompson 	struct usb_page_search res;
145002ac6454SAndrew Thompson 	struct tty *tp = sc->sc_tty;
145102ac6454SAndrew Thompson 	uint32_t cnt;
145202ac6454SAndrew Thompson 	uint32_t offset_orig;
145302ac6454SAndrew Thompson 
14548f42c748SHans Petter Selasky 	UCOM_MTX_ASSERT(sc, MA_OWNED);
145502ac6454SAndrew Thompson 
1456655dc9d0SAndrew Thompson 	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
145762d42655SHans Petter Selasky 		unsigned temp;
1458655dc9d0SAndrew Thompson 
1459655dc9d0SAndrew Thompson 		/* get total TX length */
1460655dc9d0SAndrew Thompson 
1461655dc9d0SAndrew Thompson 		temp = ucom_cons_tx_high - ucom_cons_tx_low;
1462655dc9d0SAndrew Thompson 		temp %= UCOM_CONS_BUFSIZE;
1463655dc9d0SAndrew Thompson 
1464655dc9d0SAndrew Thompson 		/* limit TX length */
1465655dc9d0SAndrew Thompson 
1466655dc9d0SAndrew Thompson 		if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low))
1467655dc9d0SAndrew Thompson 			temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
1468655dc9d0SAndrew Thompson 
1469655dc9d0SAndrew Thompson 		if (temp > len)
1470655dc9d0SAndrew Thompson 			temp = len;
1471655dc9d0SAndrew Thompson 
1472655dc9d0SAndrew Thompson 		/* copy in data */
1473655dc9d0SAndrew Thompson 
1474655dc9d0SAndrew Thompson 		usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
1475655dc9d0SAndrew Thompson 
1476655dc9d0SAndrew Thompson 		/* update counters */
1477655dc9d0SAndrew Thompson 
1478655dc9d0SAndrew Thompson 		ucom_cons_tx_low += temp;
1479655dc9d0SAndrew Thompson 		ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
1480655dc9d0SAndrew Thompson 
1481655dc9d0SAndrew Thompson 		/* store actual length */
1482655dc9d0SAndrew Thompson 
1483655dc9d0SAndrew Thompson 		*actlen = temp;
1484655dc9d0SAndrew Thompson 
1485655dc9d0SAndrew Thompson 		return (temp ? 1 : 0);
1486655dc9d0SAndrew Thompson 	}
1487655dc9d0SAndrew Thompson 
14882b85e080SAndrew Thompson 	if (tty_gone(tp) ||
14892b85e080SAndrew Thompson 	    !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
149002ac6454SAndrew Thompson 		actlen[0] = 0;
149102ac6454SAndrew Thompson 		return (0);		/* multiport device polling */
149202ac6454SAndrew Thompson 	}
149302ac6454SAndrew Thompson 	offset_orig = offset;
149402ac6454SAndrew Thompson 
149502ac6454SAndrew Thompson 	while (len != 0) {
1496a593f6b8SAndrew Thompson 		usbd_get_page(pc, offset, &res);
149702ac6454SAndrew Thompson 
149802ac6454SAndrew Thompson 		if (res.length > len) {
149902ac6454SAndrew Thompson 			res.length = len;
150002ac6454SAndrew Thompson 		}
150102ac6454SAndrew Thompson 		/* copy data directly into USB buffer */
150202ac6454SAndrew Thompson 		cnt = ttydisc_getc(tp, res.buffer, res.length);
150302ac6454SAndrew Thompson 
150402ac6454SAndrew Thompson 		offset += cnt;
150502ac6454SAndrew Thompson 		len -= cnt;
150602ac6454SAndrew Thompson 
150702ac6454SAndrew Thompson 		if (cnt < res.length) {
150802ac6454SAndrew Thompson 			/* end of buffer */
150902ac6454SAndrew Thompson 			break;
151002ac6454SAndrew Thompson 		}
151102ac6454SAndrew Thompson 	}
151202ac6454SAndrew Thompson 
151302ac6454SAndrew Thompson 	actlen[0] = offset - offset_orig;
151402ac6454SAndrew Thompson 
151502ac6454SAndrew Thompson 	DPRINTF("cnt=%d\n", actlen[0]);
151602ac6454SAndrew Thompson 
151702ac6454SAndrew Thompson 	if (actlen[0] == 0) {
151802ac6454SAndrew Thompson 		return (0);
151902ac6454SAndrew Thompson 	}
152002ac6454SAndrew Thompson 	return (1);
152102ac6454SAndrew Thompson }
152202ac6454SAndrew Thompson 
152302ac6454SAndrew Thompson void
1524a593f6b8SAndrew Thompson ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
152502ac6454SAndrew Thompson     uint32_t offset, uint32_t len)
152602ac6454SAndrew Thompson {
1527760bc48eSAndrew Thompson 	struct usb_page_search res;
152802ac6454SAndrew Thompson 	struct tty *tp = sc->sc_tty;
152902ac6454SAndrew Thompson 	char *buf;
153002ac6454SAndrew Thompson 	uint32_t cnt;
153102ac6454SAndrew Thompson 
15328f42c748SHans Petter Selasky 	UCOM_MTX_ASSERT(sc, MA_OWNED);
153302ac6454SAndrew Thompson 
1534655dc9d0SAndrew Thompson 	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
153562d42655SHans Petter Selasky 		unsigned temp;
1536655dc9d0SAndrew Thompson 
1537655dc9d0SAndrew Thompson 		/* get maximum RX length */
1538655dc9d0SAndrew Thompson 
1539655dc9d0SAndrew Thompson 		temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
1540655dc9d0SAndrew Thompson 		temp %= UCOM_CONS_BUFSIZE;
1541655dc9d0SAndrew Thompson 
1542655dc9d0SAndrew Thompson 		/* limit RX length */
1543655dc9d0SAndrew Thompson 
1544655dc9d0SAndrew Thompson 		if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high))
1545655dc9d0SAndrew Thompson 			temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
1546655dc9d0SAndrew Thompson 
1547655dc9d0SAndrew Thompson 		if (temp > len)
1548655dc9d0SAndrew Thompson 			temp = len;
1549655dc9d0SAndrew Thompson 
1550655dc9d0SAndrew Thompson 		/* copy out data */
1551655dc9d0SAndrew Thompson 
1552655dc9d0SAndrew Thompson 		usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
1553655dc9d0SAndrew Thompson 
1554655dc9d0SAndrew Thompson 		/* update counters */
1555655dc9d0SAndrew Thompson 
1556655dc9d0SAndrew Thompson 		ucom_cons_rx_high += temp;
1557655dc9d0SAndrew Thompson 		ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
1558655dc9d0SAndrew Thompson 
1559655dc9d0SAndrew Thompson 		return;
1560655dc9d0SAndrew Thompson 	}
1561655dc9d0SAndrew Thompson 
15622b85e080SAndrew Thompson 	if (tty_gone(tp))
156302ac6454SAndrew Thompson 		return;			/* multiport device polling */
15642b85e080SAndrew Thompson 
156502ac6454SAndrew Thompson 	if (len == 0)
156602ac6454SAndrew Thompson 		return;			/* no data */
156702ac6454SAndrew Thompson 
156802ac6454SAndrew Thompson 	/* set a flag to prevent recursation ? */
156902ac6454SAndrew Thompson 
157002ac6454SAndrew Thompson 	while (len > 0) {
1571a593f6b8SAndrew Thompson 		usbd_get_page(pc, offset, &res);
157202ac6454SAndrew Thompson 
157302ac6454SAndrew Thompson 		if (res.length > len) {
157402ac6454SAndrew Thompson 			res.length = len;
157502ac6454SAndrew Thompson 		}
157602ac6454SAndrew Thompson 		len -= res.length;
157702ac6454SAndrew Thompson 		offset += res.length;
157802ac6454SAndrew Thompson 
157902ac6454SAndrew Thompson 		/* pass characters to tty layer */
158002ac6454SAndrew Thompson 
158102ac6454SAndrew Thompson 		buf = res.buffer;
158202ac6454SAndrew Thompson 		cnt = res.length;
158302ac6454SAndrew Thompson 
158402ac6454SAndrew Thompson 		/* first check if we can pass the buffer directly */
158502ac6454SAndrew Thompson 
158602ac6454SAndrew Thompson 		if (ttydisc_can_bypass(tp)) {
1587d30d96eaSHans Petter Selasky 			/* clear any jitter buffer */
1588d30d96eaSHans Petter Selasky 			sc->sc_jitterbuf_in = 0;
1589d30d96eaSHans Petter Selasky 			sc->sc_jitterbuf_out = 0;
1590d30d96eaSHans Petter Selasky 
159102ac6454SAndrew Thompson 			if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
159202ac6454SAndrew Thompson 				DPRINTF("tp=%p, data lost\n", tp);
159302ac6454SAndrew Thompson 			}
159402ac6454SAndrew Thompson 			continue;
159502ac6454SAndrew Thompson 		}
159602ac6454SAndrew Thompson 		/* need to loop */
159702ac6454SAndrew Thompson 
159802ac6454SAndrew Thompson 		for (cnt = 0; cnt != res.length; cnt++) {
1599d30d96eaSHans Petter Selasky 			if (sc->sc_jitterbuf_in != sc->sc_jitterbuf_out ||
1600d30d96eaSHans Petter Selasky 			    ttydisc_rint(tp, buf[cnt], 0) == -1) {
1601d30d96eaSHans Petter Selasky 				uint16_t end;
1602d30d96eaSHans Petter Selasky 				uint16_t pos;
1603d30d96eaSHans Petter Selasky 
1604d30d96eaSHans Petter Selasky 				pos = sc->sc_jitterbuf_in;
1605d30d96eaSHans Petter Selasky 				end = sc->sc_jitterbuf_out +
1606d30d96eaSHans Petter Selasky 				    UCOM_JITTERBUF_SIZE - 1;
1607d30d96eaSHans Petter Selasky 				if (end >= UCOM_JITTERBUF_SIZE)
1608d30d96eaSHans Petter Selasky 					end -= UCOM_JITTERBUF_SIZE;
1609d30d96eaSHans Petter Selasky 
1610d30d96eaSHans Petter Selasky 				for (; cnt != res.length; cnt++) {
1611d30d96eaSHans Petter Selasky 					if (pos == end)
1612d30d96eaSHans Petter Selasky 						break;
1613d30d96eaSHans Petter Selasky 					sc->sc_jitterbuf[pos] = buf[cnt];
1614d30d96eaSHans Petter Selasky 					pos++;
1615d30d96eaSHans Petter Selasky 					if (pos >= UCOM_JITTERBUF_SIZE)
1616d30d96eaSHans Petter Selasky 						pos -= UCOM_JITTERBUF_SIZE;
1617d30d96eaSHans Petter Selasky 				}
1618d30d96eaSHans Petter Selasky 
1619d30d96eaSHans Petter Selasky 				sc->sc_jitterbuf_in = pos;
1620d30d96eaSHans Petter Selasky 
1621d30d96eaSHans Petter Selasky 				/* set RTS in async fashion */
1622d30d96eaSHans Petter Selasky 				if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW)
1623d30d96eaSHans Petter Selasky 					ucom_rts(sc, 1);
162402ac6454SAndrew Thompson 
162502ac6454SAndrew Thompson 				DPRINTF("tp=%p, lost %d "
162602ac6454SAndrew Thompson 				    "chars\n", tp, res.length - cnt);
162702ac6454SAndrew Thompson 				break;
162802ac6454SAndrew Thompson 			}
162902ac6454SAndrew Thompson 		}
163002ac6454SAndrew Thompson 	}
163102ac6454SAndrew Thompson 	ttydisc_rint_done(tp);
163202ac6454SAndrew Thompson }
163302ac6454SAndrew Thompson 
163402ac6454SAndrew Thompson static void
1635a593f6b8SAndrew Thompson ucom_free(void *xsc)
163602ac6454SAndrew Thompson {
1637760bc48eSAndrew Thompson 	struct ucom_softc *sc = xsc;
163802ac6454SAndrew Thompson 
16398f42c748SHans Petter Selasky 	if (sc->sc_callback->ucom_free != NULL)
16408f42c748SHans Petter Selasky 		sc->sc_callback->ucom_free(sc);
16418f42c748SHans Petter Selasky 	else
16428f42c748SHans Petter Selasky 		ucom_unref(sc->sc_super);
16438f42c748SHans Petter Selasky 
16448f42c748SHans Petter Selasky 	mtx_lock(&ucom_mtx);
16458f42c748SHans Petter Selasky 	ucom_close_refs--;
16468f42c748SHans Petter Selasky 	mtx_unlock(&ucom_mtx);
164702ac6454SAndrew Thompson }
1648655dc9d0SAndrew Thompson 
1649655dc9d0SAndrew Thompson CONSOLE_DRIVER(ucom);
1650655dc9d0SAndrew Thompson 
1651655dc9d0SAndrew Thompson static void
1652655dc9d0SAndrew Thompson ucom_cnprobe(struct consdev  *cp)
1653655dc9d0SAndrew Thompson {
1654acf4080fSAndrew Thompson 	if (ucom_cons_unit != -1)
1655655dc9d0SAndrew Thompson 		cp->cn_pri = CN_NORMAL;
1656acf4080fSAndrew Thompson 	else
1657acf4080fSAndrew Thompson 		cp->cn_pri = CN_DEAD;
1658acf4080fSAndrew Thompson 
1659acf4080fSAndrew Thompson 	strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name));
1660655dc9d0SAndrew Thompson }
1661655dc9d0SAndrew Thompson 
1662655dc9d0SAndrew Thompson static void
1663655dc9d0SAndrew Thompson ucom_cninit(struct consdev  *cp)
1664655dc9d0SAndrew Thompson {
1665655dc9d0SAndrew Thompson }
1666655dc9d0SAndrew Thompson 
1667655dc9d0SAndrew Thompson static void
1668655dc9d0SAndrew Thompson ucom_cnterm(struct consdev  *cp)
1669655dc9d0SAndrew Thompson {
1670655dc9d0SAndrew Thompson }
1671655dc9d0SAndrew Thompson 
16729976156fSAndriy Gapon static void
16739976156fSAndriy Gapon ucom_cngrab(struct consdev *cp)
16749976156fSAndriy Gapon {
16759976156fSAndriy Gapon }
16769976156fSAndriy Gapon 
16779976156fSAndriy Gapon static void
16789976156fSAndriy Gapon ucom_cnungrab(struct consdev *cp)
16799976156fSAndriy Gapon {
16809976156fSAndriy Gapon }
16819976156fSAndriy Gapon 
1682655dc9d0SAndrew Thompson static int
1683655dc9d0SAndrew Thompson ucom_cngetc(struct consdev *cd)
1684655dc9d0SAndrew Thompson {
1685655dc9d0SAndrew Thompson 	struct ucom_softc *sc = ucom_cons_softc;
1686655dc9d0SAndrew Thompson 	int c;
1687655dc9d0SAndrew Thompson 
1688655dc9d0SAndrew Thompson 	if (sc == NULL)
1689655dc9d0SAndrew Thompson 		return (-1);
1690655dc9d0SAndrew Thompson 
16918f42c748SHans Petter Selasky 	UCOM_MTX_LOCK(sc);
1692655dc9d0SAndrew Thompson 
1693655dc9d0SAndrew Thompson 	if (ucom_cons_rx_low != ucom_cons_rx_high) {
1694655dc9d0SAndrew Thompson 		c = ucom_cons_rx_buf[ucom_cons_rx_low];
1695655dc9d0SAndrew Thompson 		ucom_cons_rx_low ++;
1696655dc9d0SAndrew Thompson 		ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
1697655dc9d0SAndrew Thompson 	} else {
1698655dc9d0SAndrew Thompson 		c = -1;
1699655dc9d0SAndrew Thompson 	}
1700655dc9d0SAndrew Thompson 
1701655dc9d0SAndrew Thompson 	/* start USB transfers */
1702655dc9d0SAndrew Thompson 	ucom_outwakeup(sc->sc_tty);
1703655dc9d0SAndrew Thompson 
17048f42c748SHans Petter Selasky 	UCOM_MTX_UNLOCK(sc);
1705655dc9d0SAndrew Thompson 
1706655dc9d0SAndrew Thompson 	/* poll if necessary */
17070eb8d462SHans Petter Selasky 	if (USB_IN_POLLING_MODE_FUNC() && sc->sc_callback->ucom_poll)
1708655dc9d0SAndrew Thompson 		(sc->sc_callback->ucom_poll) (sc);
1709655dc9d0SAndrew Thompson 
1710655dc9d0SAndrew Thompson 	return (c);
1711655dc9d0SAndrew Thompson }
1712655dc9d0SAndrew Thompson 
1713655dc9d0SAndrew Thompson static void
1714655dc9d0SAndrew Thompson ucom_cnputc(struct consdev *cd, int c)
1715655dc9d0SAndrew Thompson {
1716655dc9d0SAndrew Thompson 	struct ucom_softc *sc = ucom_cons_softc;
171762d42655SHans Petter Selasky 	unsigned temp;
1718655dc9d0SAndrew Thompson 
1719655dc9d0SAndrew Thompson 	if (sc == NULL)
1720655dc9d0SAndrew Thompson 		return;
1721655dc9d0SAndrew Thompson 
1722655dc9d0SAndrew Thompson  repeat:
1723655dc9d0SAndrew Thompson 
17248f42c748SHans Petter Selasky 	UCOM_MTX_LOCK(sc);
1725655dc9d0SAndrew Thompson 
1726655dc9d0SAndrew Thompson 	/* compute maximum TX length */
1727655dc9d0SAndrew Thompson 
1728655dc9d0SAndrew Thompson 	temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
1729655dc9d0SAndrew Thompson 	temp %= UCOM_CONS_BUFSIZE;
1730655dc9d0SAndrew Thompson 
1731655dc9d0SAndrew Thompson 	if (temp) {
1732655dc9d0SAndrew Thompson 		ucom_cons_tx_buf[ucom_cons_tx_high] = c;
1733655dc9d0SAndrew Thompson 		ucom_cons_tx_high ++;
1734655dc9d0SAndrew Thompson 		ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
1735655dc9d0SAndrew Thompson 	}
1736655dc9d0SAndrew Thompson 
1737655dc9d0SAndrew Thompson 	/* start USB transfers */
1738655dc9d0SAndrew Thompson 	ucom_outwakeup(sc->sc_tty);
1739655dc9d0SAndrew Thompson 
17408f42c748SHans Petter Selasky 	UCOM_MTX_UNLOCK(sc);
1741655dc9d0SAndrew Thompson 
1742655dc9d0SAndrew Thompson 	/* poll if necessary */
17430eb8d462SHans Petter Selasky 	if (USB_IN_POLLING_MODE_FUNC() && sc->sc_callback->ucom_poll) {
1744655dc9d0SAndrew Thompson 		(sc->sc_callback->ucom_poll) (sc);
1745655dc9d0SAndrew Thompson 		/* simple flow control */
1746655dc9d0SAndrew Thompson 		if (temp == 0)
1747655dc9d0SAndrew Thompson 			goto repeat;
1748655dc9d0SAndrew Thompson 	}
1749655dc9d0SAndrew Thompson }
1750655dc9d0SAndrew Thompson 
17518f42c748SHans Petter Selasky /*------------------------------------------------------------------------*
17528f42c748SHans Petter Selasky  *	ucom_ref
17538f42c748SHans Petter Selasky  *
17548f42c748SHans Petter Selasky  * This function will increment the super UCOM reference count.
17558f42c748SHans Petter Selasky  *------------------------------------------------------------------------*/
17568f42c748SHans Petter Selasky void
17578f42c748SHans Petter Selasky ucom_ref(struct ucom_super_softc *ssc)
17588f42c748SHans Petter Selasky {
17598f42c748SHans Petter Selasky 	mtx_lock(&ucom_mtx);
17608f42c748SHans Petter Selasky 	ssc->sc_refs++;
17618f42c748SHans Petter Selasky 	mtx_unlock(&ucom_mtx);
17628f42c748SHans Petter Selasky }
17638f42c748SHans Petter Selasky 
17648f42c748SHans Petter Selasky /*------------------------------------------------------------------------*
1765c01fc06eSHans Petter Selasky  *	ucom_free_unit
1766c01fc06eSHans Petter Selasky  *
1767c01fc06eSHans Petter Selasky  * This function will free the super UCOM's allocated unit
1768c01fc06eSHans Petter Selasky  * number. This function can be called on a zero-initialized
1769c01fc06eSHans Petter Selasky  * structure. This function can be called multiple times.
1770c01fc06eSHans Petter Selasky  *------------------------------------------------------------------------*/
1771c01fc06eSHans Petter Selasky static void
1772c01fc06eSHans Petter Selasky ucom_free_unit(struct ucom_super_softc *ssc)
1773c01fc06eSHans Petter Selasky {
1774c01fc06eSHans Petter Selasky 	if (!(ssc->sc_flag & UCOM_FLAG_FREE_UNIT))
1775c01fc06eSHans Petter Selasky 		return;
1776c01fc06eSHans Petter Selasky 
1777c01fc06eSHans Petter Selasky 	ucom_unit_free(ssc->sc_unit);
1778c01fc06eSHans Petter Selasky 
1779c01fc06eSHans Petter Selasky 	ssc->sc_flag &= ~UCOM_FLAG_FREE_UNIT;
1780c01fc06eSHans Petter Selasky }
1781c01fc06eSHans Petter Selasky 
1782c01fc06eSHans Petter Selasky /*------------------------------------------------------------------------*
17838f42c748SHans Petter Selasky  *	ucom_unref
17848f42c748SHans Petter Selasky  *
17858f42c748SHans Petter Selasky  * This function will decrement the super UCOM reference count.
17868f42c748SHans Petter Selasky  *
17878f42c748SHans Petter Selasky  * Return values:
17888f42c748SHans Petter Selasky  * 0: UCOM structures are still referenced.
17898f42c748SHans Petter Selasky  * Else: UCOM structures are no longer referenced.
17908f42c748SHans Petter Selasky  *------------------------------------------------------------------------*/
17918f42c748SHans Petter Selasky int
17928f42c748SHans Petter Selasky ucom_unref(struct ucom_super_softc *ssc)
17938f42c748SHans Petter Selasky {
17948f42c748SHans Petter Selasky 	int retval;
17958f42c748SHans Petter Selasky 
17968f42c748SHans Petter Selasky 	mtx_lock(&ucom_mtx);
17978f42c748SHans Petter Selasky 	retval = (ssc->sc_refs < 2);
17988f42c748SHans Petter Selasky 	ssc->sc_refs--;
17998f42c748SHans Petter Selasky 	mtx_unlock(&ucom_mtx);
18008f42c748SHans Petter Selasky 
1801c01fc06eSHans Petter Selasky 	if (retval)
1802c01fc06eSHans Petter Selasky 		ucom_free_unit(ssc);
1803c01fc06eSHans Petter Selasky 
18048f42c748SHans Petter Selasky 	return (retval);
18058f42c748SHans Petter Selasky }
18068f42c748SHans Petter Selasky 
1807655dc9d0SAndrew Thompson #if defined(GDB)
1808655dc9d0SAndrew Thompson 
1809655dc9d0SAndrew Thompson #include <gdb/gdb.h>
1810655dc9d0SAndrew Thompson 
1811655dc9d0SAndrew Thompson static gdb_probe_f ucom_gdbprobe;
1812655dc9d0SAndrew Thompson static gdb_init_f ucom_gdbinit;
1813655dc9d0SAndrew Thompson static gdb_term_f ucom_gdbterm;
1814655dc9d0SAndrew Thompson static gdb_getc_f ucom_gdbgetc;
1815655dc9d0SAndrew Thompson static gdb_putc_f ucom_gdbputc;
1816655dc9d0SAndrew Thompson 
1817e43d081fSMitchell Horne GDB_DBGPORT(ucom, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc);
1818655dc9d0SAndrew Thompson 
1819655dc9d0SAndrew Thompson static int
1820655dc9d0SAndrew Thompson ucom_gdbprobe(void)
1821655dc9d0SAndrew Thompson {
1822655dc9d0SAndrew Thompson 	return ((ucom_cons_softc != NULL) ? 0 : -1);
1823655dc9d0SAndrew Thompson }
1824655dc9d0SAndrew Thompson 
1825655dc9d0SAndrew Thompson static void
1826655dc9d0SAndrew Thompson ucom_gdbinit(void)
1827655dc9d0SAndrew Thompson {
1828655dc9d0SAndrew Thompson }
1829655dc9d0SAndrew Thompson 
1830655dc9d0SAndrew Thompson static void
1831655dc9d0SAndrew Thompson ucom_gdbterm(void)
1832655dc9d0SAndrew Thompson {
1833655dc9d0SAndrew Thompson }
1834655dc9d0SAndrew Thompson 
1835655dc9d0SAndrew Thompson static void
1836655dc9d0SAndrew Thompson ucom_gdbputc(int c)
1837655dc9d0SAndrew Thompson {
1838655dc9d0SAndrew Thompson         ucom_cnputc(NULL, c);
1839655dc9d0SAndrew Thompson }
1840655dc9d0SAndrew Thompson 
1841655dc9d0SAndrew Thompson static int
1842655dc9d0SAndrew Thompson ucom_gdbgetc(void)
1843655dc9d0SAndrew Thompson {
1844655dc9d0SAndrew Thompson         return (ucom_cngetc(NULL));
1845655dc9d0SAndrew Thompson }
1846655dc9d0SAndrew Thompson 
1847655dc9d0SAndrew Thompson #endif
1848