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