xref: /dragonfly/sys/bus/u4b/serial/usb_serial.c (revision 2b3f93ea)
112bd3c8bSSascha Wildner /*	$NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $	*/
212bd3c8bSSascha Wildner 
312bd3c8bSSascha Wildner /*-
412bd3c8bSSascha Wildner  * Copyright (c) 2001-2003, 2005, 2008
512bd3c8bSSascha Wildner  *	Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
612bd3c8bSSascha Wildner  * All rights reserved.
712bd3c8bSSascha Wildner  *
812bd3c8bSSascha Wildner  * Redistribution and use in source and binary forms, with or without
912bd3c8bSSascha Wildner  * modification, are permitted provided that the following conditions
1012bd3c8bSSascha Wildner  * are met:
1112bd3c8bSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
1212bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer.
1312bd3c8bSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
1412bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
1512bd3c8bSSascha Wildner  *    documentation and/or other materials provided with the distribution.
1612bd3c8bSSascha Wildner  *
1712bd3c8bSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1812bd3c8bSSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1912bd3c8bSSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2012bd3c8bSSascha Wildner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2112bd3c8bSSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2212bd3c8bSSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2312bd3c8bSSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2412bd3c8bSSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2512bd3c8bSSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2612bd3c8bSSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2712bd3c8bSSascha Wildner  * SUCH DAMAGE.
2812bd3c8bSSascha Wildner  */
2912bd3c8bSSascha Wildner 
3012bd3c8bSSascha Wildner /*-
3112bd3c8bSSascha Wildner  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
3212bd3c8bSSascha Wildner  * All rights reserved.
3312bd3c8bSSascha Wildner  *
3412bd3c8bSSascha Wildner  * This code is derived from software contributed to The NetBSD Foundation
3512bd3c8bSSascha Wildner  * by Lennart Augustsson (lennart@augustsson.net) at
3612bd3c8bSSascha Wildner  * Carlstedt Research & Technology.
3712bd3c8bSSascha Wildner  *
3812bd3c8bSSascha Wildner  * Redistribution and use in source and binary forms, with or without
3912bd3c8bSSascha Wildner  * modification, are permitted provided that the following conditions
4012bd3c8bSSascha Wildner  * are met:
4112bd3c8bSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
4212bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer.
4312bd3c8bSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
4412bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
4512bd3c8bSSascha Wildner  *    documentation and/or other materials provided with the distribution.
4612bd3c8bSSascha Wildner  * 3. All advertising materials mentioning features or use of this software
4712bd3c8bSSascha Wildner  *    must display the following acknowledgement:
4812bd3c8bSSascha Wildner  *        This product includes software developed by the NetBSD
4912bd3c8bSSascha Wildner  *        Foundation, Inc. and its contributors.
5012bd3c8bSSascha Wildner  * 4. Neither the name of The NetBSD Foundation nor the names of its
5112bd3c8bSSascha Wildner  *    contributors may be used to endorse or promote products derived
5212bd3c8bSSascha Wildner  *    from this software without specific prior written permission.
5312bd3c8bSSascha Wildner  *
5412bd3c8bSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
5512bd3c8bSSascha Wildner  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
5612bd3c8bSSascha Wildner  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
5712bd3c8bSSascha Wildner  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
5812bd3c8bSSascha Wildner  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
5912bd3c8bSSascha Wildner  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
6012bd3c8bSSascha Wildner  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
6112bd3c8bSSascha Wildner  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
6212bd3c8bSSascha Wildner  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
6312bd3c8bSSascha Wildner  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
6412bd3c8bSSascha Wildner  * POSSIBILITY OF SUCH DAMAGE.
6512bd3c8bSSascha Wildner  */
6612bd3c8bSSascha Wildner 
67b28dae1fSMarkus Pfeiffer /*
68b28dae1fSMarkus Pfeiffer  * XXX profmakx
69b28dae1fSMarkus Pfeiffer  * This is a Frankenstein of FreeBSD's usb4bsd ucom and Dragonfly's old ucom
70b28dae1fSMarkus Pfeiffer  * module. There might be bugs lurking everywhere still
71b28dae1fSMarkus Pfeiffer  *
72b28dae1fSMarkus Pfeiffer  * In particular serial console on ucom is completely untested and likely broken
73b28dae1fSMarkus Pfeiffer  * as well as anyting that requires the modem control lines.
74b28dae1fSMarkus Pfeiffer  */
75b28dae1fSMarkus Pfeiffer 
7612bd3c8bSSascha Wildner #include <sys/stdint.h>
7712bd3c8bSSascha Wildner #include <sys/param.h>
7812bd3c8bSSascha Wildner #include <sys/queue.h>
7912bd3c8bSSascha Wildner #include <sys/types.h>
8012bd3c8bSSascha Wildner #include <sys/systm.h>
8112bd3c8bSSascha Wildner #include <sys/kernel.h>
8212bd3c8bSSascha Wildner #include <sys/bus.h>
8312bd3c8bSSascha Wildner #include <sys/module.h>
8412bd3c8bSSascha Wildner #include <sys/lock.h>
8512bd3c8bSSascha Wildner #include <sys/condvar.h>
8612bd3c8bSSascha Wildner #include <sys/sysctl.h>
8712bd3c8bSSascha Wildner #include <sys/unistd.h>
8812bd3c8bSSascha Wildner #include <sys/callout.h>
8912bd3c8bSSascha Wildner #include <sys/malloc.h>
90*2b3f93eaSMatthew Dillon #include <sys/caps.h>
9112bd3c8bSSascha Wildner #include <sys/cons.h>
92fd10b026SMarkus Pfeiffer #include <sys/serial.h>
93d95de0a4SMarkus Pfeiffer #include <sys/thread2.h>
943bee31deSMarkus Pfeiffer #include <sys/conf.h>
9512bd3c8bSSascha Wildner 
96a81829f2SSascha Wildner #include <bus/u4b/usb.h>
97a81829f2SSascha Wildner #include <bus/u4b/usbdi.h>
98a81829f2SSascha Wildner #include <bus/u4b/usbdi_util.h>
9912bd3c8bSSascha Wildner 
10012bd3c8bSSascha Wildner #define	USB_DEBUG_VAR ucom_debug
101a81829f2SSascha Wildner #include <bus/u4b/usb_debug.h>
102a81829f2SSascha Wildner #include <bus/u4b/usb_busdma.h>
103a81829f2SSascha Wildner #include <bus/u4b/usb_process.h>
10412bd3c8bSSascha Wildner 
105a81829f2SSascha Wildner #include <bus/u4b/serial/usb_serial.h>
10612bd3c8bSSascha Wildner 
10715130067Szrj /* #include "opt_gdb.h" */
10812bd3c8bSSascha Wildner 
10912bd3c8bSSascha Wildner static SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
11012bd3c8bSSascha Wildner 
111dd681da6SMatthew Dillon static int ucom_pps_mode;
112dd681da6SMatthew Dillon 
113dd681da6SMatthew Dillon SYSCTL_INT(_hw_usb_ucom, OID_AUTO, pps_mode, CTLFLAG_RW,
114dd681da6SMatthew Dillon     &ucom_pps_mode, 0, "pulse capturing mode - 0/1/2 - disabled/CTS/DCD");
115dd681da6SMatthew Dillon TUNABLE_INT("hw.usb.ucom.pss_mode", &ucom_pps_mode);
116dd681da6SMatthew Dillon 
117dd681da6SMatthew Dillon 
11812bd3c8bSSascha Wildner #ifdef USB_DEBUG
11912bd3c8bSSascha Wildner static int ucom_debug = 0;
12012bd3c8bSSascha Wildner 
121a9b765b7SSascha Wildner SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
12212bd3c8bSSascha Wildner     &ucom_debug, 0, "ucom debug level");
12312bd3c8bSSascha Wildner #endif
12412bd3c8bSSascha Wildner 
12512bd3c8bSSascha Wildner #define	UCOM_CONS_BUFSIZE 1024
12612bd3c8bSSascha Wildner 
12712bd3c8bSSascha Wildner static uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE];
12812bd3c8bSSascha Wildner static uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE];
12912bd3c8bSSascha Wildner 
13012bd3c8bSSascha Wildner static unsigned int ucom_cons_rx_low = 0;
13112bd3c8bSSascha Wildner static unsigned int ucom_cons_rx_high = 0;
13212bd3c8bSSascha Wildner 
13312bd3c8bSSascha Wildner static unsigned int ucom_cons_tx_low = 0;
13412bd3c8bSSascha Wildner static unsigned int ucom_cons_tx_high = 0;
13512bd3c8bSSascha Wildner 
13612bd3c8bSSascha Wildner static int ucom_cons_unit = -1;
13712bd3c8bSSascha Wildner static int ucom_cons_subunit = 0;
13812bd3c8bSSascha Wildner static int ucom_cons_baud = 9600;
13912bd3c8bSSascha Wildner static struct ucom_softc *ucom_cons_softc = NULL;
14012bd3c8bSSascha Wildner 
14112bd3c8bSSascha Wildner TUNABLE_INT("hw.usb.ucom.cons_unit", &ucom_cons_unit);
14212bd3c8bSSascha Wildner SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RW,
14312bd3c8bSSascha Wildner     &ucom_cons_unit, 0, "console unit number");
14412bd3c8bSSascha Wildner TUNABLE_INT("hw.usb.ucom.cons_subunit", &ucom_cons_subunit);
14512bd3c8bSSascha Wildner SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_subunit, CTLFLAG_RW,
14612bd3c8bSSascha Wildner     &ucom_cons_subunit, 0, "console subunit number");
14712bd3c8bSSascha Wildner TUNABLE_INT("hw.usb.ucom.cons_baud", &ucom_cons_baud);
14812bd3c8bSSascha Wildner SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RW,
14912bd3c8bSSascha Wildner     &ucom_cons_baud, 0, "console baud rate");
15012bd3c8bSSascha Wildner 
15112bd3c8bSSascha Wildner static usb_proc_callback_t ucom_cfg_start_transfers;
15212bd3c8bSSascha Wildner static usb_proc_callback_t ucom_cfg_open;
15312bd3c8bSSascha Wildner static usb_proc_callback_t ucom_cfg_close;
15412bd3c8bSSascha Wildner static usb_proc_callback_t ucom_cfg_line_state;
15512bd3c8bSSascha Wildner static usb_proc_callback_t ucom_cfg_status_change;
15612bd3c8bSSascha Wildner static usb_proc_callback_t ucom_cfg_param;
15712bd3c8bSSascha Wildner 
15812bd3c8bSSascha Wildner static int	ucom_unit_alloc(void);
15912bd3c8bSSascha Wildner static void	ucom_unit_free(int);
16012bd3c8bSSascha Wildner static int	ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *);
16118a80dc3SMarkus Pfeiffer static void	ucom_detach_tty(struct ucom_super_softc *, struct ucom_softc *);
16212bd3c8bSSascha Wildner static void	ucom_queue_command(struct ucom_softc *,
16312bd3c8bSSascha Wildner 		    usb_proc_callback_t *, struct termios *pt,
16412bd3c8bSSascha Wildner 		    struct usb_proc_msg *t0, struct usb_proc_msg *t1);
16512bd3c8bSSascha Wildner static void	ucom_shutdown(struct ucom_softc *);
16612bd3c8bSSascha Wildner static void	ucom_ring(struct ucom_softc *, uint8_t);
16712bd3c8bSSascha Wildner static void	ucom_break(struct ucom_softc *, uint8_t);
16812bd3c8bSSascha Wildner static void	ucom_dtr(struct ucom_softc *, uint8_t);
16912bd3c8bSSascha Wildner static void	ucom_rts(struct ucom_softc *, uint8_t);
17012bd3c8bSSascha Wildner 
171d95de0a4SMarkus Pfeiffer static int ucom_open(struct ucom_softc *sc);
172d95de0a4SMarkus Pfeiffer static int ucom_close(struct ucom_softc *sc);
173d95de0a4SMarkus Pfeiffer static void ucom_start(struct tty *tp);
174d95de0a4SMarkus Pfeiffer static void ucom_stop(struct tty *tp, int);
175fd10b026SMarkus Pfeiffer static int ucom_param(struct tty *tp, struct termios *t);
176fd10b026SMarkus Pfeiffer static int ucom_modem(struct tty *tp, int sigon, int sigoff);
177fd10b026SMarkus Pfeiffer 
178b28dae1fSMarkus Pfeiffer static int ucom_fromtio(int);
179b28dae1fSMarkus Pfeiffer static int ucom_totio(int);
180b28dae1fSMarkus Pfeiffer 
181b28dae1fSMarkus Pfeiffer static void disc_optim(struct tty *, struct termios *, struct ucom_softc *);
182b28dae1fSMarkus Pfeiffer 
183d95de0a4SMarkus Pfeiffer static d_open_t ucom_dev_open;
184d95de0a4SMarkus Pfeiffer static d_close_t ucom_dev_close;
185d95de0a4SMarkus Pfeiffer static d_read_t ucom_dev_read;
186d95de0a4SMarkus Pfeiffer static d_write_t ucom_dev_write;
187d95de0a4SMarkus Pfeiffer static d_ioctl_t ucom_dev_ioctl;
188d95de0a4SMarkus Pfeiffer 
189fd10b026SMarkus Pfeiffer static struct dev_ops ucom_ops = {
1907d2c8674SMarkus Pfeiffer   { "ucom", 0, D_MPSAFE | D_TTY },
191d95de0a4SMarkus Pfeiffer   .d_open =       ucom_dev_open,
192d95de0a4SMarkus Pfeiffer   .d_close =      ucom_dev_close,
193d95de0a4SMarkus Pfeiffer   .d_read =       ucom_dev_read,
194d95de0a4SMarkus Pfeiffer   .d_write =      ucom_dev_write,
195d95de0a4SMarkus Pfeiffer   .d_ioctl =      ucom_dev_ioctl,
196fd10b026SMarkus Pfeiffer   .d_kqfilter =   ttykqfilter,
197fd10b026SMarkus Pfeiffer   .d_revoke =     ttyrevoke
198fd10b026SMarkus Pfeiffer };
199fd10b026SMarkus Pfeiffer 
200d95de0a4SMarkus Pfeiffer static moduledata_t ucom_mod = {
201d95de0a4SMarkus Pfeiffer         "ucom",
202d95de0a4SMarkus Pfeiffer         NULL,
203d95de0a4SMarkus Pfeiffer         NULL
204d95de0a4SMarkus Pfeiffer };
205d95de0a4SMarkus Pfeiffer 
206d95de0a4SMarkus Pfeiffer DECLARE_MODULE(ucom, ucom_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
207d95de0a4SMarkus Pfeiffer MODULE_DEPEND(ucom, usb, 1, 1, 1);
208d95de0a4SMarkus Pfeiffer MODULE_VERSION(ucom, UCOM_MODVER);
209d95de0a4SMarkus Pfeiffer 
2108c494ac4SMarkus Pfeiffer /* XXXDF */
2118c494ac4SMarkus Pfeiffer #define tty_gone(tp) ((tp->t_state) & (TS_ZOMBIE))
2128c494ac4SMarkus Pfeiffer 
21318a80dc3SMarkus Pfeiffer #define	UCOM_UNIT_MAX 		128	/* maximum number of units */
214d95de0a4SMarkus Pfeiffer #define	UCOM_TTY_PREFIX		"ucom"
21512bd3c8bSSascha Wildner 
216b040f636SMarkus Pfeiffer 
217b040f636SMarkus Pfeiffer #define CALLOUT_MASK            0x80
218b040f636SMarkus Pfeiffer #define CONTROL_MASK            0x60
219b040f636SMarkus Pfeiffer #define CONTROL_INIT_STATE      0x20
220b040f636SMarkus Pfeiffer #define CONTROL_LOCK_STATE      0x40
221b040f636SMarkus Pfeiffer 
22218a80dc3SMarkus Pfeiffer static struct unrhdr *ucom_unrhdr;
22318a80dc3SMarkus Pfeiffer static struct lock ucom_lock;
22418a80dc3SMarkus Pfeiffer static int ucom_close_refs;
22518a80dc3SMarkus Pfeiffer 
22618a80dc3SMarkus Pfeiffer static void
ucom_init(void * arg)22718a80dc3SMarkus Pfeiffer ucom_init(void *arg)
22818a80dc3SMarkus Pfeiffer {
22918a80dc3SMarkus Pfeiffer 	DPRINTF("\n");
23018a80dc3SMarkus Pfeiffer 	ucom_unrhdr = new_unrhdr(0, UCOM_UNIT_MAX - 1, NULL);
23118a80dc3SMarkus Pfeiffer 	lockinit(&ucom_lock, "UCOM LOCK", 0, 0);
23218a80dc3SMarkus Pfeiffer }
233fd10b026SMarkus Pfeiffer SYSINIT(ucom_init, SI_BOOT2_KLD - 1, SI_ORDER_ANY, ucom_init, NULL);
23418a80dc3SMarkus Pfeiffer 
23518a80dc3SMarkus Pfeiffer static void
ucom_uninit(void * arg)23618a80dc3SMarkus Pfeiffer ucom_uninit(void *arg)
23718a80dc3SMarkus Pfeiffer {
23818a80dc3SMarkus Pfeiffer 	struct unrhdr *hdr;
23918a80dc3SMarkus Pfeiffer 	hdr = ucom_unrhdr;
24018a80dc3SMarkus Pfeiffer 	ucom_unrhdr = NULL;
24118a80dc3SMarkus Pfeiffer 
24218a80dc3SMarkus Pfeiffer 	DPRINTF("\n");
24318a80dc3SMarkus Pfeiffer 
24418a80dc3SMarkus Pfeiffer 	if (hdr != NULL)
24518a80dc3SMarkus Pfeiffer 		delete_unrhdr(hdr);
24618a80dc3SMarkus Pfeiffer 
24718a80dc3SMarkus Pfeiffer 	lockuninit(&ucom_lock);
24818a80dc3SMarkus Pfeiffer }
249fd10b026SMarkus Pfeiffer SYSUNINIT(ucom_uninit, SI_BOOT2_KLD - 2, SI_ORDER_ANY, ucom_uninit, NULL);
25018a80dc3SMarkus Pfeiffer 
25112bd3c8bSSascha Wildner /*
25212bd3c8bSSascha Wildner  * Mark a unit number (the X in cuaUX) as in use.
25312bd3c8bSSascha Wildner  *
25412bd3c8bSSascha Wildner  * Note that devices using a different naming scheme (see ucom_tty_name()
25512bd3c8bSSascha Wildner  * callback) still use this unit allocation.
25612bd3c8bSSascha Wildner  */
25712bd3c8bSSascha Wildner static int
ucom_unit_alloc(void)25812bd3c8bSSascha Wildner ucom_unit_alloc(void)
25912bd3c8bSSascha Wildner {
26012bd3c8bSSascha Wildner 	int unit;
26112bd3c8bSSascha Wildner 
26218a80dc3SMarkus Pfeiffer 	/* sanity checks */
26318a80dc3SMarkus Pfeiffer 	if (ucom_unrhdr == NULL) {
26418a80dc3SMarkus Pfeiffer 		DPRINTF("ucom_unrhdr is NULL\n");
26518a80dc3SMarkus Pfeiffer 		return (-1);
26612bd3c8bSSascha Wildner 	}
26718a80dc3SMarkus Pfeiffer 	unit = alloc_unr(ucom_unrhdr);
26818a80dc3SMarkus Pfeiffer 	DPRINTF("unit %d is allocated\n", unit);
26918a80dc3SMarkus Pfeiffer 	return (unit);
27012bd3c8bSSascha Wildner }
27112bd3c8bSSascha Wildner 
27212bd3c8bSSascha Wildner /*
27312bd3c8bSSascha Wildner  * Mark the unit number as not in use.
27412bd3c8bSSascha Wildner  */
27512bd3c8bSSascha Wildner static void
ucom_unit_free(int unit)27612bd3c8bSSascha Wildner ucom_unit_free(int unit)
27712bd3c8bSSascha Wildner {
27818a80dc3SMarkus Pfeiffer 	/* sanity checks */
27918a80dc3SMarkus Pfeiffer 	if (unit < 0 || unit >= UCOM_UNIT_MAX || ucom_unrhdr == NULL) {
28018a80dc3SMarkus Pfeiffer 		DPRINTF("cannot free unit number\n");
28118a80dc3SMarkus Pfeiffer 		return;
28218a80dc3SMarkus Pfeiffer 	}
28318a80dc3SMarkus Pfeiffer 	DPRINTF("unit %d is freed\n", unit);
28418a80dc3SMarkus Pfeiffer 	free_unr(ucom_unrhdr, unit);
28512bd3c8bSSascha Wildner }
28612bd3c8bSSascha Wildner 
28712bd3c8bSSascha Wildner /*
28812bd3c8bSSascha Wildner  * Setup a group of one or more serial ports.
28912bd3c8bSSascha Wildner  *
290a81829f2SSascha Wildner  * The lock pointed to by "lock" is applied before all
291a81829f2SSascha Wildner  * callbacks are called back. Also "lock" must be applied
29212bd3c8bSSascha Wildner  * before calling into the ucom-layer!
29312bd3c8bSSascha Wildner  */
29412bd3c8bSSascha Wildner int
ucom_attach(struct ucom_super_softc * ssc,struct ucom_softc * sc,int subunits,void * parent,const struct ucom_callback * callback,struct lock * lock)29512bd3c8bSSascha Wildner ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
29618a80dc3SMarkus Pfeiffer     int subunits, void *parent,
297a81829f2SSascha Wildner     const struct ucom_callback *callback, struct lock *lock)
29812bd3c8bSSascha Wildner {
29918a80dc3SMarkus Pfeiffer 	int subunit;
30012bd3c8bSSascha Wildner 	int error = 0;
30112bd3c8bSSascha Wildner 
30212bd3c8bSSascha Wildner 	if ((sc == NULL) ||
30318a80dc3SMarkus Pfeiffer 	    (subunits <= 0) ||
30418a80dc3SMarkus Pfeiffer 	    (callback == NULL) ||
30518a80dc3SMarkus Pfeiffer 	    (lock == NULL)) {
30612bd3c8bSSascha Wildner 		return (EINVAL);
30712bd3c8bSSascha Wildner 	}
30812bd3c8bSSascha Wildner 
309d95de0a4SMarkus Pfeiffer 	/* XXX Do we want our own lock here maybe */
310d95de0a4SMarkus Pfeiffer 	sc->sc_lock = lock;
311d95de0a4SMarkus Pfeiffer 
31212bd3c8bSSascha Wildner 	/* allocate a uniq unit number */
31312bd3c8bSSascha Wildner 	ssc->sc_unit = ucom_unit_alloc();
31412bd3c8bSSascha Wildner 	if (ssc->sc_unit == -1)
31512bd3c8bSSascha Wildner 		return (ENOMEM);
31612bd3c8bSSascha Wildner 
31712bd3c8bSSascha Wildner 	/* generate TTY name string */
318a81829f2SSascha Wildner 	ksnprintf(ssc->sc_ttyname, sizeof(ssc->sc_ttyname),
31912bd3c8bSSascha Wildner 	    UCOM_TTY_PREFIX "%d", ssc->sc_unit);
32012bd3c8bSSascha Wildner 
32112bd3c8bSSascha Wildner 	/* create USB request handling process */
322a81829f2SSascha Wildner 	error = usb_proc_create(&ssc->sc_tq, lock, "ucom", USB_PRI_MED);
32312bd3c8bSSascha Wildner 	if (error) {
32412bd3c8bSSascha Wildner 		ucom_unit_free(ssc->sc_unit);
32512bd3c8bSSascha Wildner 		return (error);
32612bd3c8bSSascha Wildner 	}
32712bd3c8bSSascha Wildner 	ssc->sc_subunits = subunits;
32818a80dc3SMarkus Pfeiffer 	ssc->sc_flag = UCOM_FLAG_ATTACHED |
32918a80dc3SMarkus Pfeiffer 	    UCOM_FLAG_FREE_UNIT;
33018a80dc3SMarkus Pfeiffer 
33118a80dc3SMarkus Pfeiffer 	if (callback->ucom_free == NULL)
33218a80dc3SMarkus Pfeiffer 		ssc->sc_flag |= UCOM_FLAG_WAIT_REFS;
33318a80dc3SMarkus Pfeiffer 
33418a80dc3SMarkus Pfeiffer 	/* increment reference count */
33518a80dc3SMarkus Pfeiffer 	ucom_ref(ssc);
33612bd3c8bSSascha Wildner 
33712bd3c8bSSascha Wildner 	for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
33812bd3c8bSSascha Wildner 		sc[subunit].sc_subunit = subunit;
33912bd3c8bSSascha Wildner 		sc[subunit].sc_super = ssc;
340a81829f2SSascha Wildner 		sc[subunit].sc_lock = lock;
34112bd3c8bSSascha Wildner 		sc[subunit].sc_parent = parent;
34212bd3c8bSSascha Wildner 		sc[subunit].sc_callback = callback;
34312bd3c8bSSascha Wildner 
34412bd3c8bSSascha Wildner 		error = ucom_attach_tty(ssc, &sc[subunit]);
34512bd3c8bSSascha Wildner 		if (error) {
34612bd3c8bSSascha Wildner 			ucom_detach(ssc, &sc[0]);
34712bd3c8bSSascha Wildner 			return (error);
34812bd3c8bSSascha Wildner 		}
34918a80dc3SMarkus Pfeiffer 		/* increment reference count */
35018a80dc3SMarkus Pfeiffer 		ucom_ref(ssc);
35118a80dc3SMarkus Pfeiffer 
35218a80dc3SMarkus Pfeiffer 		/* set subunit attached */
35312bd3c8bSSascha Wildner 		sc[subunit].sc_flag |= UCOM_FLAG_ATTACHED;
35412bd3c8bSSascha Wildner 	}
35512bd3c8bSSascha Wildner 
35612bd3c8bSSascha Wildner 	DPRINTF("tp = %p, unit = %d, subunits = %d\n",
35712bd3c8bSSascha Wildner 		sc->sc_tty, ssc->sc_unit, ssc->sc_subunits);
35812bd3c8bSSascha Wildner 
35912bd3c8bSSascha Wildner 	return (0);
36012bd3c8bSSascha Wildner }
36112bd3c8bSSascha Wildner 
36212bd3c8bSSascha Wildner /*
36318a80dc3SMarkus Pfeiffer  * The following function will do nothing if the structure pointed to
36418a80dc3SMarkus Pfeiffer  * by "ssc" and "sc" is zero or has already been detached.
36512bd3c8bSSascha Wildner  */
36612bd3c8bSSascha Wildner void
ucom_detach(struct ucom_super_softc * ssc,struct ucom_softc * sc)36712bd3c8bSSascha Wildner ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc)
36812bd3c8bSSascha Wildner {
36918a80dc3SMarkus Pfeiffer 	int subunit;
37012bd3c8bSSascha Wildner 
37118a80dc3SMarkus Pfeiffer 	if (!(ssc->sc_flag & UCOM_FLAG_ATTACHED))
37212bd3c8bSSascha Wildner 		return;		/* not initialized */
37312bd3c8bSSascha Wildner 
374bd91b08eSMarkus Pfeiffer 	destroy_dev(sc->sc_cdev);
375b040f636SMarkus Pfeiffer 	destroy_dev(sc->sc_cdev_init);
376b040f636SMarkus Pfeiffer 	destroy_dev(sc->sc_cdev_lock);
377b040f636SMarkus Pfeiffer 	destroy_dev(sc->sc_cdev2);
378b040f636SMarkus Pfeiffer 	destroy_dev(sc->sc_cdev2_init);
379b040f636SMarkus Pfeiffer 	destroy_dev(sc->sc_cdev2_lock);
380bd91b08eSMarkus Pfeiffer 
3812efb75f3SMatthew Dillon 	if (sc->sc_tty)
3822efb75f3SMatthew Dillon 		lwkt_gettoken(&sc->sc_tty->t_token);
383bd91b08eSMarkus Pfeiffer 
38412bd3c8bSSascha Wildner 	if (ssc->sc_sysctl_ttyname != NULL) {
38512bd3c8bSSascha Wildner 		sysctl_remove_oid(ssc->sc_sysctl_ttyname, 1, 0);
38612bd3c8bSSascha Wildner 		ssc->sc_sysctl_ttyname = NULL;
38712bd3c8bSSascha Wildner 	}
38812bd3c8bSSascha Wildner 
38912bd3c8bSSascha Wildner 	if (ssc->sc_sysctl_ttyports != NULL) {
39012bd3c8bSSascha Wildner 		sysctl_remove_oid(ssc->sc_sysctl_ttyports, 1, 0);
39112bd3c8bSSascha Wildner 		ssc->sc_sysctl_ttyports = NULL;
39212bd3c8bSSascha Wildner 	}
39312bd3c8bSSascha Wildner 
39412bd3c8bSSascha Wildner 	usb_proc_drain(&ssc->sc_tq);
39512bd3c8bSSascha Wildner 
39612bd3c8bSSascha Wildner 	for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
39712bd3c8bSSascha Wildner 		if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) {
3983b964699Szrj 
39918a80dc3SMarkus Pfeiffer 			ucom_detach_tty(ssc, &sc[subunit]);
40012bd3c8bSSascha Wildner 
40112bd3c8bSSascha Wildner 			/* avoid duplicate detach */
40212bd3c8bSSascha Wildner 			sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED;
40312bd3c8bSSascha Wildner 		}
40412bd3c8bSSascha Wildner 	}
40512bd3c8bSSascha Wildner 	usb_proc_free(&ssc->sc_tq);
40618a80dc3SMarkus Pfeiffer 
40718a80dc3SMarkus Pfeiffer 	ucom_unref(ssc);
40818a80dc3SMarkus Pfeiffer 
40918a80dc3SMarkus Pfeiffer 	if (ssc->sc_flag & UCOM_FLAG_WAIT_REFS)
41018a80dc3SMarkus Pfeiffer 		ucom_drain(ssc);
41118a80dc3SMarkus Pfeiffer 
41218a80dc3SMarkus Pfeiffer 	/* make sure we don't detach twice */
41318a80dc3SMarkus Pfeiffer 	ssc->sc_flag &= ~UCOM_FLAG_ATTACHED;
414bd91b08eSMarkus Pfeiffer 
4152efb75f3SMatthew Dillon 	if (sc->sc_tty)
4162efb75f3SMatthew Dillon 		lwkt_reltoken(&sc->sc_tty->t_token);
41718a80dc3SMarkus Pfeiffer }
41818a80dc3SMarkus Pfeiffer 
41918a80dc3SMarkus Pfeiffer void
ucom_drain(struct ucom_super_softc * ssc)42018a80dc3SMarkus Pfeiffer ucom_drain(struct ucom_super_softc *ssc)
42118a80dc3SMarkus Pfeiffer {
42218a80dc3SMarkus Pfeiffer 	lockmgr(&ucom_lock, LK_EXCLUSIVE);
42318a80dc3SMarkus Pfeiffer 	while (ssc->sc_refs > 0) {
424fd10b026SMarkus Pfeiffer 		kprintf("ucom: Waiting for a TTY device to close.\n");
42518a80dc3SMarkus Pfeiffer 		usb_pause_mtx(&ucom_lock, hz);
42618a80dc3SMarkus Pfeiffer 	}
42718a80dc3SMarkus Pfeiffer 	lockmgr(&ucom_lock, LK_RELEASE);
42818a80dc3SMarkus Pfeiffer }
42918a80dc3SMarkus Pfeiffer 
43018a80dc3SMarkus Pfeiffer void
ucom_drain_all(void * arg)43118a80dc3SMarkus Pfeiffer ucom_drain_all(void *arg)
43218a80dc3SMarkus Pfeiffer {
43318a80dc3SMarkus Pfeiffer 	lockmgr(&ucom_lock, LK_EXCLUSIVE);
43418a80dc3SMarkus Pfeiffer 	while (ucom_close_refs > 0) {
435fd10b026SMarkus Pfeiffer 		kprintf("ucom: Waiting for all detached TTY "
43618a80dc3SMarkus Pfeiffer 		    "devices to have open fds closed.\n");
43718a80dc3SMarkus Pfeiffer 		usb_pause_mtx(&ucom_lock, hz);
43818a80dc3SMarkus Pfeiffer 	}
43918a80dc3SMarkus Pfeiffer 	lockmgr(&ucom_lock, LK_RELEASE);
44012bd3c8bSSascha Wildner }
44112bd3c8bSSascha Wildner 
44212bd3c8bSSascha Wildner static int
ucom_attach_tty(struct ucom_super_softc * ssc,struct ucom_softc * sc)44312bd3c8bSSascha Wildner ucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
44412bd3c8bSSascha Wildner {
44512bd3c8bSSascha Wildner 	struct tty *tp;
44612bd3c8bSSascha Wildner 	char buf[32];			/* temporary TTY device name buffer */
44712bd3c8bSSascha Wildner 
4482efb75f3SMatthew Dillon 	tp = ttymalloc(&sc->sc_tty);
4493bee31deSMarkus Pfeiffer 
4502efb75f3SMatthew Dillon 	lwkt_gettoken(&tp->t_token);
45112bd3c8bSSascha Wildner 
4528c494ac4SMarkus Pfeiffer 	tp->t_sc = (void *)sc;
4538c494ac4SMarkus Pfeiffer 
4548c494ac4SMarkus Pfeiffer 	tp->t_oproc = ucom_start;
4558c494ac4SMarkus Pfeiffer 	tp->t_param = ucom_param;
4568c494ac4SMarkus Pfeiffer 	tp->t_stop = ucom_stop;
4578c494ac4SMarkus Pfeiffer 
45812bd3c8bSSascha Wildner 	/* Check if the client has a custom TTY name */
45912bd3c8bSSascha Wildner 	buf[0] = '\0';
46012bd3c8bSSascha Wildner 	if (sc->sc_callback->ucom_tty_name) {
46112bd3c8bSSascha Wildner 		sc->sc_callback->ucom_tty_name(sc, buf,
46212bd3c8bSSascha Wildner 		    sizeof(buf), ssc->sc_unit, sc->sc_subunit);
46312bd3c8bSSascha Wildner 	}
46412bd3c8bSSascha Wildner 	if (buf[0] == 0) {
46512bd3c8bSSascha Wildner 		/* Use default TTY name */
46612bd3c8bSSascha Wildner 		if (ssc->sc_subunits > 1) {
46712bd3c8bSSascha Wildner 			/* multiple modems in one */
468f5866fbaSMarkus Pfeiffer 			ksnprintf(buf, sizeof(buf), "%u.%u",
46912bd3c8bSSascha Wildner 			    ssc->sc_unit, sc->sc_subunit);
47012bd3c8bSSascha Wildner 		} else {
47112bd3c8bSSascha Wildner 			/* single modem */
472f5866fbaSMarkus Pfeiffer 			ksnprintf(buf, sizeof(buf), "%u",
47312bd3c8bSSascha Wildner 			    ssc->sc_unit);
47412bd3c8bSSascha Wildner 		}
47512bd3c8bSSascha Wildner 	}
4763bee31deSMarkus Pfeiffer 
477b040f636SMarkus Pfeiffer 	sc->sc_cdev = make_dev(&ucom_ops, ssc->sc_unit,
478f5866fbaSMarkus Pfeiffer 			UID_ROOT, GID_WHEEL, 0600, "ttyU%s", buf);
479b040f636SMarkus Pfeiffer 	sc->sc_cdev_init = make_dev(&ucom_ops, ssc->sc_unit | CONTROL_INIT_STATE,
480f5866fbaSMarkus Pfeiffer 			UID_ROOT, GID_WHEEL, 0600, "ttyiU%s", buf);
481b040f636SMarkus Pfeiffer 	sc->sc_cdev_lock = make_dev(&ucom_ops, ssc->sc_unit | CONTROL_LOCK_STATE,
482f5866fbaSMarkus Pfeiffer 			UID_ROOT, GID_WHEEL, 0600, "ttylU%s", buf);
483b040f636SMarkus Pfeiffer 	sc->sc_cdev2 = make_dev(&ucom_ops, ssc->sc_unit | CALLOUT_MASK,
484f5866fbaSMarkus Pfeiffer 			UID_UUCP, GID_DIALER, 0660, "cuaU%s", buf);
485b040f636SMarkus Pfeiffer 	sc->sc_cdev2_init = make_dev(&ucom_ops, ssc->sc_unit | CALLOUT_MASK | CONTROL_INIT_STATE,
486f5866fbaSMarkus Pfeiffer 			UID_UUCP, GID_DIALER, 0660, "cuaiU%s", buf);
487b040f636SMarkus Pfeiffer 	sc->sc_cdev2_lock = make_dev(&ucom_ops, ssc->sc_unit | CALLOUT_MASK | CONTROL_LOCK_STATE,
488f5866fbaSMarkus Pfeiffer 			UID_UUCP, GID_DIALER, 0660, "cualU%s", buf);
489b040f636SMarkus Pfeiffer 
490b040f636SMarkus Pfeiffer 	sc->sc_cdev->si_tty = tp;
491b040f636SMarkus Pfeiffer 	sc->sc_cdev_init->si_tty = tp;
492b040f636SMarkus Pfeiffer 	sc->sc_cdev_lock->si_tty = tp;
493b040f636SMarkus Pfeiffer 
494b040f636SMarkus Pfeiffer 	sc->sc_cdev->si_drv1 = sc;
495b040f636SMarkus Pfeiffer 	sc->sc_cdev_init->si_drv1 = sc;
496b040f636SMarkus Pfeiffer 	sc->sc_cdev_lock->si_drv1 = sc;
497b040f636SMarkus Pfeiffer 
498c90f1249SMatthew Dillon 	sc->sc_cdev2->si_tty = tp;
499c90f1249SMatthew Dillon 	sc->sc_cdev2_init->si_tty = tp;
500c90f1249SMatthew Dillon 	sc->sc_cdev2_lock->si_tty = tp;
501c90f1249SMatthew Dillon 
502b040f636SMarkus Pfeiffer 	sc->sc_cdev2->si_drv1 = sc;
503b040f636SMarkus Pfeiffer 	sc->sc_cdev2_init->si_drv1 = sc;
504b040f636SMarkus Pfeiffer 	sc->sc_cdev2_lock->si_drv1 = sc;
505b040f636SMarkus Pfeiffer 
50612bd3c8bSSascha Wildner 	sc->sc_tty = tp;
50712bd3c8bSSascha Wildner 
50812bd3c8bSSascha Wildner 	DPRINTF("ttycreate: %s\n", buf);
50912bd3c8bSSascha Wildner 
51012bd3c8bSSascha Wildner 	/* Check if this device should be a console */
51112bd3c8bSSascha Wildner 	if ((ucom_cons_softc == NULL) &&
51212bd3c8bSSascha Wildner 	    (ssc->sc_unit == ucom_cons_unit) &&
51312bd3c8bSSascha Wildner 	    (sc->sc_subunit == ucom_cons_subunit)) {
51412bd3c8bSSascha Wildner 
51518a80dc3SMarkus Pfeiffer 		DPRINTF("unit %d subunit %d is console",
51618a80dc3SMarkus Pfeiffer 		    ssc->sc_unit, sc->sc_subunit);
51712bd3c8bSSascha Wildner 
51812bd3c8bSSascha Wildner 		ucom_cons_softc = sc;
51912bd3c8bSSascha Wildner 
52015130067Szrj #if 0 /* XXXDF */
52118a80dc3SMarkus Pfeiffer 		tty_init_console(tp, ucom_cons_baud);
52215130067Szrj #endif
523d95de0a4SMarkus Pfeiffer 		tp->t_termios.c_ispeed = ucom_cons_baud;
524d95de0a4SMarkus Pfeiffer 		tp->t_termios.c_ospeed = ucom_cons_baud;
52512bd3c8bSSascha Wildner 
52618a80dc3SMarkus Pfeiffer 		UCOM_MTX_LOCK(ucom_cons_softc);
52712bd3c8bSSascha Wildner 		ucom_cons_rx_low = 0;
52812bd3c8bSSascha Wildner 		ucom_cons_rx_high = 0;
52912bd3c8bSSascha Wildner 		ucom_cons_tx_low = 0;
53012bd3c8bSSascha Wildner 		ucom_cons_tx_high = 0;
53112bd3c8bSSascha Wildner 		sc->sc_flag |= UCOM_FLAG_CONSOLE;
532d95de0a4SMarkus Pfeiffer 		ucom_open(ucom_cons_softc);
533d95de0a4SMarkus Pfeiffer 		ucom_param(tp, &tp->t_termios);
53418a80dc3SMarkus Pfeiffer 		UCOM_MTX_UNLOCK(ucom_cons_softc);
53512bd3c8bSSascha Wildner 	}
53612bd3c8bSSascha Wildner 
5372efb75f3SMatthew Dillon 	lwkt_reltoken(&tp->t_token);
5382efb75f3SMatthew Dillon 
53912bd3c8bSSascha Wildner 	return (0);
54012bd3c8bSSascha Wildner }
54112bd3c8bSSascha Wildner 
54212bd3c8bSSascha Wildner static void
ucom_detach_tty(struct ucom_super_softc * ssc,struct ucom_softc * sc)54318a80dc3SMarkus Pfeiffer ucom_detach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
54412bd3c8bSSascha Wildner {
54512bd3c8bSSascha Wildner 	struct tty *tp = sc->sc_tty;
54612bd3c8bSSascha Wildner 
54712bd3c8bSSascha Wildner 	DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
54812bd3c8bSSascha Wildner 
54912bd3c8bSSascha Wildner 	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
55018a80dc3SMarkus Pfeiffer 		UCOM_MTX_LOCK(ucom_cons_softc);
551d95de0a4SMarkus Pfeiffer 		ucom_close(ucom_cons_softc);
55212bd3c8bSSascha Wildner 		sc->sc_flag &= ~UCOM_FLAG_CONSOLE;
55318a80dc3SMarkus Pfeiffer 		UCOM_MTX_UNLOCK(ucom_cons_softc);
55412bd3c8bSSascha Wildner 		ucom_cons_softc = NULL;
55512bd3c8bSSascha Wildner 	}
55612bd3c8bSSascha Wildner 
55712bd3c8bSSascha Wildner 	/* the config thread has been stopped when we get here */
55812bd3c8bSSascha Wildner 
55918a80dc3SMarkus Pfeiffer 	UCOM_MTX_LOCK(sc);
56012bd3c8bSSascha Wildner 	sc->sc_flag |= UCOM_FLAG_GONE;
56112bd3c8bSSascha Wildner 	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY);
56218a80dc3SMarkus Pfeiffer 	UCOM_MTX_UNLOCK(sc);
56318a80dc3SMarkus Pfeiffer 
564b28dae1fSMarkus Pfeiffer 	if (tp != NULL) {
5652efb75f3SMatthew Dillon 		lwkt_gettoken(&tp->t_token);
56618a80dc3SMarkus Pfeiffer 		ucom_close_refs++;
56718a80dc3SMarkus Pfeiffer 
568b28dae1fSMarkus Pfeiffer 		UCOM_MTX_LOCK(sc);
569b28dae1fSMarkus Pfeiffer 		if (tp->t_state & TS_ISOPEN) {
570b28dae1fSMarkus Pfeiffer 			kprintf("device still open, forcing close\n");
571b28dae1fSMarkus Pfeiffer 			(*linesw[tp->t_line].l_close)(tp, 0);
572b28dae1fSMarkus Pfeiffer 			ttyclose(tp);
573b28dae1fSMarkus Pfeiffer 		}
574d95de0a4SMarkus Pfeiffer 		ucom_close(sc);	/* close, if any */
57512bd3c8bSSascha Wildner 
57612bd3c8bSSascha Wildner 		/*
57712bd3c8bSSascha Wildner 		 * make sure that read and write transfers are stopped
57812bd3c8bSSascha Wildner 		 */
57912bd3c8bSSascha Wildner 		if (sc->sc_callback->ucom_stop_read) {
58012bd3c8bSSascha Wildner 			(sc->sc_callback->ucom_stop_read) (sc);
58112bd3c8bSSascha Wildner 		}
58212bd3c8bSSascha Wildner 		if (sc->sc_callback->ucom_stop_write) {
58312bd3c8bSSascha Wildner 			(sc->sc_callback->ucom_stop_write) (sc);
58412bd3c8bSSascha Wildner 		}
58518a80dc3SMarkus Pfeiffer 		UCOM_MTX_UNLOCK(sc);
5862efb75f3SMatthew Dillon 		lwkt_reltoken(&tp->t_token);
587b28dae1fSMarkus Pfeiffer 	} else {
588b28dae1fSMarkus Pfeiffer 		DPRINTF("no tty\n");
58912bd3c8bSSascha Wildner 	}
590bd91b08eSMarkus Pfeiffer 
591bd91b08eSMarkus Pfeiffer 	dev_ops_remove_minor(&ucom_ops,ssc->sc_unit);
592bd91b08eSMarkus Pfeiffer 
593c01d5218SMatthew Dillon 	ucom_unref(ssc);
59412bd3c8bSSascha Wildner }
59512bd3c8bSSascha Wildner 
59612bd3c8bSSascha Wildner void
ucom_set_pnpinfo_usb(struct ucom_super_softc * ssc,device_t dev)59712bd3c8bSSascha Wildner ucom_set_pnpinfo_usb(struct ucom_super_softc *ssc, device_t dev)
59812bd3c8bSSascha Wildner {
59912bd3c8bSSascha Wildner 	char buf[64];
60012bd3c8bSSascha Wildner 	uint8_t iface_index;
60112bd3c8bSSascha Wildner 	struct usb_attach_arg *uaa;
60212bd3c8bSSascha Wildner 
603a81829f2SSascha Wildner 	ksnprintf(buf, sizeof(buf), "ttyname=" UCOM_TTY_PREFIX
60412bd3c8bSSascha Wildner 	    "%d ttyports=%d", ssc->sc_unit, ssc->sc_subunits);
60512bd3c8bSSascha Wildner 
60612bd3c8bSSascha Wildner 	/* Store the PNP info in the first interface for the device */
60712bd3c8bSSascha Wildner 	uaa = device_get_ivars(dev);
60812bd3c8bSSascha Wildner 	iface_index = uaa->info.bIfaceIndex;
60912bd3c8bSSascha Wildner 
61012bd3c8bSSascha Wildner 	if (usbd_set_pnpinfo(uaa->device, iface_index, buf) != 0)
61112bd3c8bSSascha Wildner 		device_printf(dev, "Could not set PNP info\n");
61212bd3c8bSSascha Wildner 
61312bd3c8bSSascha Wildner 	/*
61412bd3c8bSSascha Wildner 	 * The following information is also replicated in the PNP-info
61512bd3c8bSSascha Wildner 	 * string which is registered above:
61612bd3c8bSSascha Wildner 	 */
61712bd3c8bSSascha Wildner 	if (ssc->sc_sysctl_ttyname == NULL) {
618fd10b026SMarkus Pfeiffer 		/*
61912bd3c8bSSascha Wildner 		ssc->sc_sysctl_ttyname = SYSCTL_ADD_STRING(NULL,
62012bd3c8bSSascha Wildner 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
62112bd3c8bSSascha Wildner 		    OID_AUTO, "ttyname", CTLFLAG_RD, ssc->sc_ttyname, 0,
62212bd3c8bSSascha Wildner 		    "TTY device basename");
623fd10b026SMarkus Pfeiffer 		*/
62412bd3c8bSSascha Wildner 	}
62512bd3c8bSSascha Wildner 	if (ssc->sc_sysctl_ttyports == NULL) {
626fd10b026SMarkus Pfeiffer 		/*
62712bd3c8bSSascha Wildner 		ssc->sc_sysctl_ttyports = SYSCTL_ADD_INT(NULL,
62812bd3c8bSSascha Wildner 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
62912bd3c8bSSascha Wildner 		    OID_AUTO, "ttyports", CTLFLAG_RD,
63012bd3c8bSSascha Wildner 		    NULL, ssc->sc_subunits, "Number of ports");
631fd10b026SMarkus Pfeiffer 		*/
63212bd3c8bSSascha Wildner 	}
63312bd3c8bSSascha Wildner }
63412bd3c8bSSascha Wildner 
63512bd3c8bSSascha Wildner static void
ucom_queue_command(struct ucom_softc * sc,usb_proc_callback_t * fn,struct termios * pt,struct usb_proc_msg * t0,struct usb_proc_msg * t1)63612bd3c8bSSascha Wildner ucom_queue_command(struct ucom_softc *sc,
63712bd3c8bSSascha Wildner     usb_proc_callback_t *fn, struct termios *pt,
63812bd3c8bSSascha Wildner     struct usb_proc_msg *t0, struct usb_proc_msg *t1)
63912bd3c8bSSascha Wildner {
64012bd3c8bSSascha Wildner 	struct ucom_super_softc *ssc = sc->sc_super;
64112bd3c8bSSascha Wildner 	struct ucom_param_task *task;
64212bd3c8bSSascha Wildner 
64318a80dc3SMarkus Pfeiffer 	UCOM_MTX_ASSERT(sc, MA_OWNED);
64412bd3c8bSSascha Wildner 
64512bd3c8bSSascha Wildner 	if (usb_proc_is_gone(&ssc->sc_tq)) {
64612bd3c8bSSascha Wildner 		DPRINTF("proc is gone\n");
64712bd3c8bSSascha Wildner 		return;         /* nothing to do */
64812bd3c8bSSascha Wildner 	}
64912bd3c8bSSascha Wildner 	/*
65012bd3c8bSSascha Wildner 	 * NOTE: The task cannot get executed before we drop the
651a81829f2SSascha Wildner 	 * "sc_lock" lock. It is safe to update fields in the message
65212bd3c8bSSascha Wildner 	 * structure after that the message got queued.
65312bd3c8bSSascha Wildner 	 */
65412bd3c8bSSascha Wildner 	task = (struct ucom_param_task *)
65512bd3c8bSSascha Wildner 	  usb_proc_msignal(&ssc->sc_tq, t0, t1);
65612bd3c8bSSascha Wildner 
65712bd3c8bSSascha Wildner 	/* Setup callback and softc pointers */
65812bd3c8bSSascha Wildner 	task->hdr.pm_callback = fn;
65912bd3c8bSSascha Wildner 	task->sc = sc;
66012bd3c8bSSascha Wildner 
66112bd3c8bSSascha Wildner 	/*
66212bd3c8bSSascha Wildner 	 * Make a copy of the termios. This field is only present if
66312bd3c8bSSascha Wildner 	 * the "pt" field is not NULL.
66412bd3c8bSSascha Wildner 	 */
66512bd3c8bSSascha Wildner 	if (pt != NULL)
66612bd3c8bSSascha Wildner 		task->termios_copy = *pt;
66712bd3c8bSSascha Wildner 
66812bd3c8bSSascha Wildner 	/*
66912bd3c8bSSascha Wildner 	 * Closing the device should be synchronous.
67012bd3c8bSSascha Wildner 	 */
67112bd3c8bSSascha Wildner 	if (fn == ucom_cfg_close)
67212bd3c8bSSascha Wildner 		usb_proc_mwait(&ssc->sc_tq, t0, t1);
67312bd3c8bSSascha Wildner 
67412bd3c8bSSascha Wildner 	/*
67512bd3c8bSSascha Wildner 	 * In case of multiple configure requests,
67612bd3c8bSSascha Wildner 	 * keep track of the last one!
67712bd3c8bSSascha Wildner 	 */
67812bd3c8bSSascha Wildner 	if (fn == ucom_cfg_start_transfers)
67912bd3c8bSSascha Wildner 		sc->sc_last_start_xfer = &task->hdr;
68012bd3c8bSSascha Wildner }
68112bd3c8bSSascha Wildner 
68212bd3c8bSSascha Wildner static void
ucom_shutdown(struct ucom_softc * sc)68312bd3c8bSSascha Wildner ucom_shutdown(struct ucom_softc *sc)
68412bd3c8bSSascha Wildner {
68512bd3c8bSSascha Wildner 	struct tty *tp = sc->sc_tty;
68612bd3c8bSSascha Wildner 
68718a80dc3SMarkus Pfeiffer 	UCOM_MTX_ASSERT(sc, MA_OWNED);
68812bd3c8bSSascha Wildner 
68912bd3c8bSSascha Wildner 	DPRINTF("\n");
69012bd3c8bSSascha Wildner 
69112bd3c8bSSascha Wildner 	/*
69212bd3c8bSSascha Wildner 	 * Hang up if necessary:
69312bd3c8bSSascha Wildner 	 */
69412bd3c8bSSascha Wildner 	if (tp->t_termios.c_cflag & HUPCL) {
69569ea62cfSSascha Wildner 		ucom_modem(tp, 0, SER_DTR);
69612bd3c8bSSascha Wildner 	}
69712bd3c8bSSascha Wildner }
69812bd3c8bSSascha Wildner 
69912bd3c8bSSascha Wildner /*
70012bd3c8bSSascha Wildner  * Return values:
70112bd3c8bSSascha Wildner  *    0: normal
70212bd3c8bSSascha Wildner  * else: taskqueue is draining or gone
70312bd3c8bSSascha Wildner  */
70412bd3c8bSSascha Wildner uint8_t
ucom_cfg_is_gone(struct ucom_softc * sc)70512bd3c8bSSascha Wildner ucom_cfg_is_gone(struct ucom_softc *sc)
70612bd3c8bSSascha Wildner {
70712bd3c8bSSascha Wildner 	struct ucom_super_softc *ssc = sc->sc_super;
70812bd3c8bSSascha Wildner 
70912bd3c8bSSascha Wildner 	return (usb_proc_is_gone(&ssc->sc_tq));
71012bd3c8bSSascha Wildner }
71112bd3c8bSSascha Wildner 
71212bd3c8bSSascha Wildner static void
ucom_cfg_start_transfers(struct usb_proc_msg * _task)71312bd3c8bSSascha Wildner ucom_cfg_start_transfers(struct usb_proc_msg *_task)
71412bd3c8bSSascha Wildner {
71512bd3c8bSSascha Wildner 	struct ucom_cfg_task *task =
71612bd3c8bSSascha Wildner 	    (struct ucom_cfg_task *)_task;
71712bd3c8bSSascha Wildner 	struct ucom_softc *sc = task->sc;
71812bd3c8bSSascha Wildner 
71912bd3c8bSSascha Wildner 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
72012bd3c8bSSascha Wildner 		return;
72112bd3c8bSSascha Wildner 	}
72212bd3c8bSSascha Wildner 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
72312bd3c8bSSascha Wildner 		/* TTY device closed */
72412bd3c8bSSascha Wildner 		return;
72512bd3c8bSSascha Wildner 	}
72612bd3c8bSSascha Wildner 
72712bd3c8bSSascha Wildner 	if (_task == sc->sc_last_start_xfer)
72812bd3c8bSSascha Wildner 		sc->sc_flag |= UCOM_FLAG_GP_DATA;
72912bd3c8bSSascha Wildner 
73012bd3c8bSSascha Wildner 	if (sc->sc_callback->ucom_start_read) {
73112bd3c8bSSascha Wildner 		(sc->sc_callback->ucom_start_read) (sc);
73212bd3c8bSSascha Wildner 	}
73312bd3c8bSSascha Wildner 	if (sc->sc_callback->ucom_start_write) {
73412bd3c8bSSascha Wildner 		(sc->sc_callback->ucom_start_write) (sc);
73512bd3c8bSSascha Wildner 	}
73612bd3c8bSSascha Wildner }
73712bd3c8bSSascha Wildner 
73812bd3c8bSSascha Wildner static void
ucom_start_transfers(struct ucom_softc * sc)73912bd3c8bSSascha Wildner ucom_start_transfers(struct ucom_softc *sc)
74012bd3c8bSSascha Wildner {
74112bd3c8bSSascha Wildner 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
74212bd3c8bSSascha Wildner 		return;
74312bd3c8bSSascha Wildner 	}
74412bd3c8bSSascha Wildner 	/*
74512bd3c8bSSascha Wildner 	 * Make sure that data transfers are started in both
74612bd3c8bSSascha Wildner 	 * directions:
74712bd3c8bSSascha Wildner 	 */
74812bd3c8bSSascha Wildner 	if (sc->sc_callback->ucom_start_read) {
74912bd3c8bSSascha Wildner 		(sc->sc_callback->ucom_start_read) (sc);
75012bd3c8bSSascha Wildner 	}
75112bd3c8bSSascha Wildner 	if (sc->sc_callback->ucom_start_write) {
75212bd3c8bSSascha Wildner 		(sc->sc_callback->ucom_start_write) (sc);
75312bd3c8bSSascha Wildner 	}
75412bd3c8bSSascha Wildner }
75512bd3c8bSSascha Wildner 
75612bd3c8bSSascha Wildner static void
ucom_cfg_open(struct usb_proc_msg * _task)75712bd3c8bSSascha Wildner ucom_cfg_open(struct usb_proc_msg *_task)
75812bd3c8bSSascha Wildner {
75912bd3c8bSSascha Wildner 	struct ucom_cfg_task *task =
76012bd3c8bSSascha Wildner 	    (struct ucom_cfg_task *)_task;
76112bd3c8bSSascha Wildner 	struct ucom_softc *sc = task->sc;
76212bd3c8bSSascha Wildner 
76312bd3c8bSSascha Wildner 	DPRINTF("\n");
76412bd3c8bSSascha Wildner 
76512bd3c8bSSascha Wildner 	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
76612bd3c8bSSascha Wildner 
76712bd3c8bSSascha Wildner 		/* already opened */
76812bd3c8bSSascha Wildner 
76912bd3c8bSSascha Wildner 	} else {
77012bd3c8bSSascha Wildner 
77112bd3c8bSSascha Wildner 		sc->sc_flag |= UCOM_FLAG_LL_READY;
77212bd3c8bSSascha Wildner 
77312bd3c8bSSascha Wildner 		if (sc->sc_callback->ucom_cfg_open) {
77412bd3c8bSSascha Wildner 			(sc->sc_callback->ucom_cfg_open) (sc);
77512bd3c8bSSascha Wildner 
77612bd3c8bSSascha Wildner 			/* wait a little */
777a81829f2SSascha Wildner 			usb_pause_mtx(sc->sc_lock, hz / 10);
77812bd3c8bSSascha Wildner 		}
77912bd3c8bSSascha Wildner 	}
78012bd3c8bSSascha Wildner }
78112bd3c8bSSascha Wildner 
78212bd3c8bSSascha Wildner static int
ucom_dev_open(struct dev_open_args * ap)783d95de0a4SMarkus Pfeiffer ucom_dev_open(struct dev_open_args *ap)
78412bd3c8bSSascha Wildner {
785fd10b026SMarkus Pfeiffer 	cdev_t dev = ap->a_head.a_dev;
786d95de0a4SMarkus Pfeiffer 	struct ucom_softc *sc = (struct ucom_softc *)dev->si_drv1;
78712bd3c8bSSascha Wildner 	int error;
788b040f636SMarkus Pfeiffer 	int mynor;
78912bd3c8bSSascha Wildner 
790b040f636SMarkus Pfeiffer 	error = 0;
791b040f636SMarkus Pfeiffer 	mynor = minor(dev);
792b040f636SMarkus Pfeiffer 
793d95de0a4SMarkus Pfeiffer 	UCOM_MTX_LOCK(sc);
794d95de0a4SMarkus Pfeiffer 	error = ucom_open(sc);
795d95de0a4SMarkus Pfeiffer 	UCOM_MTX_UNLOCK(sc);
796c90f1249SMatthew Dillon 
797d95de0a4SMarkus Pfeiffer 	return error;
798d95de0a4SMarkus Pfeiffer }
799d95de0a4SMarkus Pfeiffer 
800d95de0a4SMarkus Pfeiffer static int
ucom_open(struct ucom_softc * sc)801d95de0a4SMarkus Pfeiffer ucom_open(struct ucom_softc *sc)
802d95de0a4SMarkus Pfeiffer {
803d95de0a4SMarkus Pfeiffer 	int error;
804b28dae1fSMarkus Pfeiffer 	struct tty *tp;
80512bd3c8bSSascha Wildner 
806b040f636SMarkus Pfeiffer 	int mynor;
807b040f636SMarkus Pfeiffer 
808b040f636SMarkus Pfeiffer 	mynor = minor(sc->sc_cdev);
809b040f636SMarkus Pfeiffer 
81012bd3c8bSSascha Wildner 	if (sc->sc_flag & UCOM_FLAG_GONE) {
81112bd3c8bSSascha Wildner 		return (ENXIO);
81212bd3c8bSSascha Wildner 	}
81312bd3c8bSSascha Wildner 	if (sc->sc_flag & UCOM_FLAG_HL_READY) {
81412bd3c8bSSascha Wildner 		/* already opened */
81512bd3c8bSSascha Wildner 		return (0);
81612bd3c8bSSascha Wildner 	}
817d95de0a4SMarkus Pfeiffer 	DPRINTF("tp = %p\n", sc->sc_tty);
81812bd3c8bSSascha Wildner 
81912bd3c8bSSascha Wildner 	if (sc->sc_callback->ucom_pre_open) {
82012bd3c8bSSascha Wildner 		/*
82112bd3c8bSSascha Wildner 		 * give the lower layer a chance to disallow TTY open, for
82212bd3c8bSSascha Wildner 		 * example if the device is not present:
82312bd3c8bSSascha Wildner 		 */
82412bd3c8bSSascha Wildner 		error = (sc->sc_callback->ucom_pre_open) (sc);
82512bd3c8bSSascha Wildner 		if (error) {
82612bd3c8bSSascha Wildner 			return (error);
82712bd3c8bSSascha Wildner 		}
82812bd3c8bSSascha Wildner 	}
82912bd3c8bSSascha Wildner 	sc->sc_flag |= UCOM_FLAG_HL_READY;
83012bd3c8bSSascha Wildner 
831b28dae1fSMarkus Pfeiffer 	tp = sc->sc_tty;
8322efb75f3SMatthew Dillon 	lwkt_gettoken(&tp->t_token);
833b28dae1fSMarkus Pfeiffer 
834b28dae1fSMarkus Pfeiffer 	if (!ISSET(tp->t_state, TS_ISOPEN)) {
835b28dae1fSMarkus Pfeiffer 		struct termios t;
836b28dae1fSMarkus Pfeiffer 
837b28dae1fSMarkus Pfeiffer 		tp->t_dev = reference_dev(sc->sc_cdev);
838b28dae1fSMarkus Pfeiffer 
839b040f636SMarkus Pfeiffer 		t = mynor & CALLOUT_MASK ? sc->sc_it_out : sc->sc_it_in;
840b040f636SMarkus Pfeiffer 
841b28dae1fSMarkus Pfeiffer 		tp->t_ospeed = 0;
842b28dae1fSMarkus Pfeiffer 		ucom_param(tp, &t);
843b28dae1fSMarkus Pfeiffer 		tp->t_iflag = TTYDEF_IFLAG;
844b28dae1fSMarkus Pfeiffer 		tp->t_oflag = TTYDEF_OFLAG;
845b28dae1fSMarkus Pfeiffer 		tp->t_lflag = TTYDEF_LFLAG;
846b28dae1fSMarkus Pfeiffer 		ttychars(tp);
847b28dae1fSMarkus Pfeiffer 		ttsetwater(tp);
848b28dae1fSMarkus Pfeiffer 
84912bd3c8bSSascha Wildner 		/* Disable transfers */
85012bd3c8bSSascha Wildner 		sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
85112bd3c8bSSascha Wildner 
85212bd3c8bSSascha Wildner 		sc->sc_lsr = 0;
85312bd3c8bSSascha Wildner 		sc->sc_msr = 0;
85412bd3c8bSSascha Wildner 		sc->sc_mcr = 0;
85512bd3c8bSSascha Wildner 
85612bd3c8bSSascha Wildner 		/* reset programmed line state */
85712bd3c8bSSascha Wildner 		sc->sc_pls_curr = 0;
85812bd3c8bSSascha Wildner 		sc->sc_pls_set = 0;
85912bd3c8bSSascha Wildner 		sc->sc_pls_clr = 0;
86012bd3c8bSSascha Wildner 
86118a80dc3SMarkus Pfeiffer 		/* reset jitter buffer */
86218a80dc3SMarkus Pfeiffer 		sc->sc_jitterbuf_in = 0;
86318a80dc3SMarkus Pfeiffer 		sc->sc_jitterbuf_out = 0;
86418a80dc3SMarkus Pfeiffer 
86512bd3c8bSSascha Wildner 		ucom_queue_command(sc, ucom_cfg_open, NULL,
86612bd3c8bSSascha Wildner 				&sc->sc_open_task[0].hdr,
86712bd3c8bSSascha Wildner 				&sc->sc_open_task[1].hdr);
86812bd3c8bSSascha Wildner 
86912bd3c8bSSascha Wildner 		/* Queue transfer enable command last */
87012bd3c8bSSascha Wildner 		ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
87112bd3c8bSSascha Wildner 				&sc->sc_start_task[0].hdr,
87212bd3c8bSSascha Wildner 				&sc->sc_start_task[1].hdr);
87312bd3c8bSSascha Wildner 
87469ea62cfSSascha Wildner 		ucom_modem(sc->sc_tty, SER_DTR | SER_RTS, 0);
87512bd3c8bSSascha Wildner 
87612bd3c8bSSascha Wildner 		ucom_ring(sc, 0);
87712bd3c8bSSascha Wildner 
87812bd3c8bSSascha Wildner 		ucom_break(sc, 0);
87912bd3c8bSSascha Wildner 
88012bd3c8bSSascha Wildner 		ucom_status_change(sc);
88112bd3c8bSSascha Wildner 
882b28dae1fSMarkus Pfeiffer 		if (ISSET(sc->sc_msr, SER_DCD)) {
883b28dae1fSMarkus Pfeiffer 			(*linesw[tp->t_line].l_modem)(tp, 1);
884b28dae1fSMarkus Pfeiffer 		}
885b28dae1fSMarkus Pfeiffer 	}
886b28dae1fSMarkus Pfeiffer 
887b28dae1fSMarkus Pfeiffer 	error = ttyopen(sc->sc_cdev, tp);
888b28dae1fSMarkus Pfeiffer 	if (error) {
8892efb75f3SMatthew Dillon 		lwkt_reltoken(&tp->t_token);
890b28dae1fSMarkus Pfeiffer 		return (error);
891b28dae1fSMarkus Pfeiffer 	}
892b28dae1fSMarkus Pfeiffer 
893b28dae1fSMarkus Pfeiffer 	error = (*linesw[tp->t_line].l_open)(sc->sc_cdev, tp);
894b28dae1fSMarkus Pfeiffer 	if (error) {
8952efb75f3SMatthew Dillon 		lwkt_reltoken(&tp->t_token);
896b28dae1fSMarkus Pfeiffer 		return (error);
897b28dae1fSMarkus Pfeiffer 	}
898b28dae1fSMarkus Pfeiffer 
899b28dae1fSMarkus Pfeiffer 	disc_optim(tp, &tp->t_termios, sc);
900b28dae1fSMarkus Pfeiffer 
9012efb75f3SMatthew Dillon 	lwkt_reltoken(&tp->t_token);
902b28dae1fSMarkus Pfeiffer 
90312bd3c8bSSascha Wildner 	return (0);
90412bd3c8bSSascha Wildner }
90512bd3c8bSSascha Wildner 
90612bd3c8bSSascha Wildner static void
ucom_cfg_close(struct usb_proc_msg * _task)90712bd3c8bSSascha Wildner ucom_cfg_close(struct usb_proc_msg *_task)
90812bd3c8bSSascha Wildner {
90912bd3c8bSSascha Wildner 	struct ucom_cfg_task *task =
91012bd3c8bSSascha Wildner 	    (struct ucom_cfg_task *)_task;
91112bd3c8bSSascha Wildner 	struct ucom_softc *sc = task->sc;
91212bd3c8bSSascha Wildner 
91312bd3c8bSSascha Wildner 	DPRINTF("\n");
91412bd3c8bSSascha Wildner 
91512bd3c8bSSascha Wildner 	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
91612bd3c8bSSascha Wildner 		sc->sc_flag &= ~UCOM_FLAG_LL_READY;
91712bd3c8bSSascha Wildner 		if (sc->sc_callback->ucom_cfg_close)
91812bd3c8bSSascha Wildner 			(sc->sc_callback->ucom_cfg_close) (sc);
91912bd3c8bSSascha Wildner 	} else {
92012bd3c8bSSascha Wildner 		/* already closed */
92112bd3c8bSSascha Wildner 	}
92212bd3c8bSSascha Wildner }
92312bd3c8bSSascha Wildner 
924fd10b026SMarkus Pfeiffer static int
ucom_dev_close(struct dev_close_args * ap)925d95de0a4SMarkus Pfeiffer ucom_dev_close(struct dev_close_args *ap)
92612bd3c8bSSascha Wildner {
927d95de0a4SMarkus Pfeiffer 	cdev_t dev = ap->a_head.a_dev;
928d95de0a4SMarkus Pfeiffer 	struct ucom_softc *sc = (struct ucom_softc *)dev->si_drv1;
929d95de0a4SMarkus Pfeiffer 	int error;
93012bd3c8bSSascha Wildner 
931d95de0a4SMarkus Pfeiffer 	UCOM_MTX_LOCK(sc);
932d95de0a4SMarkus Pfeiffer 	error = ucom_close(sc);
933d95de0a4SMarkus Pfeiffer 	UCOM_MTX_UNLOCK(sc);
934d95de0a4SMarkus Pfeiffer 
935d95de0a4SMarkus Pfeiffer 	return error;
936d95de0a4SMarkus Pfeiffer }
937d95de0a4SMarkus Pfeiffer 
938d95de0a4SMarkus Pfeiffer static int
ucom_close(struct ucom_softc * sc)939d95de0a4SMarkus Pfeiffer ucom_close(struct ucom_softc *sc)
940d95de0a4SMarkus Pfeiffer {
941d95de0a4SMarkus Pfeiffer 	struct tty *tp = sc->sc_tty;
942d95de0a4SMarkus Pfeiffer 	int error = 0;
94312bd3c8bSSascha Wildner 
94412bd3c8bSSascha Wildner 	DPRINTF("tp=%p\n", tp);
94512bd3c8bSSascha Wildner 
94612bd3c8bSSascha Wildner 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
94712bd3c8bSSascha Wildner 		DPRINTF("tp=%p already closed\n", tp);
948d95de0a4SMarkus Pfeiffer 		return (error);
94912bd3c8bSSascha Wildner 	}
950b28dae1fSMarkus Pfeiffer 	if (!ISSET(tp->t_state, TS_ISOPEN)) {
951b28dae1fSMarkus Pfeiffer 		return(error);
952b28dae1fSMarkus Pfeiffer 	}
95312bd3c8bSSascha Wildner 	ucom_shutdown(sc);
95412bd3c8bSSascha Wildner 
95512bd3c8bSSascha Wildner 	ucom_queue_command(sc, ucom_cfg_close, NULL,
95612bd3c8bSSascha Wildner 	    &sc->sc_close_task[0].hdr,
95712bd3c8bSSascha Wildner 	    &sc->sc_close_task[1].hdr);
95812bd3c8bSSascha Wildner 
95912bd3c8bSSascha Wildner 	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
96012bd3c8bSSascha Wildner 
96112bd3c8bSSascha Wildner 	if (sc->sc_callback->ucom_stop_read) {
96212bd3c8bSSascha Wildner 		(sc->sc_callback->ucom_stop_read) (sc);
96312bd3c8bSSascha Wildner 	}
964d95de0a4SMarkus Pfeiffer 
9652efb75f3SMatthew Dillon 	lwkt_gettoken(&tp->t_token);
966b28dae1fSMarkus Pfeiffer 	(*linesw[tp->t_line].l_close)(tp, 0); /* XXX: flags */
967b28dae1fSMarkus Pfeiffer 	disc_optim(tp, &tp->t_termios, sc);
968b28dae1fSMarkus Pfeiffer 	ttyclose(tp);
969b28dae1fSMarkus Pfeiffer 
970b28dae1fSMarkus Pfeiffer 	if (tp->t_dev) {
971b28dae1fSMarkus Pfeiffer 		release_dev(tp->t_dev);
972b28dae1fSMarkus Pfeiffer 		tp->t_dev = NULL;
973b28dae1fSMarkus Pfeiffer 	}
974b28dae1fSMarkus Pfeiffer 	/* XXX: Detach wakeup */
9752efb75f3SMatthew Dillon 	lwkt_reltoken(&tp->t_token);
976b28dae1fSMarkus Pfeiffer 
977d95de0a4SMarkus Pfeiffer 	return (error);
97812bd3c8bSSascha Wildner }
97912bd3c8bSSascha Wildner 
98069ea62cfSSascha Wildner #if 0 /* XXX */
98118a80dc3SMarkus Pfeiffer static void
98218a80dc3SMarkus Pfeiffer ucom_inwakeup(struct tty *tp)
98318a80dc3SMarkus Pfeiffer {
98418a80dc3SMarkus Pfeiffer 	struct ucom_softc *sc = tty_softc(tp);
98518a80dc3SMarkus Pfeiffer 	uint16_t pos;
98618a80dc3SMarkus Pfeiffer 
98718a80dc3SMarkus Pfeiffer 	if (sc == NULL)
98818a80dc3SMarkus Pfeiffer 		return;
98918a80dc3SMarkus Pfeiffer 
99018a80dc3SMarkus Pfeiffer 	UCOM_MTX_ASSERT(sc, MA_OWNED);
99118a80dc3SMarkus Pfeiffer 
99218a80dc3SMarkus Pfeiffer 	DPRINTF("tp=%p\n", tp);
99318a80dc3SMarkus Pfeiffer 
99418a80dc3SMarkus Pfeiffer 	if (ttydisc_can_bypass(tp) != 0 ||
99518a80dc3SMarkus Pfeiffer 	    (sc->sc_flag & UCOM_FLAG_HL_READY) == 0 ||
99618a80dc3SMarkus Pfeiffer 	    (sc->sc_flag & UCOM_FLAG_INWAKEUP) != 0) {
99718a80dc3SMarkus Pfeiffer 		return;
99818a80dc3SMarkus Pfeiffer 	}
99918a80dc3SMarkus Pfeiffer 
100018a80dc3SMarkus Pfeiffer 	/* prevent recursion */
100118a80dc3SMarkus Pfeiffer 	sc->sc_flag |= UCOM_FLAG_INWAKEUP;
100218a80dc3SMarkus Pfeiffer 
100318a80dc3SMarkus Pfeiffer 	pos = sc->sc_jitterbuf_out;
100418a80dc3SMarkus Pfeiffer 
100518a80dc3SMarkus Pfeiffer 	while (sc->sc_jitterbuf_in != pos) {
100618a80dc3SMarkus Pfeiffer 		int c;
100718a80dc3SMarkus Pfeiffer 
100818a80dc3SMarkus Pfeiffer 		c = (char)sc->sc_jitterbuf[pos];
100918a80dc3SMarkus Pfeiffer 
101018a80dc3SMarkus Pfeiffer 		if (ttydisc_rint(tp, c, 0) == -1)
101118a80dc3SMarkus Pfeiffer 			break;
101218a80dc3SMarkus Pfeiffer 		pos++;
101318a80dc3SMarkus Pfeiffer 		if (pos >= UCOM_JITTERBUF_SIZE)
101418a80dc3SMarkus Pfeiffer 			pos -= UCOM_JITTERBUF_SIZE;
101518a80dc3SMarkus Pfeiffer 	}
101618a80dc3SMarkus Pfeiffer 
101718a80dc3SMarkus Pfeiffer 	sc->sc_jitterbuf_out = pos;
101818a80dc3SMarkus Pfeiffer 
101918a80dc3SMarkus Pfeiffer 	/* clear RTS in async fashion */
102018a80dc3SMarkus Pfeiffer 	if ((sc->sc_jitterbuf_in == pos) &&
102118a80dc3SMarkus Pfeiffer 	    (sc->sc_flag & UCOM_FLAG_RTS_IFLOW))
102218a80dc3SMarkus Pfeiffer 		ucom_rts(sc, 0);
102318a80dc3SMarkus Pfeiffer 
102418a80dc3SMarkus Pfeiffer 	sc->sc_flag &= ~UCOM_FLAG_INWAKEUP;
102518a80dc3SMarkus Pfeiffer }
102669ea62cfSSascha Wildner #endif
102718a80dc3SMarkus Pfeiffer 
102812bd3c8bSSascha Wildner static int
ucom_dev_read(struct dev_read_args * ap)1029d95de0a4SMarkus Pfeiffer ucom_dev_read(struct dev_read_args *ap)
103012bd3c8bSSascha Wildner {
1031d95de0a4SMarkus Pfeiffer 	cdev_t dev = ap->a_head.a_dev;
1032d95de0a4SMarkus Pfeiffer 	struct ucom_softc *sc;
1033d95de0a4SMarkus Pfeiffer 	struct tty *tp;
103412bd3c8bSSascha Wildner 	int error;
103512bd3c8bSSascha Wildner 
103675d796baSSascha Wildner 	sc = NULL;
10378c494ac4SMarkus Pfeiffer 
10388c494ac4SMarkus Pfeiffer 	tp = dev->si_tty;
10392efb75f3SMatthew Dillon 	lwkt_gettoken(&tp->t_token);
10408c494ac4SMarkus Pfeiffer 	KKASSERT(tp!=NULL);
10418c494ac4SMarkus Pfeiffer 	sc = tp->t_sc;
10428c494ac4SMarkus Pfeiffer 	KKASSERT(sc!=NULL);
1043d95de0a4SMarkus Pfeiffer 
1044b28dae1fSMarkus Pfeiffer 	DPRINTF("tp = %p, flag = 0x%x\n", tp, ap->a_ioflag);
10458c494ac4SMarkus Pfeiffer 
1046c90f1249SMatthew Dillon 	/* must not be locked in case it blocks */
1047c90f1249SMatthew Dillon 	/*UCOM_MTX_LOCK(sc);*/
1048d95de0a4SMarkus Pfeiffer 	error = (*linesw[tp->t_line].l_read)(tp, ap->a_uio, ap->a_ioflag);
1049c90f1249SMatthew Dillon 	/*UCOM_MTX_UNLOCK(sc);*/
1050d95de0a4SMarkus Pfeiffer 
10512efb75f3SMatthew Dillon 	lwkt_reltoken(&tp->t_token);
1052b28dae1fSMarkus Pfeiffer 	DPRINTF("error = %d\n", error);
1053d95de0a4SMarkus Pfeiffer 
1054d95de0a4SMarkus Pfeiffer 	return (error);
1055d95de0a4SMarkus Pfeiffer }
1056d95de0a4SMarkus Pfeiffer 
1057d95de0a4SMarkus Pfeiffer static int
ucom_dev_write(struct dev_write_args * ap)1058d95de0a4SMarkus Pfeiffer ucom_dev_write(struct dev_write_args *ap)
1059d95de0a4SMarkus Pfeiffer {
1060d95de0a4SMarkus Pfeiffer 	cdev_t dev = ap->a_head.a_dev;
1061d95de0a4SMarkus Pfeiffer 	struct ucom_softc *sc;
1062d95de0a4SMarkus Pfeiffer 	struct tty *tp;
1063d95de0a4SMarkus Pfeiffer 	int error;
1064d95de0a4SMarkus Pfeiffer 
10658c494ac4SMarkus Pfeiffer 	tp = dev->si_tty;
10662efb75f3SMatthew Dillon 	lwkt_gettoken(&tp->t_token);
10678c494ac4SMarkus Pfeiffer 	KKASSERT(tp!=NULL);
10688c494ac4SMarkus Pfeiffer 	sc = tp->t_sc;
10698c494ac4SMarkus Pfeiffer 	KKASSERT(sc!=NULL);
1070d95de0a4SMarkus Pfeiffer 
1071b28dae1fSMarkus Pfeiffer 	DPRINTF("tp = %p, flag = 0x%x\n", tp, ap->a_ioflag);
1072d95de0a4SMarkus Pfeiffer 
1073c90f1249SMatthew Dillon 	/* must not be locked in case it blocks */
1074c90f1249SMatthew Dillon 	/*UCOM_MTX_LOCK(sc);*/
1075d95de0a4SMarkus Pfeiffer 	error = (*linesw[tp->t_line].l_write)(tp, ap->a_uio, ap->a_ioflag);
1076c90f1249SMatthew Dillon 	/*UCOM_MTX_UNLOCK(sc);*/
1077d95de0a4SMarkus Pfeiffer 
10782efb75f3SMatthew Dillon 	lwkt_reltoken(&tp->t_token);
1079d95de0a4SMarkus Pfeiffer 	DPRINTF("ucomwrite: error = %d\n", error);
1080d95de0a4SMarkus Pfeiffer 
1081d95de0a4SMarkus Pfeiffer 	return (error);
1082d95de0a4SMarkus Pfeiffer }
1083d95de0a4SMarkus Pfeiffer 
1084d95de0a4SMarkus Pfeiffer static int
ucom_dev_ioctl(struct dev_ioctl_args * ap)1085d95de0a4SMarkus Pfeiffer ucom_dev_ioctl(struct dev_ioctl_args *ap)
1086d95de0a4SMarkus Pfeiffer {
1087d95de0a4SMarkus Pfeiffer 	cdev_t dev = ap->a_head.a_dev;
1088d95de0a4SMarkus Pfeiffer 	struct ucom_softc *sc = (struct ucom_softc *)dev->si_drv1;
1089d95de0a4SMarkus Pfeiffer 	u_long cmd = ap->a_cmd;
1090d95de0a4SMarkus Pfeiffer 	caddr_t data = ap->a_data;
1091d95de0a4SMarkus Pfeiffer 	struct tty *tp = sc->sc_tty;
1092b28dae1fSMarkus Pfeiffer 	int d;
1093d95de0a4SMarkus Pfeiffer 	int error;
1094b040f636SMarkus Pfeiffer 	int mynor;
1095d95de0a4SMarkus Pfeiffer 
1096d95de0a4SMarkus Pfeiffer 	UCOM_MTX_LOCK(sc);
10972efb75f3SMatthew Dillon 	lwkt_gettoken(&tp->t_token);
109812bd3c8bSSascha Wildner 
1099b040f636SMarkus Pfeiffer 	mynor = minor(dev);
1100b040f636SMarkus Pfeiffer 
110112bd3c8bSSascha Wildner 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
11022efb75f3SMatthew Dillon 		lwkt_reltoken(&tp->t_token);
1103c90f1249SMatthew Dillon 		UCOM_MTX_UNLOCK(sc);
110412bd3c8bSSascha Wildner 		return (EIO);
110512bd3c8bSSascha Wildner 	}
110612bd3c8bSSascha Wildner 	DPRINTF("cmd = 0x%08lx\n", cmd);
1107b040f636SMarkus Pfeiffer 	if (mynor & CONTROL_MASK) {
1108b040f636SMarkus Pfeiffer 		struct termios *ct;
1109b040f636SMarkus Pfeiffer 
1110b040f636SMarkus Pfeiffer 		switch (mynor & CONTROL_MASK) {
1111b040f636SMarkus Pfeiffer 		case CONTROL_INIT_STATE:
1112b040f636SMarkus Pfeiffer 			ct = mynor & CALLOUT_MASK ? &sc->sc_it_out : &sc->sc_it_in;
1113b040f636SMarkus Pfeiffer 			break;
1114b040f636SMarkus Pfeiffer 		case CONTROL_LOCK_STATE:
1115b040f636SMarkus Pfeiffer 			ct = mynor & CALLOUT_MASK ? &sc->sc_lt_out : &sc->sc_lt_in;
1116b040f636SMarkus Pfeiffer 			break;
1117b040f636SMarkus Pfeiffer 		default:
11182efb75f3SMatthew Dillon 			lwkt_reltoken(&tp->t_token);
1119c90f1249SMatthew Dillon 			UCOM_MTX_UNLOCK(sc);
1120b040f636SMarkus Pfeiffer 			return (ENODEV);        /* /dev/nodev */
1121b040f636SMarkus Pfeiffer 		}
1122b040f636SMarkus Pfeiffer 		switch (ap->a_cmd) {
1123b040f636SMarkus Pfeiffer 		case TIOCSETA:
1124*2b3f93eaSMatthew Dillon 			error = caps_priv_check(ap->a_cred,
1125*2b3f93eaSMatthew Dillon 						SYSCAP_RESTRICTEDROOT);
1126b040f636SMarkus Pfeiffer 			if (error != 0) {
11272efb75f3SMatthew Dillon 				lwkt_reltoken(&tp->t_token);
1128c90f1249SMatthew Dillon 				UCOM_MTX_UNLOCK(sc);
1129b040f636SMarkus Pfeiffer 				return (error);
1130b040f636SMarkus Pfeiffer 			}
1131b040f636SMarkus Pfeiffer 			*ct = *(struct termios *)data;
11322efb75f3SMatthew Dillon 			lwkt_reltoken(&tp->t_token);
1133c90f1249SMatthew Dillon 			UCOM_MTX_UNLOCK(sc);
1134b040f636SMarkus Pfeiffer 			return (0);
1135b040f636SMarkus Pfeiffer 		case TIOCGETA:
1136b040f636SMarkus Pfeiffer 			*(struct termios *)data = *ct;
11372efb75f3SMatthew Dillon 			lwkt_reltoken(&tp->t_token);
1138c90f1249SMatthew Dillon 			UCOM_MTX_UNLOCK(sc);
1139b040f636SMarkus Pfeiffer 			return (0);
1140b040f636SMarkus Pfeiffer 		case TIOCGETD:
1141b040f636SMarkus Pfeiffer 			*(int *)data = TTYDISC;
11422efb75f3SMatthew Dillon 			lwkt_reltoken(&tp->t_token);
1143c90f1249SMatthew Dillon 			UCOM_MTX_UNLOCK(sc);
1144b040f636SMarkus Pfeiffer 			return (0);
1145b040f636SMarkus Pfeiffer 		case TIOCGWINSZ:
1146b040f636SMarkus Pfeiffer 			bzero(data, sizeof(struct winsize));
11472efb75f3SMatthew Dillon 			lwkt_reltoken(&tp->t_token);
1148c90f1249SMatthew Dillon 			UCOM_MTX_UNLOCK(sc);
1149b040f636SMarkus Pfeiffer 			return (0);
1150b040f636SMarkus Pfeiffer 		default:
11512efb75f3SMatthew Dillon 			lwkt_reltoken(&tp->t_token);
1152c90f1249SMatthew Dillon 			UCOM_MTX_UNLOCK(sc);
1153b040f636SMarkus Pfeiffer 			return (ENOTTY);
1154b040f636SMarkus Pfeiffer 		}
1155b040f636SMarkus Pfeiffer 	}
115612bd3c8bSSascha Wildner 
1157d95de0a4SMarkus Pfeiffer 	error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data,
1158d95de0a4SMarkus Pfeiffer 					      ap->a_fflag, ap->a_cred);
1159d95de0a4SMarkus Pfeiffer 
1160d95de0a4SMarkus Pfeiffer 	if (error != ENOIOCTL) {
1161d95de0a4SMarkus Pfeiffer 		DPRINTF("ucomioctl: l_ioctl: error = %d\n", error);
11622efb75f3SMatthew Dillon 		lwkt_reltoken(&tp->t_token);
1163d95de0a4SMarkus Pfeiffer 		UCOM_MTX_UNLOCK(sc);
1164d95de0a4SMarkus Pfeiffer 		return (error);
1165d95de0a4SMarkus Pfeiffer 	}
1166d95de0a4SMarkus Pfeiffer 
1167d95de0a4SMarkus Pfeiffer 	error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag);
1168b28dae1fSMarkus Pfeiffer 	disc_optim(tp, &tp->t_termios, sc);
1169d95de0a4SMarkus Pfeiffer 	if (error != ENOIOCTL) {
1170d95de0a4SMarkus Pfeiffer 		DPRINTF("ucomioctl: ttioctl: error = %d\n", error);
11712efb75f3SMatthew Dillon 		lwkt_reltoken(&tp->t_token);
1172d95de0a4SMarkus Pfeiffer 		UCOM_MTX_UNLOCK(sc);
1173d95de0a4SMarkus Pfeiffer 
1174d95de0a4SMarkus Pfeiffer 		return (error);
1175d95de0a4SMarkus Pfeiffer 	}
1176d95de0a4SMarkus Pfeiffer 
1177b28dae1fSMarkus Pfeiffer 	error = 0;
1178d95de0a4SMarkus Pfeiffer 
117912bd3c8bSSascha Wildner 	switch (cmd) {
1180dda46d9eSSascha Wildner #if 0 /* XXXDF */
118112bd3c8bSSascha Wildner 	case TIOCSRING:
118212bd3c8bSSascha Wildner 		ucom_ring(sc, 1);
118312bd3c8bSSascha Wildner 		error = 0;
118412bd3c8bSSascha Wildner 		break;
118512bd3c8bSSascha Wildner 	case TIOCCRING:
118612bd3c8bSSascha Wildner 		ucom_ring(sc, 0);
118712bd3c8bSSascha Wildner 		error = 0;
118812bd3c8bSSascha Wildner 		break;
118912bd3c8bSSascha Wildner #endif
119012bd3c8bSSascha Wildner 	case TIOCSBRK:
119112bd3c8bSSascha Wildner 		ucom_break(sc, 1);
119212bd3c8bSSascha Wildner 		error = 0;
119312bd3c8bSSascha Wildner 		break;
119412bd3c8bSSascha Wildner 	case TIOCCBRK:
119512bd3c8bSSascha Wildner 		ucom_break(sc, 0);
119612bd3c8bSSascha Wildner 		error = 0;
119712bd3c8bSSascha Wildner 		break;
1198b28dae1fSMarkus Pfeiffer 	case TIOCSDTR:
1199b28dae1fSMarkus Pfeiffer 		ucom_dtr(sc, 1);
1200b28dae1fSMarkus Pfeiffer 		break;
1201b28dae1fSMarkus Pfeiffer 	case TIOCCDTR:
1202b28dae1fSMarkus Pfeiffer 		ucom_dtr(sc, 0);
1203b28dae1fSMarkus Pfeiffer 		break;
1204b28dae1fSMarkus Pfeiffer 	case TIOCMSET:
1205b28dae1fSMarkus Pfeiffer 		d = *(int *)ap->a_data;
1206b28dae1fSMarkus Pfeiffer 		DPRINTF("ucomioctl: TIOCMSET, 0x%x\n", d);
1207b28dae1fSMarkus Pfeiffer 		ucom_modem(tp, ucom_fromtio(d), 0);
1208b28dae1fSMarkus Pfeiffer 		break;
1209b28dae1fSMarkus Pfeiffer 	case TIOCMGET:
1210b28dae1fSMarkus Pfeiffer 		d = ucom_modem(tp, 0, 0);
1211b28dae1fSMarkus Pfeiffer 		DPRINTF("ucomioctl: TIOCMGET, 0x%x\n", d);
1212b28dae1fSMarkus Pfeiffer 		*(int *)ap->a_data = ucom_totio(d);
1213c90f1249SMatthew Dillon 		ucom_status_change(sc);
1214b28dae1fSMarkus Pfeiffer 		break;
1215b28dae1fSMarkus Pfeiffer 	case TIOCMBIS:
1216b28dae1fSMarkus Pfeiffer 		d = *(int *)ap->a_data;
1217b28dae1fSMarkus Pfeiffer 		ucom_modem(tp, ucom_fromtio(d), 0);
1218b28dae1fSMarkus Pfeiffer 		break;
1219b28dae1fSMarkus Pfeiffer 	case TIOCMBIC:
1220b28dae1fSMarkus Pfeiffer 		d = *(int *)ap->a_data;
1221b28dae1fSMarkus Pfeiffer 		ucom_modem(tp, 0, ucom_fromtio(d));
1222b28dae1fSMarkus Pfeiffer 		break;
122312bd3c8bSSascha Wildner 	default:
122412bd3c8bSSascha Wildner 		if (sc->sc_callback->ucom_ioctl) {
122512bd3c8bSSascha Wildner 			error = (sc->sc_callback->ucom_ioctl)
1226d95de0a4SMarkus Pfeiffer 			    (sc, cmd, data, 0, curthread);
1227d95de0a4SMarkus Pfeiffer 			if (error>=0) {
12282efb75f3SMatthew Dillon 				lwkt_reltoken(&tp->t_token);
1229d95de0a4SMarkus Pfeiffer 				UCOM_MTX_UNLOCK(sc);
1230d95de0a4SMarkus Pfeiffer 
1231d95de0a4SMarkus Pfeiffer 				return(error);
1232d95de0a4SMarkus Pfeiffer 			}
123312bd3c8bSSascha Wildner 		} else {
123412bd3c8bSSascha Wildner 			error = ENOIOCTL;
123512bd3c8bSSascha Wildner 		}
1236dd681da6SMatthew Dillon 		if (error == ENOIOCTL)
1237dd681da6SMatthew Dillon 			error = pps_ioctl(cmd, data, &sc->sc_pps);
123812bd3c8bSSascha Wildner 		break;
123912bd3c8bSSascha Wildner 	}
12402efb75f3SMatthew Dillon 	lwkt_reltoken(&tp->t_token);
1241d95de0a4SMarkus Pfeiffer 	UCOM_MTX_UNLOCK(sc);
1242d95de0a4SMarkus Pfeiffer 
124312bd3c8bSSascha Wildner 	return (error);
124412bd3c8bSSascha Wildner }
124512bd3c8bSSascha Wildner 
124612bd3c8bSSascha Wildner static int
ucom_totio(int bits)1247b28dae1fSMarkus Pfeiffer ucom_totio(int bits)
1248b28dae1fSMarkus Pfeiffer {
1249b28dae1fSMarkus Pfeiffer 	int rbits = 0;
1250b28dae1fSMarkus Pfeiffer 
1251b28dae1fSMarkus Pfeiffer 	SET(bits, TIOCM_LE);
1252b28dae1fSMarkus Pfeiffer 
1253b28dae1fSMarkus Pfeiffer 	if (ISSET(bits, SER_DTR)) {
1254b28dae1fSMarkus Pfeiffer 		SET(rbits, TIOCM_DTR);
1255b28dae1fSMarkus Pfeiffer 	}
1256b28dae1fSMarkus Pfeiffer 	if (ISSET(bits, SER_RTS)) {
1257b28dae1fSMarkus Pfeiffer 		SET(rbits, TIOCM_RTS);
1258b28dae1fSMarkus Pfeiffer 	}
1259b28dae1fSMarkus Pfeiffer 	if (ISSET(bits, SER_CTS)) {
1260b28dae1fSMarkus Pfeiffer 		SET(rbits, TIOCM_CTS);
1261b28dae1fSMarkus Pfeiffer 	}
1262b28dae1fSMarkus Pfeiffer 	if (ISSET(bits, SER_DCD)) {
1263b28dae1fSMarkus Pfeiffer 		SET(rbits, TIOCM_CD);
1264b28dae1fSMarkus Pfeiffer 	}
1265b28dae1fSMarkus Pfeiffer 	if (ISSET(bits, SER_DSR)) {
1266b28dae1fSMarkus Pfeiffer 		SET(rbits, TIOCM_DSR);
1267b28dae1fSMarkus Pfeiffer 	}
1268b28dae1fSMarkus Pfeiffer 	if (ISSET(bits, SER_RI)) {
1269b28dae1fSMarkus Pfeiffer 		SET(rbits, TIOCM_RI);
1270b28dae1fSMarkus Pfeiffer 	}
1271b28dae1fSMarkus Pfeiffer 
1272b28dae1fSMarkus Pfeiffer 	return (rbits);
1273b28dae1fSMarkus Pfeiffer }
1274b28dae1fSMarkus Pfeiffer 
1275b28dae1fSMarkus Pfeiffer static int
ucom_fromtio(int bits)1276b28dae1fSMarkus Pfeiffer ucom_fromtio(int bits)
1277b28dae1fSMarkus Pfeiffer {
1278b28dae1fSMarkus Pfeiffer 	int rbits = 0;
1279b28dae1fSMarkus Pfeiffer 
1280b28dae1fSMarkus Pfeiffer 	if (ISSET(bits, TIOCM_DTR)) {
1281b28dae1fSMarkus Pfeiffer 		SET(rbits, SER_DTR);
1282b28dae1fSMarkus Pfeiffer 	}
1283b28dae1fSMarkus Pfeiffer 	if (ISSET(bits, TIOCM_RTS)) {
1284b28dae1fSMarkus Pfeiffer 		SET(rbits, SER_RTS);
1285b28dae1fSMarkus Pfeiffer 	}
1286b28dae1fSMarkus Pfeiffer 	if (ISSET(bits, TIOCM_CTS)) {
1287b28dae1fSMarkus Pfeiffer 		SET(rbits, SER_CTS);
1288b28dae1fSMarkus Pfeiffer 	}
1289b28dae1fSMarkus Pfeiffer 	if (ISSET(bits, TIOCM_CD)) {
1290b28dae1fSMarkus Pfeiffer 		SET(rbits, SER_DCD);
1291b28dae1fSMarkus Pfeiffer 	}
1292b28dae1fSMarkus Pfeiffer 	if (ISSET(bits, TIOCM_DSR)) {
1293b28dae1fSMarkus Pfeiffer 		SET(rbits, SER_DSR);
1294b28dae1fSMarkus Pfeiffer 	}
1295b28dae1fSMarkus Pfeiffer 	if (ISSET(bits, TIOCM_RI)) {
1296b28dae1fSMarkus Pfeiffer 		SET(rbits, SER_RI);
1297b28dae1fSMarkus Pfeiffer 	}
1298b28dae1fSMarkus Pfeiffer 
1299b28dae1fSMarkus Pfeiffer 	return (rbits);
1300b28dae1fSMarkus Pfeiffer }
1301b28dae1fSMarkus Pfeiffer 
1302b28dae1fSMarkus Pfeiffer static int
ucom_modem(struct tty * tp,int sigon,int sigoff)130312bd3c8bSSascha Wildner ucom_modem(struct tty *tp, int sigon, int sigoff)
130412bd3c8bSSascha Wildner {
13058c494ac4SMarkus Pfeiffer 	struct ucom_softc *sc = (struct ucom_softc *)tp->t_sc;
130612bd3c8bSSascha Wildner 	uint8_t onoff;
130712bd3c8bSSascha Wildner 
130818a80dc3SMarkus Pfeiffer 	UCOM_MTX_ASSERT(sc, MA_OWNED);
130912bd3c8bSSascha Wildner 
131012bd3c8bSSascha Wildner 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
131112bd3c8bSSascha Wildner 		return (0);
131212bd3c8bSSascha Wildner 	}
131312bd3c8bSSascha Wildner 	if ((sigon == 0) && (sigoff == 0)) {
131412bd3c8bSSascha Wildner 
131512bd3c8bSSascha Wildner 		if (sc->sc_mcr & SER_DTR) {
131612bd3c8bSSascha Wildner 			sigon |= SER_DTR;
131712bd3c8bSSascha Wildner 		}
131812bd3c8bSSascha Wildner 		if (sc->sc_mcr & SER_RTS) {
131912bd3c8bSSascha Wildner 			sigon |= SER_RTS;
132012bd3c8bSSascha Wildner 		}
132112bd3c8bSSascha Wildner 		if (sc->sc_msr & SER_CTS) {
132212bd3c8bSSascha Wildner 			sigon |= SER_CTS;
132312bd3c8bSSascha Wildner 		}
132412bd3c8bSSascha Wildner 		if (sc->sc_msr & SER_DCD) {
132512bd3c8bSSascha Wildner 			sigon |= SER_DCD;
132612bd3c8bSSascha Wildner 		}
132712bd3c8bSSascha Wildner 		if (sc->sc_msr & SER_DSR) {
132812bd3c8bSSascha Wildner 			sigon |= SER_DSR;
132912bd3c8bSSascha Wildner 		}
133012bd3c8bSSascha Wildner 		if (sc->sc_msr & SER_RI) {
133112bd3c8bSSascha Wildner 			sigon |= SER_RI;
133212bd3c8bSSascha Wildner 		}
133312bd3c8bSSascha Wildner 		return (sigon);
133412bd3c8bSSascha Wildner 	}
133512bd3c8bSSascha Wildner 	if (sigon & SER_DTR) {
133612bd3c8bSSascha Wildner 		sc->sc_mcr |= SER_DTR;
133712bd3c8bSSascha Wildner 	}
133812bd3c8bSSascha Wildner 	if (sigoff & SER_DTR) {
133912bd3c8bSSascha Wildner 		sc->sc_mcr &= ~SER_DTR;
134012bd3c8bSSascha Wildner 	}
134112bd3c8bSSascha Wildner 	if (sigon & SER_RTS) {
134212bd3c8bSSascha Wildner 		sc->sc_mcr |= SER_RTS;
134312bd3c8bSSascha Wildner 	}
134412bd3c8bSSascha Wildner 	if (sigoff & SER_RTS) {
134512bd3c8bSSascha Wildner 		sc->sc_mcr &= ~SER_RTS;
134612bd3c8bSSascha Wildner 	}
134712bd3c8bSSascha Wildner 	onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
134812bd3c8bSSascha Wildner 	ucom_dtr(sc, onoff);
134912bd3c8bSSascha Wildner 
135012bd3c8bSSascha Wildner 	onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
135112bd3c8bSSascha Wildner 	ucom_rts(sc, onoff);
135212bd3c8bSSascha Wildner 
135312bd3c8bSSascha Wildner 	return (0);
135412bd3c8bSSascha Wildner }
135512bd3c8bSSascha Wildner 
135612bd3c8bSSascha Wildner static void
ucom_cfg_line_state(struct usb_proc_msg * _task)135712bd3c8bSSascha Wildner ucom_cfg_line_state(struct usb_proc_msg *_task)
135812bd3c8bSSascha Wildner {
135912bd3c8bSSascha Wildner 	struct ucom_cfg_task *task =
136012bd3c8bSSascha Wildner 	    (struct ucom_cfg_task *)_task;
136112bd3c8bSSascha Wildner 	struct ucom_softc *sc = task->sc;
136212bd3c8bSSascha Wildner 	uint8_t notch_bits;
136312bd3c8bSSascha Wildner 	uint8_t any_bits;
136412bd3c8bSSascha Wildner 	uint8_t prev_value;
136512bd3c8bSSascha Wildner 	uint8_t last_value;
136612bd3c8bSSascha Wildner 	uint8_t mask;
136712bd3c8bSSascha Wildner 
136812bd3c8bSSascha Wildner 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
136912bd3c8bSSascha Wildner 		return;
137012bd3c8bSSascha Wildner 	}
137112bd3c8bSSascha Wildner 
137212bd3c8bSSascha Wildner 	mask = 0;
137312bd3c8bSSascha Wildner 	/* compute callback mask */
137412bd3c8bSSascha Wildner 	if (sc->sc_callback->ucom_cfg_set_dtr)
137512bd3c8bSSascha Wildner 		mask |= UCOM_LS_DTR;
137612bd3c8bSSascha Wildner 	if (sc->sc_callback->ucom_cfg_set_rts)
137712bd3c8bSSascha Wildner 		mask |= UCOM_LS_RTS;
137812bd3c8bSSascha Wildner 	if (sc->sc_callback->ucom_cfg_set_break)
137912bd3c8bSSascha Wildner 		mask |= UCOM_LS_BREAK;
138012bd3c8bSSascha Wildner 	if (sc->sc_callback->ucom_cfg_set_ring)
138112bd3c8bSSascha Wildner 		mask |= UCOM_LS_RING;
138212bd3c8bSSascha Wildner 
138312bd3c8bSSascha Wildner 	/* compute the bits we are to program */
138412bd3c8bSSascha Wildner 	notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
138512bd3c8bSSascha Wildner 	any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
138612bd3c8bSSascha Wildner 	prev_value = sc->sc_pls_curr ^ notch_bits;
138712bd3c8bSSascha Wildner 	last_value = sc->sc_pls_curr;
138812bd3c8bSSascha Wildner 
138912bd3c8bSSascha Wildner 	/* reset programmed line state */
139012bd3c8bSSascha Wildner 	sc->sc_pls_curr = 0;
139112bd3c8bSSascha Wildner 	sc->sc_pls_set = 0;
139212bd3c8bSSascha Wildner 	sc->sc_pls_clr = 0;
139312bd3c8bSSascha Wildner 
139418a80dc3SMarkus Pfeiffer 	/* ensure that we don't lose any levels */
139512bd3c8bSSascha Wildner 	if (notch_bits & UCOM_LS_DTR)
139612bd3c8bSSascha Wildner 		sc->sc_callback->ucom_cfg_set_dtr(sc,
139712bd3c8bSSascha Wildner 		    (prev_value & UCOM_LS_DTR) ? 1 : 0);
139812bd3c8bSSascha Wildner 	if (notch_bits & UCOM_LS_RTS)
139912bd3c8bSSascha Wildner 		sc->sc_callback->ucom_cfg_set_rts(sc,
140012bd3c8bSSascha Wildner 		    (prev_value & UCOM_LS_RTS) ? 1 : 0);
140112bd3c8bSSascha Wildner 	if (notch_bits & UCOM_LS_BREAK)
140212bd3c8bSSascha Wildner 		sc->sc_callback->ucom_cfg_set_break(sc,
140312bd3c8bSSascha Wildner 		    (prev_value & UCOM_LS_BREAK) ? 1 : 0);
140412bd3c8bSSascha Wildner 	if (notch_bits & UCOM_LS_RING)
140512bd3c8bSSascha Wildner 		sc->sc_callback->ucom_cfg_set_ring(sc,
140612bd3c8bSSascha Wildner 		    (prev_value & UCOM_LS_RING) ? 1 : 0);
140712bd3c8bSSascha Wildner 
140812bd3c8bSSascha Wildner 	/* set last value */
140912bd3c8bSSascha Wildner 	if (any_bits & UCOM_LS_DTR)
141012bd3c8bSSascha Wildner 		sc->sc_callback->ucom_cfg_set_dtr(sc,
141112bd3c8bSSascha Wildner 		    (last_value & UCOM_LS_DTR) ? 1 : 0);
141212bd3c8bSSascha Wildner 	if (any_bits & UCOM_LS_RTS)
141312bd3c8bSSascha Wildner 		sc->sc_callback->ucom_cfg_set_rts(sc,
141412bd3c8bSSascha Wildner 		    (last_value & UCOM_LS_RTS) ? 1 : 0);
141512bd3c8bSSascha Wildner 	if (any_bits & UCOM_LS_BREAK)
141612bd3c8bSSascha Wildner 		sc->sc_callback->ucom_cfg_set_break(sc,
141712bd3c8bSSascha Wildner 		    (last_value & UCOM_LS_BREAK) ? 1 : 0);
141812bd3c8bSSascha Wildner 	if (any_bits & UCOM_LS_RING)
141912bd3c8bSSascha Wildner 		sc->sc_callback->ucom_cfg_set_ring(sc,
142012bd3c8bSSascha Wildner 		    (last_value & UCOM_LS_RING) ? 1 : 0);
142112bd3c8bSSascha Wildner }
142212bd3c8bSSascha Wildner 
142312bd3c8bSSascha Wildner static void
ucom_line_state(struct ucom_softc * sc,uint8_t set_bits,uint8_t clear_bits)142412bd3c8bSSascha Wildner ucom_line_state(struct ucom_softc *sc,
142512bd3c8bSSascha Wildner     uint8_t set_bits, uint8_t clear_bits)
142612bd3c8bSSascha Wildner {
142718a80dc3SMarkus Pfeiffer 	UCOM_MTX_ASSERT(sc, MA_OWNED);
142812bd3c8bSSascha Wildner 
142912bd3c8bSSascha Wildner 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
143012bd3c8bSSascha Wildner 		return;
143112bd3c8bSSascha Wildner 	}
143212bd3c8bSSascha Wildner 
143312bd3c8bSSascha Wildner 	DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
143412bd3c8bSSascha Wildner 
143512bd3c8bSSascha Wildner 	/* update current programmed line state */
143612bd3c8bSSascha Wildner 	sc->sc_pls_curr |= set_bits;
143712bd3c8bSSascha Wildner 	sc->sc_pls_curr &= ~clear_bits;
143812bd3c8bSSascha Wildner 	sc->sc_pls_set |= set_bits;
143912bd3c8bSSascha Wildner 	sc->sc_pls_clr |= clear_bits;
144012bd3c8bSSascha Wildner 
144112bd3c8bSSascha Wildner 	/* defer driver programming */
144212bd3c8bSSascha Wildner 	ucom_queue_command(sc, ucom_cfg_line_state, NULL,
144312bd3c8bSSascha Wildner 	    &sc->sc_line_state_task[0].hdr,
144412bd3c8bSSascha Wildner 	    &sc->sc_line_state_task[1].hdr);
144512bd3c8bSSascha Wildner }
144612bd3c8bSSascha Wildner 
144712bd3c8bSSascha Wildner static void
ucom_ring(struct ucom_softc * sc,uint8_t onoff)144812bd3c8bSSascha Wildner ucom_ring(struct ucom_softc *sc, uint8_t onoff)
144912bd3c8bSSascha Wildner {
145012bd3c8bSSascha Wildner 	DPRINTF("onoff = %d\n", onoff);
145112bd3c8bSSascha Wildner 
145212bd3c8bSSascha Wildner 	if (onoff)
145312bd3c8bSSascha Wildner 		ucom_line_state(sc, UCOM_LS_RING, 0);
145412bd3c8bSSascha Wildner 	else
145512bd3c8bSSascha Wildner 		ucom_line_state(sc, 0, UCOM_LS_RING);
145612bd3c8bSSascha Wildner }
145712bd3c8bSSascha Wildner 
145812bd3c8bSSascha Wildner static void
ucom_break(struct ucom_softc * sc,uint8_t onoff)145912bd3c8bSSascha Wildner ucom_break(struct ucom_softc *sc, uint8_t onoff)
146012bd3c8bSSascha Wildner {
146112bd3c8bSSascha Wildner 	DPRINTF("onoff = %d\n", onoff);
146212bd3c8bSSascha Wildner 
146312bd3c8bSSascha Wildner 	if (onoff)
146412bd3c8bSSascha Wildner 		ucom_line_state(sc, UCOM_LS_BREAK, 0);
146512bd3c8bSSascha Wildner 	else
146612bd3c8bSSascha Wildner 		ucom_line_state(sc, 0, UCOM_LS_BREAK);
146712bd3c8bSSascha Wildner }
146812bd3c8bSSascha Wildner 
146912bd3c8bSSascha Wildner static void
ucom_dtr(struct ucom_softc * sc,uint8_t onoff)147012bd3c8bSSascha Wildner ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
147112bd3c8bSSascha Wildner {
147212bd3c8bSSascha Wildner 	DPRINTF("onoff = %d\n", onoff);
147312bd3c8bSSascha Wildner 
147412bd3c8bSSascha Wildner 	if (onoff)
147512bd3c8bSSascha Wildner 		ucom_line_state(sc, UCOM_LS_DTR, 0);
147612bd3c8bSSascha Wildner 	else
147712bd3c8bSSascha Wildner 		ucom_line_state(sc, 0, UCOM_LS_DTR);
147812bd3c8bSSascha Wildner }
147912bd3c8bSSascha Wildner 
148012bd3c8bSSascha Wildner static void
ucom_rts(struct ucom_softc * sc,uint8_t onoff)148112bd3c8bSSascha Wildner ucom_rts(struct ucom_softc *sc, uint8_t onoff)
148212bd3c8bSSascha Wildner {
148312bd3c8bSSascha Wildner 	DPRINTF("onoff = %d\n", onoff);
148412bd3c8bSSascha Wildner 
148512bd3c8bSSascha Wildner 	if (onoff)
148612bd3c8bSSascha Wildner 		ucom_line_state(sc, UCOM_LS_RTS, 0);
148712bd3c8bSSascha Wildner 	else
148812bd3c8bSSascha Wildner 		ucom_line_state(sc, 0, UCOM_LS_RTS);
148912bd3c8bSSascha Wildner }
149012bd3c8bSSascha Wildner 
149112bd3c8bSSascha Wildner static void
ucom_cfg_status_change(struct usb_proc_msg * _task)149212bd3c8bSSascha Wildner ucom_cfg_status_change(struct usb_proc_msg *_task)
149312bd3c8bSSascha Wildner {
149412bd3c8bSSascha Wildner 	struct ucom_cfg_task *task =
149512bd3c8bSSascha Wildner 	    (struct ucom_cfg_task *)_task;
149612bd3c8bSSascha Wildner 	struct ucom_softc *sc = task->sc;
149712bd3c8bSSascha Wildner 	struct tty *tp;
149812bd3c8bSSascha Wildner 	uint8_t new_msr;
149912bd3c8bSSascha Wildner 	uint8_t new_lsr;
1500dd681da6SMatthew Dillon 	uint8_t msr_delta;
150112bd3c8bSSascha Wildner 	uint8_t lsr_delta;
150212bd3c8bSSascha Wildner 
150312bd3c8bSSascha Wildner 	tp = sc->sc_tty;
150412bd3c8bSSascha Wildner 
150518a80dc3SMarkus Pfeiffer 	UCOM_MTX_ASSERT(sc, MA_OWNED);
150612bd3c8bSSascha Wildner 
150712bd3c8bSSascha Wildner 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
150812bd3c8bSSascha Wildner 		return;
150912bd3c8bSSascha Wildner 	}
151012bd3c8bSSascha Wildner 	if (sc->sc_callback->ucom_cfg_get_status == NULL) {
151112bd3c8bSSascha Wildner 		return;
151212bd3c8bSSascha Wildner 	}
151312bd3c8bSSascha Wildner 	/* get status */
151412bd3c8bSSascha Wildner 
151512bd3c8bSSascha Wildner 	new_msr = 0;
151612bd3c8bSSascha Wildner 	new_lsr = 0;
151712bd3c8bSSascha Wildner 
151812bd3c8bSSascha Wildner 	(sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
151912bd3c8bSSascha Wildner 
152012bd3c8bSSascha Wildner 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
152112bd3c8bSSascha Wildner 		/* TTY device closed */
152212bd3c8bSSascha Wildner 		return;
152312bd3c8bSSascha Wildner 	}
1524dd681da6SMatthew Dillon 	msr_delta = (sc->sc_msr ^ new_msr);
152512bd3c8bSSascha Wildner 	lsr_delta = (sc->sc_lsr ^ new_lsr);
152612bd3c8bSSascha Wildner 
152712bd3c8bSSascha Wildner 	sc->sc_msr = new_msr;
152812bd3c8bSSascha Wildner 	sc->sc_lsr = new_lsr;
152912bd3c8bSSascha Wildner 
1530dd681da6SMatthew Dillon #if 0	/* missing pps_capture */
1531dd681da6SMatthew Dillon 	/*
1532dd681da6SMatthew Dillon 	 * Time pulse counting support. Note that both CTS and DCD are
1533dd681da6SMatthew Dillon 	 * active-low signals. The status bit is high to indicate that
1534dd681da6SMatthew Dillon 	 * the signal on the line is low, which corresponds to a PPS
1535dd681da6SMatthew Dillon 	 * clear event.
1536dd681da6SMatthew Dillon 	 */
1537dd681da6SMatthew Dillon 	switch(ucom_pps_mode) {
1538dd681da6SMatthew Dillon 	case 1:
1539dd681da6SMatthew Dillon 		if ((sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) &&
1540dd681da6SMatthew Dillon 		    (msr_delta & SER_CTS)) {
1541dd681da6SMatthew Dillon 			pps_capture(&sc->sc_pps);
1542dd681da6SMatthew Dillon 			pps_event(&sc->sc_pps, (sc->sc_msr & SER_CTS) ?
1543dd681da6SMatthew Dillon 			    PPS_CAPTURECLEAR : PPS_CAPTUREASSERT);
1544dd681da6SMatthew Dillon 		}
1545dd681da6SMatthew Dillon 		break;
1546dd681da6SMatthew Dillon 	case 2:
1547dd681da6SMatthew Dillon 		if ((sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) &&
1548dd681da6SMatthew Dillon 		    (msr_delta & SER_DCD)) {
1549dd681da6SMatthew Dillon 			pps_capture(&sc->sc_pps);
1550dd681da6SMatthew Dillon 			pps_event(&sc->sc_pps, (sc->sc_msr & SER_DCD) ?
1551dd681da6SMatthew Dillon 			    PPS_CAPTURECLEAR : PPS_CAPTUREASSERT);
1552dd681da6SMatthew Dillon 		}
1553dd681da6SMatthew Dillon 		break;
1554dd681da6SMatthew Dillon 	default:
1555dd681da6SMatthew Dillon 		break;
1556dd681da6SMatthew Dillon 	}
1557dd681da6SMatthew Dillon #endif
155812bd3c8bSSascha Wildner 
1559dd681da6SMatthew Dillon 	if (msr_delta & SER_DCD) {
1560dd681da6SMatthew Dillon 
1561dd681da6SMatthew Dillon 		int onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
1562dd681da6SMatthew Dillon 
156312bd3c8bSSascha Wildner 		DPRINTF("DCD changed to %d\n", onoff);
1564dd681da6SMatthew Dillon 
15658c494ac4SMarkus Pfeiffer 		(*linesw[tp->t_line].l_modem)(tp, onoff);
156612bd3c8bSSascha Wildner 	}
156712bd3c8bSSascha Wildner 
156812bd3c8bSSascha Wildner 	if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
156912bd3c8bSSascha Wildner 
157012bd3c8bSSascha Wildner 		DPRINTF("BREAK detected\n");
15718c494ac4SMarkus Pfeiffer 		(*linesw[tp->t_line].l_rint)(0, tp);
15728c494ac4SMarkus Pfeiffer 
1573fd10b026SMarkus Pfeiffer 		/*
157412bd3c8bSSascha Wildner 		ttydisc_rint(tp, 0, TRE_BREAK);
157512bd3c8bSSascha Wildner 		ttydisc_rint_done(tp);
1576fd10b026SMarkus Pfeiffer 		*/
157712bd3c8bSSascha Wildner 	}
157812bd3c8bSSascha Wildner 
157912bd3c8bSSascha Wildner 	if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) {
158012bd3c8bSSascha Wildner 
158112bd3c8bSSascha Wildner 		DPRINTF("Frame error detected\n");
15828c494ac4SMarkus Pfeiffer 		(*linesw[tp->t_line].l_rint)(0, tp);
1583b28dae1fSMarkus Pfeiffer 
1584fd10b026SMarkus Pfeiffer 		/*
158512bd3c8bSSascha Wildner 		ttydisc_rint(tp, 0, TRE_FRAMING);
158612bd3c8bSSascha Wildner 		ttydisc_rint_done(tp);
1587fd10b026SMarkus Pfeiffer 		*/
158812bd3c8bSSascha Wildner 	}
158912bd3c8bSSascha Wildner 
159012bd3c8bSSascha Wildner 	if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) {
159112bd3c8bSSascha Wildner 
159212bd3c8bSSascha Wildner 		DPRINTF("Parity error detected\n");
15938c494ac4SMarkus Pfeiffer 		(*linesw[tp->t_line].l_rint)(0, tp);
1594fd10b026SMarkus Pfeiffer 		/*
159512bd3c8bSSascha Wildner 		ttydisc_rint(tp, 0, TRE_PARITY);
159612bd3c8bSSascha Wildner 		ttydisc_rint_done(tp);
1597fd10b026SMarkus Pfeiffer 		*/
159812bd3c8bSSascha Wildner 	}
159912bd3c8bSSascha Wildner }
160012bd3c8bSSascha Wildner 
160112bd3c8bSSascha Wildner void
ucom_status_change(struct ucom_softc * sc)160212bd3c8bSSascha Wildner ucom_status_change(struct ucom_softc *sc)
160312bd3c8bSSascha Wildner {
160418a80dc3SMarkus Pfeiffer 	UCOM_MTX_ASSERT(sc, MA_OWNED);
160512bd3c8bSSascha Wildner 
160612bd3c8bSSascha Wildner 	if (sc->sc_flag & UCOM_FLAG_CONSOLE)
160712bd3c8bSSascha Wildner 		return;		/* not supported */
160812bd3c8bSSascha Wildner 
160912bd3c8bSSascha Wildner 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
161012bd3c8bSSascha Wildner 		return;
161112bd3c8bSSascha Wildner 	}
161212bd3c8bSSascha Wildner 	DPRINTF("\n");
161312bd3c8bSSascha Wildner 
161412bd3c8bSSascha Wildner 	ucom_queue_command(sc, ucom_cfg_status_change, NULL,
161512bd3c8bSSascha Wildner 	    &sc->sc_status_task[0].hdr,
161612bd3c8bSSascha Wildner 	    &sc->sc_status_task[1].hdr);
161712bd3c8bSSascha Wildner }
161812bd3c8bSSascha Wildner 
161912bd3c8bSSascha Wildner static void
ucom_cfg_param(struct usb_proc_msg * _task)162012bd3c8bSSascha Wildner ucom_cfg_param(struct usb_proc_msg *_task)
162112bd3c8bSSascha Wildner {
162212bd3c8bSSascha Wildner 	struct ucom_param_task *task =
162312bd3c8bSSascha Wildner 	    (struct ucom_param_task *)_task;
162412bd3c8bSSascha Wildner 	struct ucom_softc *sc = task->sc;
162512bd3c8bSSascha Wildner 
162612bd3c8bSSascha Wildner 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
162712bd3c8bSSascha Wildner 		return;
162812bd3c8bSSascha Wildner 	}
162912bd3c8bSSascha Wildner 	if (sc->sc_callback->ucom_cfg_param == NULL) {
163012bd3c8bSSascha Wildner 		return;
163112bd3c8bSSascha Wildner 	}
163212bd3c8bSSascha Wildner 
163312bd3c8bSSascha Wildner 	(sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
163412bd3c8bSSascha Wildner 
163512bd3c8bSSascha Wildner 	/* wait a little */
1636a81829f2SSascha Wildner 	usb_pause_mtx(sc->sc_lock, hz / 10);
163712bd3c8bSSascha Wildner }
163812bd3c8bSSascha Wildner 
163912bd3c8bSSascha Wildner static int
ucom_param(struct tty * tp,struct termios * t)164012bd3c8bSSascha Wildner ucom_param(struct tty *tp, struct termios *t)
164112bd3c8bSSascha Wildner {
16428c494ac4SMarkus Pfeiffer 	struct ucom_softc *sc = (struct ucom_softc *)tp->t_sc;
164312bd3c8bSSascha Wildner 	uint8_t opened;
164412bd3c8bSSascha Wildner 	int error;
164512bd3c8bSSascha Wildner 
16462efb75f3SMatthew Dillon 	lwkt_gettoken(&tp->t_token);
164718a80dc3SMarkus Pfeiffer 	UCOM_MTX_ASSERT(sc, MA_OWNED);
164812bd3c8bSSascha Wildner 
164912bd3c8bSSascha Wildner 	opened = 0;
165012bd3c8bSSascha Wildner 	error = 0;
165112bd3c8bSSascha Wildner 
165212bd3c8bSSascha Wildner 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
165312bd3c8bSSascha Wildner 
165412bd3c8bSSascha Wildner 		/* XXX the TTY layer should call "open()" first! */
165518a80dc3SMarkus Pfeiffer 		/*
165618a80dc3SMarkus Pfeiffer 		 * Not quite: Its ordering is partly backwards, but
165718a80dc3SMarkus Pfeiffer 		 * some parameters must be set early in ttydev_open(),
165818a80dc3SMarkus Pfeiffer 		 * possibly before calling ttydevsw_open().
165918a80dc3SMarkus Pfeiffer 		 */
1660d95de0a4SMarkus Pfeiffer 		error = ucom_open(sc);
1661d95de0a4SMarkus Pfeiffer 
166212bd3c8bSSascha Wildner 		if (error) {
166312bd3c8bSSascha Wildner 			goto done;
166412bd3c8bSSascha Wildner 		}
166512bd3c8bSSascha Wildner 		opened = 1;
166612bd3c8bSSascha Wildner 	}
166712bd3c8bSSascha Wildner 	DPRINTF("sc = %p\n", sc);
166812bd3c8bSSascha Wildner 
166912bd3c8bSSascha Wildner 	/* Check requested parameters. */
167012bd3c8bSSascha Wildner 	if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
167118a80dc3SMarkus Pfeiffer 		/* XXX c_ospeed == 0 is perfectly valid. */
167212bd3c8bSSascha Wildner 		DPRINTF("mismatch ispeed and ospeed\n");
167312bd3c8bSSascha Wildner 		error = EINVAL;
167412bd3c8bSSascha Wildner 		goto done;
167512bd3c8bSSascha Wildner 	}
167612bd3c8bSSascha Wildner 	t->c_ispeed = t->c_ospeed;
167712bd3c8bSSascha Wildner 
167812bd3c8bSSascha Wildner 	if (sc->sc_callback->ucom_pre_param) {
167912bd3c8bSSascha Wildner 		/* Let the lower layer verify the parameters */
168012bd3c8bSSascha Wildner 		error = (sc->sc_callback->ucom_pre_param) (sc, t);
168112bd3c8bSSascha Wildner 		if (error) {
168212bd3c8bSSascha Wildner 			DPRINTF("callback error = %d\n", error);
168312bd3c8bSSascha Wildner 			goto done;
168412bd3c8bSSascha Wildner 		}
168512bd3c8bSSascha Wildner 	}
168612bd3c8bSSascha Wildner 
168712bd3c8bSSascha Wildner 	/* Disable transfers */
168812bd3c8bSSascha Wildner 	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
168912bd3c8bSSascha Wildner 
169012bd3c8bSSascha Wildner 	/* Queue baud rate programming command first */
169112bd3c8bSSascha Wildner 	ucom_queue_command(sc, ucom_cfg_param, t,
169212bd3c8bSSascha Wildner 	    &sc->sc_param_task[0].hdr,
169312bd3c8bSSascha Wildner 	    &sc->sc_param_task[1].hdr);
169412bd3c8bSSascha Wildner 
169512bd3c8bSSascha Wildner 	/* Queue transfer enable command last */
169612bd3c8bSSascha Wildner 	ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
169712bd3c8bSSascha Wildner 	    &sc->sc_start_task[0].hdr,
169812bd3c8bSSascha Wildner 	    &sc->sc_start_task[1].hdr);
169912bd3c8bSSascha Wildner 
170012bd3c8bSSascha Wildner 	if (t->c_cflag & CRTS_IFLOW) {
170112bd3c8bSSascha Wildner 		sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
170212bd3c8bSSascha Wildner 	} else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
170312bd3c8bSSascha Wildner 		sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
170412bd3c8bSSascha Wildner 		ucom_modem(tp, SER_RTS, 0);
170512bd3c8bSSascha Wildner 	}
170612bd3c8bSSascha Wildner done:
170712bd3c8bSSascha Wildner 	if (error) {
170812bd3c8bSSascha Wildner 		if (opened) {
1709d95de0a4SMarkus Pfeiffer 			ucom_close(sc);
171012bd3c8bSSascha Wildner 		}
171112bd3c8bSSascha Wildner 	}
17122efb75f3SMatthew Dillon 	lwkt_reltoken(&tp->t_token);
1713d95de0a4SMarkus Pfeiffer 
171412bd3c8bSSascha Wildner 	return (error);
171512bd3c8bSSascha Wildner }
171612bd3c8bSSascha Wildner 
171712bd3c8bSSascha Wildner static void
ucom_start(struct tty * tp)1718d95de0a4SMarkus Pfeiffer ucom_start(struct tty *tp)
171912bd3c8bSSascha Wildner {
17208c494ac4SMarkus Pfeiffer 	struct ucom_softc *sc = (struct ucom_softc *)tp->t_sc;
1721c90f1249SMatthew Dillon 	int didlock;
172212bd3c8bSSascha Wildner 
1723c90f1249SMatthew Dillon 	/* may be called locked or unlocked */
1724c90f1249SMatthew Dillon 	if (lockowned(sc->sc_lock)) {
1725c90f1249SMatthew Dillon 		didlock = 0;
1726c90f1249SMatthew Dillon 	} else {
1727c90f1249SMatthew Dillon 		UCOM_MTX_LOCK(sc);
1728c90f1249SMatthew Dillon 		didlock = 1;
1729c90f1249SMatthew Dillon 	}
1730c90f1249SMatthew Dillon 	/* UCOM_MTX_ASSERT(sc, MA_OWNED); */
173112bd3c8bSSascha Wildner 
173212bd3c8bSSascha Wildner 	DPRINTF("sc = %p\n", sc);
173312bd3c8bSSascha Wildner 
173412bd3c8bSSascha Wildner 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
173512bd3c8bSSascha Wildner 		/* The higher layer is not ready */
1736c90f1249SMatthew Dillon 		if (didlock)
1737c90f1249SMatthew Dillon 			UCOM_MTX_UNLOCK(sc);
173812bd3c8bSSascha Wildner 		return;
173912bd3c8bSSascha Wildner 	}
1740b28dae1fSMarkus Pfeiffer 
17412efb75f3SMatthew Dillon 	lwkt_gettoken(&tp->t_token);
1742b28dae1fSMarkus Pfeiffer 
1743b28dae1fSMarkus Pfeiffer 	if (tp->t_state & TS_TBLOCK) {
1744b28dae1fSMarkus Pfeiffer 		if (ISSET(sc->sc_mcr, SER_RTS) &&
1745b28dae1fSMarkus Pfeiffer 		    ISSET(sc->sc_flag, UCOM_FLAG_RTS_IFLOW)) {
1746b28dae1fSMarkus Pfeiffer 			DPRINTF("ucom_start: clear RTS\n");
1747b28dae1fSMarkus Pfeiffer 			(void)ucom_modem(tp, 0, SER_RTS);
1748b28dae1fSMarkus Pfeiffer 		}
1749b28dae1fSMarkus Pfeiffer 	} else {
1750b28dae1fSMarkus Pfeiffer 		if (!ISSET(sc->sc_mcr, SER_RTS) &&
1751b28dae1fSMarkus Pfeiffer 		    tp->t_rawq.c_cc <= tp->t_ilowat &&
1752b28dae1fSMarkus Pfeiffer 		    ISSET(sc->sc_flag, UCOM_FLAG_RTS_IFLOW)) {
1753b28dae1fSMarkus Pfeiffer 			DPRINTF("ucom_start: set RTS\n");
1754b28dae1fSMarkus Pfeiffer 			(void)ucom_modem(tp, SER_RTS, 0);
1755b28dae1fSMarkus Pfeiffer 		}
1756b28dae1fSMarkus Pfeiffer 	}
1757b28dae1fSMarkus Pfeiffer 
1758b28dae1fSMarkus Pfeiffer 	if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) {
1759b28dae1fSMarkus Pfeiffer 		ttwwakeup(tp);
1760b28dae1fSMarkus Pfeiffer 		DPRINTF("ucom_start: stopped\n");
1761b28dae1fSMarkus Pfeiffer 		goto out;
1762b28dae1fSMarkus Pfeiffer 	}
1763b28dae1fSMarkus Pfeiffer 
1764b28dae1fSMarkus Pfeiffer 	if (tp->t_outq.c_cc <= tp->t_olowat) {
1765b28dae1fSMarkus Pfeiffer 		if (ISSET(tp->t_state, TS_SO_OLOWAT)) {
1766b28dae1fSMarkus Pfeiffer 			CLR(tp->t_state, TS_SO_OLOWAT);
1767b28dae1fSMarkus Pfeiffer 			wakeup(TSA_OLOWAT(tp));
1768b28dae1fSMarkus Pfeiffer 		}
1769b28dae1fSMarkus Pfeiffer 		KNOTE(&tp->t_wkq.ki_note, 0);
1770b28dae1fSMarkus Pfeiffer 		if (tp->t_outq.c_cc == 0) {
1771b28dae1fSMarkus Pfeiffer 			if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
1772b28dae1fSMarkus Pfeiffer 					TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
1773b28dae1fSMarkus Pfeiffer 				CLR(tp->t_state, TS_SO_OCOMPLETE);
1774b28dae1fSMarkus Pfeiffer 				wakeup(TSA_OCOMPLETE(tp));
1775b28dae1fSMarkus Pfeiffer 			}
1776b28dae1fSMarkus Pfeiffer 			goto out;
1777b28dae1fSMarkus Pfeiffer 		}
1778b28dae1fSMarkus Pfeiffer 	}
1779b28dae1fSMarkus Pfeiffer 
1780b28dae1fSMarkus Pfeiffer 	DPRINTF("about to start write?\n");
178112bd3c8bSSascha Wildner 	ucom_start_transfers(sc);
1782b28dae1fSMarkus Pfeiffer 
1783b28dae1fSMarkus Pfeiffer 	ttwwakeup(tp);
1784b28dae1fSMarkus Pfeiffer 
1785b28dae1fSMarkus Pfeiffer out:
17862efb75f3SMatthew Dillon 	lwkt_reltoken(&tp->t_token);
1787c90f1249SMatthew Dillon 	if (didlock)
1788c90f1249SMatthew Dillon 		UCOM_MTX_UNLOCK(sc);
1789d95de0a4SMarkus Pfeiffer }
1790d95de0a4SMarkus Pfeiffer 
1791d95de0a4SMarkus Pfeiffer static void
ucom_stop(struct tty * tp,int flag)1792b28dae1fSMarkus Pfeiffer ucom_stop(struct tty *tp, int flag)
1793d95de0a4SMarkus Pfeiffer {
1794b28dae1fSMarkus Pfeiffer 	struct ucom_softc *sc = (struct ucom_softc *)tp->t_sc;
1795b28dae1fSMarkus Pfeiffer 
1796b28dae1fSMarkus Pfeiffer 	DPRINTF("sc = %p, x = 0x%x\n", sc, flag);
1797b28dae1fSMarkus Pfeiffer 
17982efb75f3SMatthew Dillon 	lwkt_gettoken(&tp->t_token);
1799b28dae1fSMarkus Pfeiffer 	if (flag & FREAD) {
1800b28dae1fSMarkus Pfeiffer 		/*
1801b28dae1fSMarkus Pfeiffer 		 * This is just supposed to flush pending receive data,
1802b28dae1fSMarkus Pfeiffer 		 * not stop the reception of data entirely!
1803b28dae1fSMarkus Pfeiffer 		 */
1804b28dae1fSMarkus Pfeiffer 		DPRINTF("read\n");
1805b28dae1fSMarkus Pfeiffer 		if (sc->sc_callback->ucom_stop_read) {
1806b28dae1fSMarkus Pfeiffer 			(sc->sc_callback->ucom_stop_read) (sc);
1807b28dae1fSMarkus Pfeiffer 		}
1808b28dae1fSMarkus Pfeiffer 		if (sc->sc_callback->ucom_start_read) {
1809b28dae1fSMarkus Pfeiffer 			(sc->sc_callback->ucom_start_read) (sc);
1810b28dae1fSMarkus Pfeiffer 		}
181115130067Szrj #if 0
181215130067Szrj 		ucomstopread(sc);
1813b28dae1fSMarkus Pfeiffer 		ucomstartread(sc);
181415130067Szrj #endif
18153b964699Szrj 	}
1816b28dae1fSMarkus Pfeiffer 
1817b28dae1fSMarkus Pfeiffer 	if (flag & FWRITE) {
1818b28dae1fSMarkus Pfeiffer 		DPRINTF("write\n");
1819b28dae1fSMarkus Pfeiffer 		if (ISSET(tp->t_state, TS_BUSY)) {
1820b28dae1fSMarkus Pfeiffer 			/* XXX do what? */
1821b28dae1fSMarkus Pfeiffer 			if (!ISSET(tp->t_state, TS_TTSTOP))
1822b28dae1fSMarkus Pfeiffer 				SET(tp->t_state, TS_FLUSH);
1823b28dae1fSMarkus Pfeiffer 		}
1824b28dae1fSMarkus Pfeiffer 	}
1825b28dae1fSMarkus Pfeiffer 
1826b28dae1fSMarkus Pfeiffer 	DPRINTF("done\n");
18272efb75f3SMatthew Dillon 	lwkt_reltoken(&tp->t_token);
182812bd3c8bSSascha Wildner }
182912bd3c8bSSascha Wildner 
183012bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
183112bd3c8bSSascha Wildner  *	ucom_get_data
18328c494ac4SMarkus Pfeiffer  * Input values:
18338c494ac4SMarkus Pfeiffer  * len: maximum length of data to get
183412bd3c8bSSascha Wildner  *
1835b28dae1fSMarkus Pfeiffer  * Get data from the TTY layer
1836b28dae1fSMarkus Pfeiffer  *
183712bd3c8bSSascha Wildner  * Return values:
183812bd3c8bSSascha Wildner  * 0: No data is available.
183912bd3c8bSSascha Wildner  * Else: Data is available.
184012bd3c8bSSascha Wildner  *------------------------------------------------------------------------*/
1841b28dae1fSMarkus Pfeiffer 
1842b28dae1fSMarkus Pfeiffer /* Copy data from the tty layer to usb */
184312bd3c8bSSascha Wildner uint8_t
ucom_get_data(struct ucom_softc * sc,struct usb_page_cache * pc,uint32_t offset,uint32_t len,uint32_t * actlen)184412bd3c8bSSascha Wildner ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
184512bd3c8bSSascha Wildner     uint32_t offset, uint32_t len, uint32_t *actlen)
184612bd3c8bSSascha Wildner {
184712bd3c8bSSascha Wildner 	struct usb_page_search res;
184812bd3c8bSSascha Wildner 	struct tty *tp = sc->sc_tty;
184912bd3c8bSSascha Wildner 	uint32_t cnt;
185012bd3c8bSSascha Wildner 	uint32_t offset_orig;
1851b28dae1fSMarkus Pfeiffer 
1852b28dae1fSMarkus Pfeiffer 	DPRINTF("\n");
185312bd3c8bSSascha Wildner 
185418a80dc3SMarkus Pfeiffer 	UCOM_MTX_ASSERT(sc, MA_OWNED);
18553b964699Szrj 
185612bd3c8bSSascha Wildner 	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
185712bd3c8bSSascha Wildner 		unsigned int temp;
185812bd3c8bSSascha Wildner 
185912bd3c8bSSascha Wildner 		/* get total TX length */
186012bd3c8bSSascha Wildner 
186112bd3c8bSSascha Wildner 		temp = ucom_cons_tx_high - ucom_cons_tx_low;
186212bd3c8bSSascha Wildner 		temp %= UCOM_CONS_BUFSIZE;
186312bd3c8bSSascha Wildner 
186412bd3c8bSSascha Wildner 		/* limit TX length */
186512bd3c8bSSascha Wildner 
186612bd3c8bSSascha Wildner 		if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low))
186712bd3c8bSSascha Wildner 			temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
186812bd3c8bSSascha Wildner 
186912bd3c8bSSascha Wildner 		if (temp > len)
187012bd3c8bSSascha Wildner 			temp = len;
187112bd3c8bSSascha Wildner 
187212bd3c8bSSascha Wildner 		/* copy in data */
187312bd3c8bSSascha Wildner 
187412bd3c8bSSascha Wildner 		usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
187512bd3c8bSSascha Wildner 
187612bd3c8bSSascha Wildner 		/* update counters */
187712bd3c8bSSascha Wildner 
187812bd3c8bSSascha Wildner 		ucom_cons_tx_low += temp;
187912bd3c8bSSascha Wildner 		ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
188012bd3c8bSSascha Wildner 
188112bd3c8bSSascha Wildner 		/* store actual length */
188212bd3c8bSSascha Wildner 
188312bd3c8bSSascha Wildner 		*actlen = temp;
188412bd3c8bSSascha Wildner 
188512bd3c8bSSascha Wildner 		return (temp ? 1 : 0);
188612bd3c8bSSascha Wildner 	}
188712bd3c8bSSascha Wildner 
188812bd3c8bSSascha Wildner 	if (tty_gone(tp) ||
188912bd3c8bSSascha Wildner 	    !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
189012bd3c8bSSascha Wildner 		actlen[0] = 0;
189112bd3c8bSSascha Wildner 		return (0);		/* multiport device polling */
189212bd3c8bSSascha Wildner 	}
1893b28dae1fSMarkus Pfeiffer 
189412bd3c8bSSascha Wildner 	offset_orig = offset;
189512bd3c8bSSascha Wildner 
18962efb75f3SMatthew Dillon 	lwkt_gettoken(&tp->t_token);
189712bd3c8bSSascha Wildner 	while (len != 0) {
189812bd3c8bSSascha Wildner 		usbd_get_page(pc, offset, &res);
189912bd3c8bSSascha Wildner 
1900b28dae1fSMarkus Pfeiffer 		/* Buffer bigger than max requested data */
190112bd3c8bSSascha Wildner 		if (res.length > len) {
190212bd3c8bSSascha Wildner 			res.length = len;
190312bd3c8bSSascha Wildner 		}
190412bd3c8bSSascha Wildner 		/* copy data directly into USB buffer */
19058c494ac4SMarkus Pfeiffer 		SET(tp->t_state, TS_BUSY);
19064725869bSMatthew Dillon 		cnt = clist_qtob(&tp->t_outq, res.buffer, len);
1907b28dae1fSMarkus Pfeiffer 		if (cnt == 0) {
1908b28dae1fSMarkus Pfeiffer 			DPRINTF("ucom_get_data: cnt == 0\n");
1909b28dae1fSMarkus Pfeiffer 			CLR(tp->t_state, TS_BUSY);
1910b28dae1fSMarkus Pfeiffer 			break;
1911b28dae1fSMarkus Pfeiffer 		}
1912b28dae1fSMarkus Pfeiffer 
19138c494ac4SMarkus Pfeiffer 		CLR(tp->t_state, TS_BUSY);
19148c494ac4SMarkus Pfeiffer 
1915b28dae1fSMarkus Pfeiffer 		/* XXX mp: This breaks avrdude,
1916b28dae1fSMarkus Pfeiffer                            does the flush need to happen
1917b28dae1fSMarkus Pfeiffer                            elsewhere?
19188c494ac4SMarkus Pfeiffer 		if (ISSET(tp->t_state, TS_FLUSH))
19198c494ac4SMarkus Pfeiffer 			CLR(tp->t_state, TS_FLUSH);
19208c494ac4SMarkus Pfeiffer 		else
19218c494ac4SMarkus Pfeiffer 			ndflush(&tp->t_outq,cnt);
1922b28dae1fSMarkus Pfeiffer 		*/
19238c494ac4SMarkus Pfeiffer 
192412bd3c8bSSascha Wildner 		offset += cnt;
192512bd3c8bSSascha Wildner 		len -= cnt;
192612bd3c8bSSascha Wildner 
192712bd3c8bSSascha Wildner 		if (cnt < res.length) {
192812bd3c8bSSascha Wildner 			/* end of buffer */
192912bd3c8bSSascha Wildner 			break;
193012bd3c8bSSascha Wildner 		}
193112bd3c8bSSascha Wildner 	}
19322efb75f3SMatthew Dillon 	lwkt_reltoken(&tp->t_token);
193312bd3c8bSSascha Wildner 
193412bd3c8bSSascha Wildner 	actlen[0] = offset - offset_orig;
193512bd3c8bSSascha Wildner 
193612bd3c8bSSascha Wildner 	DPRINTF("cnt=%d\n", actlen[0]);
193712bd3c8bSSascha Wildner 
193812bd3c8bSSascha Wildner 	if (actlen[0] == 0) {
193912bd3c8bSSascha Wildner 		return (0);
194012bd3c8bSSascha Wildner 	}
194112bd3c8bSSascha Wildner 	return (1);
194212bd3c8bSSascha Wildner }
194312bd3c8bSSascha Wildner 
1944b28dae1fSMarkus Pfeiffer /*
1945b28dae1fSMarkus Pfeiffer  * Write data to the tty layer
1946b28dae1fSMarkus Pfeiffer  */
1947b28dae1fSMarkus Pfeiffer 
194812bd3c8bSSascha Wildner void
ucom_put_data(struct ucom_softc * sc,struct usb_page_cache * pc,uint32_t offset,uint32_t len)194912bd3c8bSSascha Wildner ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
195012bd3c8bSSascha Wildner     uint32_t offset, uint32_t len)
195112bd3c8bSSascha Wildner {
195212bd3c8bSSascha Wildner 	struct usb_page_search res;
195312bd3c8bSSascha Wildner 	struct tty *tp = sc->sc_tty;
195412bd3c8bSSascha Wildner 	char *buf;
195512bd3c8bSSascha Wildner 	uint32_t cnt;
19568c494ac4SMarkus Pfeiffer 	int lostcc;
195712bd3c8bSSascha Wildner 
1958b28dae1fSMarkus Pfeiffer 	DPRINTF("\n");
1959b28dae1fSMarkus Pfeiffer 
196018a80dc3SMarkus Pfeiffer 	UCOM_MTX_ASSERT(sc, MA_OWNED);
19612efb75f3SMatthew Dillon 	lwkt_gettoken(&tp->t_token);
196212bd3c8bSSascha Wildner 
196312bd3c8bSSascha Wildner 	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
196412bd3c8bSSascha Wildner 		unsigned int temp;
196512bd3c8bSSascha Wildner 
196612bd3c8bSSascha Wildner 		/* get maximum RX length */
196712bd3c8bSSascha Wildner 
196812bd3c8bSSascha Wildner 		temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
196912bd3c8bSSascha Wildner 		temp %= UCOM_CONS_BUFSIZE;
197012bd3c8bSSascha Wildner 
197112bd3c8bSSascha Wildner 		/* limit RX length */
197212bd3c8bSSascha Wildner 
197312bd3c8bSSascha Wildner 		if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high))
197412bd3c8bSSascha Wildner 			temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
197512bd3c8bSSascha Wildner 
197612bd3c8bSSascha Wildner 		if (temp > len)
197712bd3c8bSSascha Wildner 			temp = len;
197812bd3c8bSSascha Wildner 
197912bd3c8bSSascha Wildner 		/* copy out data */
198012bd3c8bSSascha Wildner 
198112bd3c8bSSascha Wildner 		usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
198212bd3c8bSSascha Wildner 
198312bd3c8bSSascha Wildner 		/* update counters */
198412bd3c8bSSascha Wildner 
198512bd3c8bSSascha Wildner 		ucom_cons_rx_high += temp;
198612bd3c8bSSascha Wildner 		ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
198712bd3c8bSSascha Wildner 
19882efb75f3SMatthew Dillon 		lwkt_reltoken(&tp->t_token);
198912bd3c8bSSascha Wildner 		return;
199012bd3c8bSSascha Wildner 	}
199112bd3c8bSSascha Wildner 
19928c494ac4SMarkus Pfeiffer 	if (tty_gone(tp)) {
19932efb75f3SMatthew Dillon 		lwkt_reltoken(&tp->t_token);
199412bd3c8bSSascha Wildner 		return;			/* multiport device polling */
19958c494ac4SMarkus Pfeiffer 	}
19968c494ac4SMarkus Pfeiffer 	if (len == 0) {
19972efb75f3SMatthew Dillon 		lwkt_reltoken(&tp->t_token);
199812bd3c8bSSascha Wildner 		return;			/* no data */
19998c494ac4SMarkus Pfeiffer 	}
200012bd3c8bSSascha Wildner 
200112bd3c8bSSascha Wildner 	/* set a flag to prevent recursation ? */
200212bd3c8bSSascha Wildner 
200312bd3c8bSSascha Wildner 	while (len > 0) {
200412bd3c8bSSascha Wildner 		usbd_get_page(pc, offset, &res);
200512bd3c8bSSascha Wildner 
200612bd3c8bSSascha Wildner 		if (res.length > len) {
200712bd3c8bSSascha Wildner 			res.length = len;
200812bd3c8bSSascha Wildner 		}
200912bd3c8bSSascha Wildner 		len -= res.length;
201012bd3c8bSSascha Wildner 		offset += res.length;
201112bd3c8bSSascha Wildner 
201212bd3c8bSSascha Wildner 		/* pass characters to tty layer */
201312bd3c8bSSascha Wildner 
201412bd3c8bSSascha Wildner 		buf = res.buffer;
201512bd3c8bSSascha Wildner 		cnt = res.length;
201612bd3c8bSSascha Wildner 
201712bd3c8bSSascha Wildner 		/* first check if we can pass the buffer directly */
201812bd3c8bSSascha Wildner 
20198c494ac4SMarkus Pfeiffer 		if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
202018a80dc3SMarkus Pfeiffer 			/* clear any jitter buffer */
202118a80dc3SMarkus Pfeiffer 			sc->sc_jitterbuf_in = 0;
202218a80dc3SMarkus Pfeiffer 			sc->sc_jitterbuf_out = 0;
202318a80dc3SMarkus Pfeiffer 
20248c494ac4SMarkus Pfeiffer 			if (tp->t_rawq.c_cc + cnt > tp->t_ihiwat
20258c494ac4SMarkus Pfeiffer 			    && (sc->sc_flag & UCOM_FLAG_RTS_IFLOW
20268c494ac4SMarkus Pfeiffer 				|| tp->t_iflag & IXOFF)
20278c494ac4SMarkus Pfeiffer 			    && !(tp->t_state & TS_TBLOCK))
20288c494ac4SMarkus Pfeiffer 			       ttyblock(tp);
20294725869bSMatthew Dillon 			lostcc = clist_btoq((char *)buf, cnt, &tp->t_rawq);
20308c494ac4SMarkus Pfeiffer 			tp->t_rawcc += cnt;
20318c494ac4SMarkus Pfeiffer 			if (sc->hotchar) {
20328c494ac4SMarkus Pfeiffer 				while (cnt) {
20338c494ac4SMarkus Pfeiffer 					if (*buf == sc->hotchar)
20348c494ac4SMarkus Pfeiffer 						break;
20358c494ac4SMarkus Pfeiffer 					--cnt;
20368c494ac4SMarkus Pfeiffer 					++buf;
20378c494ac4SMarkus Pfeiffer 				}
20388c494ac4SMarkus Pfeiffer 				if (cnt)
20398c494ac4SMarkus Pfeiffer 					setsofttty();
20408c494ac4SMarkus Pfeiffer 			}
20418c494ac4SMarkus Pfeiffer 			ttwakeup(tp);
20428c494ac4SMarkus Pfeiffer 			if (tp->t_state & TS_TTSTOP
20438c494ac4SMarkus Pfeiffer 			    && (tp->t_iflag & IXANY
20448c494ac4SMarkus Pfeiffer 				|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
20458c494ac4SMarkus Pfeiffer 				tp->t_state &= ~TS_TTSTOP;
20468c494ac4SMarkus Pfeiffer 				tp->t_lflag &= ~FLUSHO;
20478c494ac4SMarkus Pfeiffer 				ucom_start(tp);
20488c494ac4SMarkus Pfeiffer 			}
20498c494ac4SMarkus Pfeiffer 			if (lostcc > 0)
20508c494ac4SMarkus Pfeiffer 				kprintf("lost %d chars\n", lostcc);
20518c494ac4SMarkus Pfeiffer 
20528c494ac4SMarkus Pfeiffer 			/*
205312bd3c8bSSascha Wildner 			if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
205412bd3c8bSSascha Wildner 				DPRINTF("tp=%p, data lost\n", tp);
205512bd3c8bSSascha Wildner 			}
20568c494ac4SMarkus Pfeiffer 			*/
2057b28dae1fSMarkus Pfeiffer 			continue;
20588c494ac4SMarkus Pfeiffer 		} else {
205912bd3c8bSSascha Wildner 		/* need to loop */
206012bd3c8bSSascha Wildner 			for (cnt = 0; cnt != res.length; cnt++) {
206118a80dc3SMarkus Pfeiffer 				if (sc->sc_jitterbuf_in != sc->sc_jitterbuf_out ||
2062c90f1249SMatthew Dillon 				    (*linesw[tp->t_line].l_rint)((unsigned char)buf[cnt], tp) == -1) {
206318a80dc3SMarkus Pfeiffer 					uint16_t end;
206418a80dc3SMarkus Pfeiffer 					uint16_t pos;
206518a80dc3SMarkus Pfeiffer 
206618a80dc3SMarkus Pfeiffer 					pos = sc->sc_jitterbuf_in;
206718a80dc3SMarkus Pfeiffer 					end = sc->sc_jitterbuf_out +
206818a80dc3SMarkus Pfeiffer 					    UCOM_JITTERBUF_SIZE - 1;
20698c494ac4SMarkus Pfeiffer 
207018a80dc3SMarkus Pfeiffer 					if (end >= UCOM_JITTERBUF_SIZE)
207118a80dc3SMarkus Pfeiffer 						end -= UCOM_JITTERBUF_SIZE;
207218a80dc3SMarkus Pfeiffer 
207318a80dc3SMarkus Pfeiffer 					for (; cnt != res.length; cnt++) {
207418a80dc3SMarkus Pfeiffer 						if (pos == end)
207518a80dc3SMarkus Pfeiffer 							break;
207618a80dc3SMarkus Pfeiffer 						sc->sc_jitterbuf[pos] = buf[cnt];
207718a80dc3SMarkus Pfeiffer 						pos++;
207818a80dc3SMarkus Pfeiffer 						if (pos >= UCOM_JITTERBUF_SIZE)
207918a80dc3SMarkus Pfeiffer 							pos -= UCOM_JITTERBUF_SIZE;
208018a80dc3SMarkus Pfeiffer 					}
208118a80dc3SMarkus Pfeiffer 
208218a80dc3SMarkus Pfeiffer 					sc->sc_jitterbuf_in = pos;
208318a80dc3SMarkus Pfeiffer 
208418a80dc3SMarkus Pfeiffer 					/* set RTS in async fashion */
208518a80dc3SMarkus Pfeiffer 					if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW)
208618a80dc3SMarkus Pfeiffer 						ucom_rts(sc, 1);
208712bd3c8bSSascha Wildner 
208812bd3c8bSSascha Wildner 					DPRINTF("tp=%p, lost %d "
208912bd3c8bSSascha Wildner 					    "chars\n", tp, res.length - cnt);
209012bd3c8bSSascha Wildner 					break;
209112bd3c8bSSascha Wildner 				}
209212bd3c8bSSascha Wildner 			}
209312bd3c8bSSascha Wildner 		}
20948c494ac4SMarkus Pfeiffer 	}
20952efb75f3SMatthew Dillon 	lwkt_reltoken(&tp->t_token);
20968c494ac4SMarkus Pfeiffer 	/*
209712bd3c8bSSascha Wildner 	ttydisc_rint_done(tp);
20988c494ac4SMarkus Pfeiffer 	*/
209912bd3c8bSSascha Wildner }
210012bd3c8bSSascha Wildner 
210169ea62cfSSascha Wildner #if 0 /* XXX */
210212bd3c8bSSascha Wildner static void
210312bd3c8bSSascha Wildner ucom_free(void *xsc)
210412bd3c8bSSascha Wildner {
210512bd3c8bSSascha Wildner 	struct ucom_softc *sc = xsc;
210612bd3c8bSSascha Wildner 
210718a80dc3SMarkus Pfeiffer 	if (sc->sc_callback->ucom_free != NULL)
210818a80dc3SMarkus Pfeiffer 		sc->sc_callback->ucom_free(sc);
210918a80dc3SMarkus Pfeiffer 	else
2110c01d5218SMatthew Dillon 		/*ucom_unref(sc->sc_super) XXX hack, see end of ucom_detach_tty() */;
211118a80dc3SMarkus Pfeiffer 
211218a80dc3SMarkus Pfeiffer 	lockmgr(&ucom_lock, LK_EXCLUSIVE);
211318a80dc3SMarkus Pfeiffer 	ucom_close_refs--;
211418a80dc3SMarkus Pfeiffer 	lockmgr(&ucom_lock, LK_RELEASE);
211512bd3c8bSSascha Wildner }
211612bd3c8bSSascha Wildner 
211712bd3c8bSSascha Wildner static cn_probe_t ucom_cnprobe;
211812bd3c8bSSascha Wildner static cn_init_t ucom_cninit;
211912bd3c8bSSascha Wildner static cn_term_t ucom_cnterm;
212012bd3c8bSSascha Wildner static cn_getc_t ucom_cngetc;
212112bd3c8bSSascha Wildner static cn_putc_t ucom_cnputc;
2122fd10b026SMarkus Pfeiffer 
2123fd10b026SMarkus Pfeiffer /*
212412bd3c8bSSascha Wildner static cn_grab_t ucom_cngrab;
212512bd3c8bSSascha Wildner static cn_ungrab_t ucom_cnungrab;
212612bd3c8bSSascha Wildner CONSOLE_DRIVER(ucom);
2127fd10b026SMarkus Pfeiffer */
212812bd3c8bSSascha Wildner 
212912bd3c8bSSascha Wildner static void
213012bd3c8bSSascha Wildner ucom_cnprobe(struct consdev  *cp)
213112bd3c8bSSascha Wildner {
213212bd3c8bSSascha Wildner 	if (ucom_cons_unit != -1)
213312bd3c8bSSascha Wildner 		cp->cn_pri = CN_NORMAL;
213412bd3c8bSSascha Wildner 	else
213512bd3c8bSSascha Wildner 		cp->cn_pri = CN_DEAD;
213612bd3c8bSSascha Wildner 
2137fd10b026SMarkus Pfeiffer 	/*
213812bd3c8bSSascha Wildner 	strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name));
2139fd10b026SMarkus Pfeiffer 	*/
214012bd3c8bSSascha Wildner }
214112bd3c8bSSascha Wildner 
214212bd3c8bSSascha Wildner static void
214312bd3c8bSSascha Wildner ucom_cninit(struct consdev  *cp)
214412bd3c8bSSascha Wildner {
214512bd3c8bSSascha Wildner }
214612bd3c8bSSascha Wildner 
214712bd3c8bSSascha Wildner static void
214812bd3c8bSSascha Wildner ucom_cnterm(struct consdev  *cp)
214912bd3c8bSSascha Wildner {
215012bd3c8bSSascha Wildner }
215112bd3c8bSSascha Wildner 
215212bd3c8bSSascha Wildner static void
215312bd3c8bSSascha Wildner ucom_cngrab(struct consdev *cp)
215412bd3c8bSSascha Wildner {
215512bd3c8bSSascha Wildner }
215612bd3c8bSSascha Wildner 
215712bd3c8bSSascha Wildner static void
215812bd3c8bSSascha Wildner ucom_cnungrab(struct consdev *cp)
215912bd3c8bSSascha Wildner {
216012bd3c8bSSascha Wildner }
216112bd3c8bSSascha Wildner 
216212bd3c8bSSascha Wildner static int
216312bd3c8bSSascha Wildner ucom_cngetc(struct consdev *cd)
216412bd3c8bSSascha Wildner {
216512bd3c8bSSascha Wildner 	struct ucom_softc *sc = ucom_cons_softc;
216612bd3c8bSSascha Wildner 	int c;
216712bd3c8bSSascha Wildner 
216812bd3c8bSSascha Wildner 	if (sc == NULL)
216912bd3c8bSSascha Wildner 		return (-1);
217012bd3c8bSSascha Wildner 
217118a80dc3SMarkus Pfeiffer 	UCOM_MTX_LOCK(sc);
217212bd3c8bSSascha Wildner 
217312bd3c8bSSascha Wildner 	if (ucom_cons_rx_low != ucom_cons_rx_high) {
217412bd3c8bSSascha Wildner 		c = ucom_cons_rx_buf[ucom_cons_rx_low];
217512bd3c8bSSascha Wildner 		ucom_cons_rx_low ++;
217612bd3c8bSSascha Wildner 		ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
217712bd3c8bSSascha Wildner 	} else {
217812bd3c8bSSascha Wildner 		c = -1;
217912bd3c8bSSascha Wildner 	}
218012bd3c8bSSascha Wildner 
218112bd3c8bSSascha Wildner 	/* start USB transfers */
218212bd3c8bSSascha Wildner 	ucom_outwakeup(sc->sc_tty);
218312bd3c8bSSascha Wildner 
218418a80dc3SMarkus Pfeiffer 	UCOM_MTX_UNLOCK(sc);
218512bd3c8bSSascha Wildner 
218612bd3c8bSSascha Wildner 	/* poll if necessary */
2187fd10b026SMarkus Pfeiffer 	/*
218812bd3c8bSSascha Wildner 	if (kdb_active && sc->sc_callback->ucom_poll)
218912bd3c8bSSascha Wildner 		(sc->sc_callback->ucom_poll) (sc);
2190fd10b026SMarkus Pfeiffer 	*/
219112bd3c8bSSascha Wildner 	return (c);
219212bd3c8bSSascha Wildner }
219312bd3c8bSSascha Wildner 
219412bd3c8bSSascha Wildner static void
2195fd10b026SMarkus Pfeiffer ucom_cnputc(void *cd, int c)
2196fd10b026SMarkus Pfeiffer 	/*
219712bd3c8bSSascha Wildner ucom_cnputc(struct consdev *cd, int c)
2198fd10b026SMarkus Pfeiffer 	*/
2199fd10b026SMarkus Pfeiffer 
220012bd3c8bSSascha Wildner {
220112bd3c8bSSascha Wildner 	struct ucom_softc *sc = ucom_cons_softc;
220212bd3c8bSSascha Wildner 	unsigned int temp;
220312bd3c8bSSascha Wildner 
220412bd3c8bSSascha Wildner 	if (sc == NULL)
220512bd3c8bSSascha Wildner 		return;
220612bd3c8bSSascha Wildner 
220712bd3c8bSSascha Wildner  repeat:
220812bd3c8bSSascha Wildner 
220918a80dc3SMarkus Pfeiffer 	UCOM_MTX_LOCK(sc);
221012bd3c8bSSascha Wildner 
221112bd3c8bSSascha Wildner 	/* compute maximum TX length */
221212bd3c8bSSascha Wildner 
221312bd3c8bSSascha Wildner 	temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
221412bd3c8bSSascha Wildner 	temp %= UCOM_CONS_BUFSIZE;
221512bd3c8bSSascha Wildner 
221612bd3c8bSSascha Wildner 	if (temp) {
221712bd3c8bSSascha Wildner 		ucom_cons_tx_buf[ucom_cons_tx_high] = c;
221812bd3c8bSSascha Wildner 		ucom_cons_tx_high ++;
221912bd3c8bSSascha Wildner 		ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
222012bd3c8bSSascha Wildner 	}
222112bd3c8bSSascha Wildner 
222212bd3c8bSSascha Wildner 	/* start USB transfers */
222312bd3c8bSSascha Wildner 	ucom_outwakeup(sc->sc_tty);
222412bd3c8bSSascha Wildner 
222518a80dc3SMarkus Pfeiffer 	UCOM_MTX_UNLOCK(sc);
222612bd3c8bSSascha Wildner 
222712bd3c8bSSascha Wildner 	/* poll if necessary */
222869ea62cfSSascha Wildner #if 0 /* XXX */
222912bd3c8bSSascha Wildner 	if (kdb_active && sc->sc_callback->ucom_poll) {
223012bd3c8bSSascha Wildner 		(sc->sc_callback->ucom_poll) (sc);
223112bd3c8bSSascha Wildner 		/* simple flow control */
223212bd3c8bSSascha Wildner 		if (temp == 0)
223312bd3c8bSSascha Wildner 			goto repeat;
223412bd3c8bSSascha Wildner 	}
2235fd10b026SMarkus Pfeiffer #endif
223612bd3c8bSSascha Wildner }
2237fd10b026SMarkus Pfeiffer #endif
223818a80dc3SMarkus Pfeiffer /*------------------------------------------------------------------------*
223918a80dc3SMarkus Pfeiffer  *	ucom_ref
224018a80dc3SMarkus Pfeiffer  *
224118a80dc3SMarkus Pfeiffer  * This function will increment the super UCOM reference count.
224218a80dc3SMarkus Pfeiffer  *------------------------------------------------------------------------*/
224318a80dc3SMarkus Pfeiffer void
ucom_ref(struct ucom_super_softc * ssc)224418a80dc3SMarkus Pfeiffer ucom_ref(struct ucom_super_softc *ssc)
224518a80dc3SMarkus Pfeiffer {
224618a80dc3SMarkus Pfeiffer 	lockmgr(&ucom_lock, LK_EXCLUSIVE);
224718a80dc3SMarkus Pfeiffer 	ssc->sc_refs++;
224818a80dc3SMarkus Pfeiffer 	lockmgr(&ucom_lock, LK_RELEASE);
224918a80dc3SMarkus Pfeiffer }
225018a80dc3SMarkus Pfeiffer 
225118a80dc3SMarkus Pfeiffer /*------------------------------------------------------------------------*
225218a80dc3SMarkus Pfeiffer  *	ucom_free_unit
225318a80dc3SMarkus Pfeiffer  *
225418a80dc3SMarkus Pfeiffer  * This function will free the super UCOM's allocated unit
225518a80dc3SMarkus Pfeiffer  * number. This function can be called on a zero-initialized
225618a80dc3SMarkus Pfeiffer  * structure. This function can be called multiple times.
225718a80dc3SMarkus Pfeiffer  *------------------------------------------------------------------------*/
225818a80dc3SMarkus Pfeiffer static void
ucom_free_unit(struct ucom_super_softc * ssc)225918a80dc3SMarkus Pfeiffer ucom_free_unit(struct ucom_super_softc *ssc)
226018a80dc3SMarkus Pfeiffer {
226118a80dc3SMarkus Pfeiffer 	if (!(ssc->sc_flag & UCOM_FLAG_FREE_UNIT))
226218a80dc3SMarkus Pfeiffer 		return;
226318a80dc3SMarkus Pfeiffer 
226418a80dc3SMarkus Pfeiffer 	ucom_unit_free(ssc->sc_unit);
226518a80dc3SMarkus Pfeiffer 
226618a80dc3SMarkus Pfeiffer 	ssc->sc_flag &= ~UCOM_FLAG_FREE_UNIT;
226718a80dc3SMarkus Pfeiffer }
226818a80dc3SMarkus Pfeiffer 
226918a80dc3SMarkus Pfeiffer /*------------------------------------------------------------------------*
227018a80dc3SMarkus Pfeiffer  *	ucom_unref
227118a80dc3SMarkus Pfeiffer  *
227218a80dc3SMarkus Pfeiffer  * This function will decrement the super UCOM reference count.
227318a80dc3SMarkus Pfeiffer  *
227418a80dc3SMarkus Pfeiffer  * Return values:
227518a80dc3SMarkus Pfeiffer  * 0: UCOM structures are still referenced.
227618a80dc3SMarkus Pfeiffer  * Else: UCOM structures are no longer referenced.
227718a80dc3SMarkus Pfeiffer  *------------------------------------------------------------------------*/
227818a80dc3SMarkus Pfeiffer int
ucom_unref(struct ucom_super_softc * ssc)227918a80dc3SMarkus Pfeiffer ucom_unref(struct ucom_super_softc *ssc)
228018a80dc3SMarkus Pfeiffer {
228118a80dc3SMarkus Pfeiffer 	int retval;
228218a80dc3SMarkus Pfeiffer 
228318a80dc3SMarkus Pfeiffer 	lockmgr(&ucom_lock, LK_EXCLUSIVE);
228418a80dc3SMarkus Pfeiffer 	retval = (ssc->sc_refs < 2);
228518a80dc3SMarkus Pfeiffer 	ssc->sc_refs--;
228618a80dc3SMarkus Pfeiffer 	lockmgr(&ucom_lock, LK_RELEASE);
228718a80dc3SMarkus Pfeiffer 
228818a80dc3SMarkus Pfeiffer 	if (retval)
228918a80dc3SMarkus Pfeiffer 		ucom_free_unit(ssc);
229018a80dc3SMarkus Pfeiffer 
229118a80dc3SMarkus Pfeiffer 	return (retval);
229218a80dc3SMarkus Pfeiffer }
229318a80dc3SMarkus Pfeiffer 
2294b28dae1fSMarkus Pfeiffer /*
22952efb75f3SMatthew Dillon  * NOTE: Must be called with tp->t_token held.
2296b28dae1fSMarkus Pfeiffer  */
2297b28dae1fSMarkus Pfeiffer static void
disc_optim(struct tty * tp,struct termios * t,struct ucom_softc * sc)2298b28dae1fSMarkus Pfeiffer disc_optim(struct tty *tp, struct termios *t, struct ucom_softc *sc)
2299b28dae1fSMarkus Pfeiffer {
23002efb75f3SMatthew Dillon 	ASSERT_LWKT_TOKEN_HELD(&tp->t_token);
2301b28dae1fSMarkus Pfeiffer 	if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
2302b28dae1fSMarkus Pfeiffer 	    && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
2303b28dae1fSMarkus Pfeiffer 	    && (!(t->c_iflag & PARMRK)
2304b28dae1fSMarkus Pfeiffer 		|| (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
2305b28dae1fSMarkus Pfeiffer 	    && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
2306b28dae1fSMarkus Pfeiffer 	    && linesw[tp->t_line].l_rint == ttyinput) {
2307b28dae1fSMarkus Pfeiffer 		DPRINTF("disc_optim: bypass l_rint\n");
2308b28dae1fSMarkus Pfeiffer 		tp->t_state |= TS_CAN_BYPASS_L_RINT;
2309b28dae1fSMarkus Pfeiffer 	} else {
2310b28dae1fSMarkus Pfeiffer 		DPRINTF("disc_optim: can't bypass l_rint\n");
2311b28dae1fSMarkus Pfeiffer 		tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
2312b28dae1fSMarkus Pfeiffer 	}
2313b28dae1fSMarkus Pfeiffer 	sc->hotchar = linesw[tp->t_line].l_hotchar;
2314b28dae1fSMarkus Pfeiffer }
2315b28dae1fSMarkus Pfeiffer 
231612bd3c8bSSascha Wildner #if defined(GDB)
231712bd3c8bSSascha Wildner 
231812bd3c8bSSascha Wildner #include <gdb/gdb.h>
231912bd3c8bSSascha Wildner 
232012bd3c8bSSascha Wildner static gdb_probe_f ucom_gdbprobe;
232112bd3c8bSSascha Wildner static gdb_init_f ucom_gdbinit;
232212bd3c8bSSascha Wildner static gdb_term_f ucom_gdbterm;
232312bd3c8bSSascha Wildner static gdb_getc_f ucom_gdbgetc;
232412bd3c8bSSascha Wildner static gdb_putc_f ucom_gdbputc;
232512bd3c8bSSascha Wildner 
232612bd3c8bSSascha Wildner GDB_DBGPORT(sio, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc);
232712bd3c8bSSascha Wildner 
232812bd3c8bSSascha Wildner static int
ucom_gdbprobe(void)232912bd3c8bSSascha Wildner ucom_gdbprobe(void)
233012bd3c8bSSascha Wildner {
233112bd3c8bSSascha Wildner 	return ((ucom_cons_softc != NULL) ? 0 : -1);
233212bd3c8bSSascha Wildner }
233312bd3c8bSSascha Wildner 
233412bd3c8bSSascha Wildner static void
ucom_gdbinit(void)233512bd3c8bSSascha Wildner ucom_gdbinit(void)
233612bd3c8bSSascha Wildner {
233712bd3c8bSSascha Wildner }
233812bd3c8bSSascha Wildner 
233912bd3c8bSSascha Wildner static void
ucom_gdbterm(void)234012bd3c8bSSascha Wildner ucom_gdbterm(void)
234112bd3c8bSSascha Wildner {
234212bd3c8bSSascha Wildner }
234312bd3c8bSSascha Wildner 
234412bd3c8bSSascha Wildner static void
ucom_gdbputc(int c)234512bd3c8bSSascha Wildner ucom_gdbputc(int c)
234612bd3c8bSSascha Wildner {
234712bd3c8bSSascha Wildner         ucom_cnputc(NULL, c);
234812bd3c8bSSascha Wildner }
234912bd3c8bSSascha Wildner 
235012bd3c8bSSascha Wildner static int
ucom_gdbgetc(void)235112bd3c8bSSascha Wildner ucom_gdbgetc(void)
235212bd3c8bSSascha Wildner {
235312bd3c8bSSascha Wildner         return (ucom_cngetc(NULL));
235412bd3c8bSSascha Wildner }
235512bd3c8bSSascha Wildner 
235612bd3c8bSSascha Wildner #endif
2357