usb_serial.c revision 239179
1/*	$NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $	*/
2
3/*-
4 * Copyright (c) 2001-2003, 2005, 2008
5 *	Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/dev/usb/serial/usb_serial.c 239179 2012-08-10 15:21:12Z hselasky $");
32
33/*-
34 * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
35 * All rights reserved.
36 *
37 * This code is derived from software contributed to The NetBSD Foundation
38 * by Lennart Augustsson (lennart@augustsson.net) at
39 * Carlstedt Research & Technology.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 *    notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 *    notice, this list of conditions and the following disclaimer in the
48 *    documentation and/or other materials provided with the distribution.
49 * 3. All advertising materials mentioning features or use of this software
50 *    must display the following acknowledgement:
51 *        This product includes software developed by the NetBSD
52 *        Foundation, Inc. and its contributors.
53 * 4. Neither the name of The NetBSD Foundation nor the names of its
54 *    contributors may be used to endorse or promote products derived
55 *    from this software without specific prior written permission.
56 *
57 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
58 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
59 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
60 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
61 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
62 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
63 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
64 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
66 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
67 * POSSIBILITY OF SUCH DAMAGE.
68 */
69
70#include <sys/stdint.h>
71#include <sys/stddef.h>
72#include <sys/param.h>
73#include <sys/queue.h>
74#include <sys/types.h>
75#include <sys/systm.h>
76#include <sys/kernel.h>
77#include <sys/bus.h>
78#include <sys/module.h>
79#include <sys/lock.h>
80#include <sys/mutex.h>
81#include <sys/condvar.h>
82#include <sys/sysctl.h>
83#include <sys/sx.h>
84#include <sys/unistd.h>
85#include <sys/callout.h>
86#include <sys/malloc.h>
87#include <sys/priv.h>
88#include <sys/cons.h>
89#include <sys/kdb.h>
90
91#include <dev/usb/usb.h>
92#include <dev/usb/usbdi.h>
93#include <dev/usb/usbdi_util.h>
94
95#define	USB_DEBUG_VAR ucom_debug
96#include <dev/usb/usb_debug.h>
97#include <dev/usb/usb_busdma.h>
98#include <dev/usb/usb_process.h>
99
100#include <dev/usb/serial/usb_serial.h>
101
102#include "opt_gdb.h"
103
104static SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
105
106#ifdef USB_DEBUG
107static int ucom_debug = 0;
108
109SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
110    &ucom_debug, 0, "ucom debug level");
111#endif
112
113#define	UCOM_CONS_BUFSIZE 1024
114
115static uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE];
116static uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE];
117
118static unsigned int ucom_cons_rx_low = 0;
119static unsigned int ucom_cons_rx_high = 0;
120
121static unsigned int ucom_cons_tx_low = 0;
122static unsigned int ucom_cons_tx_high = 0;
123
124static int ucom_cons_unit = -1;
125static int ucom_cons_subunit = 0;
126static int ucom_cons_baud = 9600;
127static struct ucom_softc *ucom_cons_softc = NULL;
128
129TUNABLE_INT("hw.usb.ucom.cons_unit", &ucom_cons_unit);
130SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RW,
131    &ucom_cons_unit, 0, "console unit number");
132TUNABLE_INT("hw.usb.ucom.cons_subunit", &ucom_cons_subunit);
133SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_subunit, CTLFLAG_RW,
134    &ucom_cons_subunit, 0, "console subunit number");
135TUNABLE_INT("hw.usb.ucom.cons_baud", &ucom_cons_baud);
136SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RW,
137    &ucom_cons_baud, 0, "console baud rate");
138
139static usb_proc_callback_t ucom_cfg_start_transfers;
140static usb_proc_callback_t ucom_cfg_open;
141static usb_proc_callback_t ucom_cfg_close;
142static usb_proc_callback_t ucom_cfg_line_state;
143static usb_proc_callback_t ucom_cfg_status_change;
144static usb_proc_callback_t ucom_cfg_param;
145
146static int	ucom_unit_alloc(void);
147static void	ucom_unit_free(int);
148static int	ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *);
149static void	ucom_detach_tty(struct ucom_super_softc *, struct ucom_softc *);
150static void	ucom_queue_command(struct ucom_softc *,
151		    usb_proc_callback_t *, struct termios *pt,
152		    struct usb_proc_msg *t0, struct usb_proc_msg *t1);
153static void	ucom_shutdown(struct ucom_softc *);
154static void	ucom_ring(struct ucom_softc *, uint8_t);
155static void	ucom_break(struct ucom_softc *, uint8_t);
156static void	ucom_dtr(struct ucom_softc *, uint8_t);
157static void	ucom_rts(struct ucom_softc *, uint8_t);
158
159static tsw_open_t ucom_open;
160static tsw_close_t ucom_close;
161static tsw_ioctl_t ucom_ioctl;
162static tsw_modem_t ucom_modem;
163static tsw_param_t ucom_param;
164static tsw_outwakeup_t ucom_outwakeup;
165static tsw_free_t ucom_free;
166
167static struct ttydevsw ucom_class = {
168	.tsw_flags = TF_INITLOCK | TF_CALLOUT,
169	.tsw_open = ucom_open,
170	.tsw_close = ucom_close,
171	.tsw_outwakeup = ucom_outwakeup,
172	.tsw_ioctl = ucom_ioctl,
173	.tsw_param = ucom_param,
174	.tsw_modem = ucom_modem,
175	.tsw_free = ucom_free,
176};
177
178MODULE_DEPEND(ucom, usb, 1, 1, 1);
179MODULE_VERSION(ucom, 1);
180
181#define	UCOM_UNIT_MAX 		128	/* maximum number of units */
182#define	UCOM_TTY_PREFIX		"U"
183
184static struct unrhdr *ucom_unrhdr;
185static struct mtx ucom_mtx;
186static int ucom_close_refs;
187
188static void
189ucom_init(void *arg)
190{
191	DPRINTF("\n");
192	ucom_unrhdr = new_unrhdr(0, UCOM_UNIT_MAX - 1, NULL);
193	mtx_init(&ucom_mtx, "UCOM MTX", NULL, MTX_DEF);
194}
195SYSINIT(ucom_init, SI_SUB_KLD - 1, SI_ORDER_ANY, ucom_init, NULL);
196
197static void
198ucom_uninit(void *arg)
199{
200	struct unrhdr *hdr;
201	hdr = ucom_unrhdr;
202	ucom_unrhdr = NULL;
203
204	DPRINTF("\n");
205
206	if (hdr != NULL)
207		delete_unrhdr(hdr);
208
209	mtx_destroy(&ucom_mtx);
210}
211SYSUNINIT(ucom_uninit, SI_SUB_KLD - 2, SI_ORDER_ANY, ucom_uninit, NULL);
212
213/*
214 * Mark a unit number (the X in cuaUX) as in use.
215 *
216 * Note that devices using a different naming scheme (see ucom_tty_name()
217 * callback) still use this unit allocation.
218 */
219static int
220ucom_unit_alloc(void)
221{
222	int unit;
223
224	/* sanity checks */
225	if (ucom_unrhdr == NULL) {
226		DPRINTF("ucom_unrhdr is NULL\n");
227		return (-1);
228	}
229	unit = alloc_unr(ucom_unrhdr);
230	DPRINTF("unit %d is allocated\n", unit);
231	return (unit);
232}
233
234/*
235 * Mark the unit number as not in use.
236 */
237static void
238ucom_unit_free(int unit)
239{
240	/* sanity checks */
241	if (unit < 0 || unit >= UCOM_UNIT_MAX || ucom_unrhdr == NULL) {
242		DPRINTF("cannot free unit number\n");
243		return;
244	}
245	DPRINTF("unit %d is freed\n", unit);
246	free_unr(ucom_unrhdr, unit);
247}
248
249/*
250 * Setup a group of one or more serial ports.
251 *
252 * The mutex pointed to by "mtx" is applied before all
253 * callbacks are called back. Also "mtx" must be applied
254 * before calling into the ucom-layer!
255 */
256int
257ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
258    int subunits, void *parent,
259    const struct ucom_callback *callback, struct mtx *mtx)
260{
261	int subunit;
262	int error = 0;
263
264	if ((sc == NULL) ||
265	    (subunits <= 0) ||
266	    (callback == NULL) ||
267	    (mtx == NULL)) {
268		return (EINVAL);
269	}
270
271	/* allocate a uniq unit number */
272	ssc->sc_unit = ucom_unit_alloc();
273	if (ssc->sc_unit == -1)
274		return (ENOMEM);
275
276	/* generate TTY name string */
277	snprintf(ssc->sc_ttyname, sizeof(ssc->sc_ttyname),
278	    UCOM_TTY_PREFIX "%d", ssc->sc_unit);
279
280	/* create USB request handling process */
281	error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
282	if (error) {
283		ucom_unit_free(ssc->sc_unit);
284		return (error);
285	}
286	ssc->sc_subunits = subunits;
287
288	if (callback->ucom_free == NULL) {
289		ssc->sc_wait_refs = 1;
290		ucom_ref(ssc);
291	}
292
293	for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
294		sc[subunit].sc_subunit = subunit;
295		sc[subunit].sc_super = ssc;
296		sc[subunit].sc_mtx = mtx;
297		sc[subunit].sc_parent = parent;
298		sc[subunit].sc_callback = callback;
299
300		error = ucom_attach_tty(ssc, &sc[subunit]);
301		if (error) {
302			ucom_detach(ssc, &sc[0]);
303			return (error);
304		}
305		/* increment reference count */
306		ucom_ref(ssc);
307
308		/* set subunit attached */
309		sc[subunit].sc_flag |= UCOM_FLAG_ATTACHED;
310	}
311
312	DPRINTF("tp = %p, unit = %d, subunits = %d\n",
313		sc->sc_tty, ssc->sc_unit, ssc->sc_subunits);
314
315	return (0);
316}
317
318/*
319 * NOTE: the following function will do nothing if
320 * the structure pointed to by "ssc" and "sc" is zero.
321 */
322void
323ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc)
324{
325	int subunit;
326
327	if (ssc->sc_subunits == 0)
328		return;		/* not initialized */
329
330	if (ssc->sc_sysctl_ttyname != NULL) {
331		sysctl_remove_oid(ssc->sc_sysctl_ttyname, 1, 0);
332		ssc->sc_sysctl_ttyname = NULL;
333	}
334
335	if (ssc->sc_sysctl_ttyports != NULL) {
336		sysctl_remove_oid(ssc->sc_sysctl_ttyports, 1, 0);
337		ssc->sc_sysctl_ttyports = NULL;
338	}
339
340	usb_proc_drain(&ssc->sc_tq);
341
342	for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
343		if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) {
344
345			ucom_detach_tty(ssc, &sc[subunit]);
346
347			/* avoid duplicate detach */
348			sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED;
349		}
350	}
351	usb_proc_free(&ssc->sc_tq);
352
353	if (ssc->sc_wait_refs != 0) {
354		ucom_unref(ssc);
355		ucom_drain(ssc);
356	}
357}
358
359void
360ucom_drain(struct ucom_super_softc *ssc)
361{
362	mtx_lock(&ucom_mtx);
363	while (ssc->sc_refs >= 2) {
364		printf("ucom: Waiting for a TTY device to close.\n");
365		usb_pause_mtx(&ucom_mtx, hz);
366	}
367	mtx_unlock(&ucom_mtx);
368}
369
370void
371ucom_drain_all(void *arg)
372{
373	mtx_lock(&ucom_mtx);
374	while (ucom_close_refs > 0) {
375		printf("ucom: Waiting for all detached TTY "
376		    "devices to have open fds closed.\n");
377		usb_pause_mtx(&ucom_mtx, hz);
378	}
379	mtx_unlock(&ucom_mtx);
380}
381
382static int
383ucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
384{
385	struct tty *tp;
386	char buf[32];			/* temporary TTY device name buffer */
387
388	tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx);
389	if (tp == NULL)
390		return (ENOMEM);
391
392	/* Check if the client has a custom TTY name */
393	buf[0] = '\0';
394	if (sc->sc_callback->ucom_tty_name) {
395		sc->sc_callback->ucom_tty_name(sc, buf,
396		    sizeof(buf), ssc->sc_unit, sc->sc_subunit);
397	}
398	if (buf[0] == 0) {
399		/* Use default TTY name */
400		if (ssc->sc_subunits > 1) {
401			/* multiple modems in one */
402			snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u.%u",
403			    ssc->sc_unit, sc->sc_subunit);
404		} else {
405			/* single modem */
406			snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u",
407			    ssc->sc_unit);
408		}
409	}
410	tty_makedev(tp, NULL, "%s", buf);
411
412	sc->sc_tty = tp;
413
414	DPRINTF("ttycreate: %s\n", buf);
415
416	/* Check if this device should be a console */
417	if ((ucom_cons_softc == NULL) &&
418	    (ssc->sc_unit == ucom_cons_unit) &&
419	    (sc->sc_subunit == ucom_cons_subunit)) {
420		struct termios t;
421
422		DPRINTF("unit %d subunit %d is console", ssc->sc_unit, sc->sc_subunit);
423
424		ucom_cons_softc = sc;
425
426		memset(&t, 0, sizeof(t));
427		t.c_ispeed = ucom_cons_baud;
428		t.c_ospeed = t.c_ispeed;
429		t.c_cflag = CS8;
430
431		UCOM_MTX_LOCK(ucom_cons_softc);
432		ucom_cons_rx_low = 0;
433		ucom_cons_rx_high = 0;
434		ucom_cons_tx_low = 0;
435		ucom_cons_tx_high = 0;
436		sc->sc_flag |= UCOM_FLAG_CONSOLE;
437		ucom_open(ucom_cons_softc->sc_tty);
438		ucom_param(ucom_cons_softc->sc_tty, &t);
439		UCOM_MTX_UNLOCK(ucom_cons_softc);
440	}
441
442	return (0);
443}
444
445static void
446ucom_detach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
447{
448	struct tty *tp = sc->sc_tty;
449
450	DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
451
452	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
453		UCOM_MTX_LOCK(ucom_cons_softc);
454		ucom_close(ucom_cons_softc->sc_tty);
455		sc->sc_flag &= ~UCOM_FLAG_CONSOLE;
456		UCOM_MTX_UNLOCK(ucom_cons_softc);
457		ucom_cons_softc = NULL;
458	}
459
460	/* the config thread has been stopped when we get here */
461
462	UCOM_MTX_LOCK(sc);
463	sc->sc_flag |= UCOM_FLAG_GONE;
464	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY);
465	UCOM_MTX_UNLOCK(sc);
466
467	if (tp) {
468		mtx_lock(&ucom_mtx);
469		ucom_close_refs++;
470		mtx_unlock(&ucom_mtx);
471
472		tty_lock(tp);
473
474		ucom_close(tp);	/* close, if any */
475
476		tty_rel_gone(tp);
477
478		UCOM_MTX_LOCK(sc);
479		/*
480		 * make sure that read and write transfers are stopped
481		 */
482		if (sc->sc_callback->ucom_stop_read)
483			(sc->sc_callback->ucom_stop_read) (sc);
484		if (sc->sc_callback->ucom_stop_write)
485			(sc->sc_callback->ucom_stop_write) (sc);
486		UCOM_MTX_UNLOCK(sc);
487	}
488}
489
490void
491ucom_set_pnpinfo_usb(struct ucom_super_softc *ssc, device_t dev)
492{
493	char buf[64];
494	uint8_t iface_index;
495	struct usb_attach_arg *uaa;
496
497	snprintf(buf, sizeof(buf), "ttyname=" UCOM_TTY_PREFIX
498	    "%d ttyports=%d", ssc->sc_unit, ssc->sc_subunits);
499
500	/* Store the PNP info in the first interface for the device */
501	uaa = device_get_ivars(dev);
502	iface_index = uaa->info.bIfaceIndex;
503
504	if (usbd_set_pnpinfo(uaa->device, iface_index, buf) != 0)
505		device_printf(dev, "Could not set PNP info\n");
506
507	/*
508	 * The following information is also replicated in the PNP-info
509	 * string which is registered above:
510	 */
511	if (ssc->sc_sysctl_ttyname == NULL) {
512		ssc->sc_sysctl_ttyname = SYSCTL_ADD_STRING(NULL,
513		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
514		    OID_AUTO, "ttyname", CTLFLAG_RD, ssc->sc_ttyname, 0,
515		    "TTY device basename");
516	}
517	if (ssc->sc_sysctl_ttyports == NULL) {
518		ssc->sc_sysctl_ttyports = SYSCTL_ADD_INT(NULL,
519		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
520		    OID_AUTO, "ttyports", CTLFLAG_RD,
521		    NULL, ssc->sc_subunits, "Number of ports");
522	}
523}
524
525static void
526ucom_queue_command(struct ucom_softc *sc,
527    usb_proc_callback_t *fn, struct termios *pt,
528    struct usb_proc_msg *t0, struct usb_proc_msg *t1)
529{
530	struct ucom_super_softc *ssc = sc->sc_super;
531	struct ucom_param_task *task;
532
533	UCOM_MTX_ASSERT(sc, MA_OWNED);
534
535	if (usb_proc_is_gone(&ssc->sc_tq)) {
536		DPRINTF("proc is gone\n");
537		return;         /* nothing to do */
538	}
539	/*
540	 * NOTE: The task cannot get executed before we drop the
541	 * "sc_mtx" mutex. It is safe to update fields in the message
542	 * structure after that the message got queued.
543	 */
544	task = (struct ucom_param_task *)
545	  usb_proc_msignal(&ssc->sc_tq, t0, t1);
546
547	/* Setup callback and softc pointers */
548	task->hdr.pm_callback = fn;
549	task->sc = sc;
550
551	/*
552	 * Make a copy of the termios. This field is only present if
553	 * the "pt" field is not NULL.
554	 */
555	if (pt != NULL)
556		task->termios_copy = *pt;
557
558	/*
559	 * Closing the device should be synchronous.
560	 */
561	if (fn == ucom_cfg_close)
562		usb_proc_mwait(&ssc->sc_tq, t0, t1);
563
564	/*
565	 * In case of multiple configure requests,
566	 * keep track of the last one!
567	 */
568	if (fn == ucom_cfg_start_transfers)
569		sc->sc_last_start_xfer = &task->hdr;
570}
571
572static void
573ucom_shutdown(struct ucom_softc *sc)
574{
575	struct tty *tp = sc->sc_tty;
576
577	UCOM_MTX_ASSERT(sc, MA_OWNED);
578
579	DPRINTF("\n");
580
581	/*
582	 * Hang up if necessary:
583	 */
584	if (tp->t_termios.c_cflag & HUPCL) {
585		ucom_modem(tp, 0, SER_DTR);
586	}
587}
588
589/*
590 * Return values:
591 *    0: normal
592 * else: taskqueue is draining or gone
593 */
594uint8_t
595ucom_cfg_is_gone(struct ucom_softc *sc)
596{
597	struct ucom_super_softc *ssc = sc->sc_super;
598
599	return (usb_proc_is_gone(&ssc->sc_tq));
600}
601
602static void
603ucom_cfg_start_transfers(struct usb_proc_msg *_task)
604{
605	struct ucom_cfg_task *task =
606	    (struct ucom_cfg_task *)_task;
607	struct ucom_softc *sc = task->sc;
608
609	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
610		return;
611	}
612	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
613		/* TTY device closed */
614		return;
615	}
616
617	if (_task == sc->sc_last_start_xfer)
618		sc->sc_flag |= UCOM_FLAG_GP_DATA;
619
620	if (sc->sc_callback->ucom_start_read) {
621		(sc->sc_callback->ucom_start_read) (sc);
622	}
623	if (sc->sc_callback->ucom_start_write) {
624		(sc->sc_callback->ucom_start_write) (sc);
625	}
626}
627
628static void
629ucom_start_transfers(struct ucom_softc *sc)
630{
631	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
632		return;
633	}
634	/*
635	 * Make sure that data transfers are started in both
636	 * directions:
637	 */
638	if (sc->sc_callback->ucom_start_read) {
639		(sc->sc_callback->ucom_start_read) (sc);
640	}
641	if (sc->sc_callback->ucom_start_write) {
642		(sc->sc_callback->ucom_start_write) (sc);
643	}
644}
645
646static void
647ucom_cfg_open(struct usb_proc_msg *_task)
648{
649	struct ucom_cfg_task *task =
650	    (struct ucom_cfg_task *)_task;
651	struct ucom_softc *sc = task->sc;
652
653	DPRINTF("\n");
654
655	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
656
657		/* already opened */
658
659	} else {
660
661		sc->sc_flag |= UCOM_FLAG_LL_READY;
662
663		if (sc->sc_callback->ucom_cfg_open) {
664			(sc->sc_callback->ucom_cfg_open) (sc);
665
666			/* wait a little */
667			usb_pause_mtx(sc->sc_mtx, hz / 10);
668		}
669	}
670}
671
672static int
673ucom_open(struct tty *tp)
674{
675	struct ucom_softc *sc = tty_softc(tp);
676	int error;
677
678	UCOM_MTX_ASSERT(sc, MA_OWNED);
679
680	if (sc->sc_flag & UCOM_FLAG_GONE) {
681		return (ENXIO);
682	}
683	if (sc->sc_flag & UCOM_FLAG_HL_READY) {
684		/* already opened */
685		return (0);
686	}
687	DPRINTF("tp = %p\n", tp);
688
689	if (sc->sc_callback->ucom_pre_open) {
690		/*
691		 * give the lower layer a chance to disallow TTY open, for
692		 * example if the device is not present:
693		 */
694		error = (sc->sc_callback->ucom_pre_open) (sc);
695		if (error) {
696			return (error);
697		}
698	}
699	sc->sc_flag |= UCOM_FLAG_HL_READY;
700
701	/* Disable transfers */
702	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
703
704	sc->sc_lsr = 0;
705	sc->sc_msr = 0;
706	sc->sc_mcr = 0;
707
708	/* reset programmed line state */
709	sc->sc_pls_curr = 0;
710	sc->sc_pls_set = 0;
711	sc->sc_pls_clr = 0;
712
713	ucom_queue_command(sc, ucom_cfg_open, NULL,
714	    &sc->sc_open_task[0].hdr,
715	    &sc->sc_open_task[1].hdr);
716
717	/* Queue transfer enable command last */
718	ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
719	    &sc->sc_start_task[0].hdr,
720	    &sc->sc_start_task[1].hdr);
721
722	ucom_modem(tp, SER_DTR | SER_RTS, 0);
723
724	ucom_ring(sc, 0);
725
726	ucom_break(sc, 0);
727
728	ucom_status_change(sc);
729
730	return (0);
731}
732
733static void
734ucom_cfg_close(struct usb_proc_msg *_task)
735{
736	struct ucom_cfg_task *task =
737	    (struct ucom_cfg_task *)_task;
738	struct ucom_softc *sc = task->sc;
739
740	DPRINTF("\n");
741
742	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
743		sc->sc_flag &= ~UCOM_FLAG_LL_READY;
744		if (sc->sc_callback->ucom_cfg_close)
745			(sc->sc_callback->ucom_cfg_close) (sc);
746	} else {
747		/* already closed */
748	}
749}
750
751static void
752ucom_close(struct tty *tp)
753{
754	struct ucom_softc *sc = tty_softc(tp);
755
756	UCOM_MTX_ASSERT(sc, MA_OWNED);
757
758	DPRINTF("tp=%p\n", tp);
759
760	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
761		DPRINTF("tp=%p already closed\n", tp);
762		return;
763	}
764	ucom_shutdown(sc);
765
766	ucom_queue_command(sc, ucom_cfg_close, NULL,
767	    &sc->sc_close_task[0].hdr,
768	    &sc->sc_close_task[1].hdr);
769
770	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
771
772	if (sc->sc_callback->ucom_stop_read) {
773		(sc->sc_callback->ucom_stop_read) (sc);
774	}
775}
776
777static int
778ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
779{
780	struct ucom_softc *sc = tty_softc(tp);
781	int error;
782
783	UCOM_MTX_ASSERT(sc, MA_OWNED);
784
785	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
786		return (EIO);
787	}
788	DPRINTF("cmd = 0x%08lx\n", cmd);
789
790	switch (cmd) {
791#if 0
792	case TIOCSRING:
793		ucom_ring(sc, 1);
794		error = 0;
795		break;
796	case TIOCCRING:
797		ucom_ring(sc, 0);
798		error = 0;
799		break;
800#endif
801	case TIOCSBRK:
802		ucom_break(sc, 1);
803		error = 0;
804		break;
805	case TIOCCBRK:
806		ucom_break(sc, 0);
807		error = 0;
808		break;
809	default:
810		if (sc->sc_callback->ucom_ioctl) {
811			error = (sc->sc_callback->ucom_ioctl)
812			    (sc, cmd, data, 0, td);
813		} else {
814			error = ENOIOCTL;
815		}
816		break;
817	}
818	return (error);
819}
820
821static int
822ucom_modem(struct tty *tp, int sigon, int sigoff)
823{
824	struct ucom_softc *sc = tty_softc(tp);
825	uint8_t onoff;
826
827	UCOM_MTX_ASSERT(sc, MA_OWNED);
828
829	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
830		return (0);
831	}
832	if ((sigon == 0) && (sigoff == 0)) {
833
834		if (sc->sc_mcr & SER_DTR) {
835			sigon |= SER_DTR;
836		}
837		if (sc->sc_mcr & SER_RTS) {
838			sigon |= SER_RTS;
839		}
840		if (sc->sc_msr & SER_CTS) {
841			sigon |= SER_CTS;
842		}
843		if (sc->sc_msr & SER_DCD) {
844			sigon |= SER_DCD;
845		}
846		if (sc->sc_msr & SER_DSR) {
847			sigon |= SER_DSR;
848		}
849		if (sc->sc_msr & SER_RI) {
850			sigon |= SER_RI;
851		}
852		return (sigon);
853	}
854	if (sigon & SER_DTR) {
855		sc->sc_mcr |= SER_DTR;
856	}
857	if (sigoff & SER_DTR) {
858		sc->sc_mcr &= ~SER_DTR;
859	}
860	if (sigon & SER_RTS) {
861		sc->sc_mcr |= SER_RTS;
862	}
863	if (sigoff & SER_RTS) {
864		sc->sc_mcr &= ~SER_RTS;
865	}
866	onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
867	ucom_dtr(sc, onoff);
868
869	onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
870	ucom_rts(sc, onoff);
871
872	return (0);
873}
874
875static void
876ucom_cfg_line_state(struct usb_proc_msg *_task)
877{
878	struct ucom_cfg_task *task =
879	    (struct ucom_cfg_task *)_task;
880	struct ucom_softc *sc = task->sc;
881	uint8_t notch_bits;
882	uint8_t any_bits;
883	uint8_t prev_value;
884	uint8_t last_value;
885	uint8_t mask;
886
887	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
888		return;
889	}
890
891	mask = 0;
892	/* compute callback mask */
893	if (sc->sc_callback->ucom_cfg_set_dtr)
894		mask |= UCOM_LS_DTR;
895	if (sc->sc_callback->ucom_cfg_set_rts)
896		mask |= UCOM_LS_RTS;
897	if (sc->sc_callback->ucom_cfg_set_break)
898		mask |= UCOM_LS_BREAK;
899	if (sc->sc_callback->ucom_cfg_set_ring)
900		mask |= UCOM_LS_RING;
901
902	/* compute the bits we are to program */
903	notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
904	any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
905	prev_value = sc->sc_pls_curr ^ notch_bits;
906	last_value = sc->sc_pls_curr;
907
908	/* reset programmed line state */
909	sc->sc_pls_curr = 0;
910	sc->sc_pls_set = 0;
911	sc->sc_pls_clr = 0;
912
913	/* ensure that we don't loose any levels */
914	if (notch_bits & UCOM_LS_DTR)
915		sc->sc_callback->ucom_cfg_set_dtr(sc,
916		    (prev_value & UCOM_LS_DTR) ? 1 : 0);
917	if (notch_bits & UCOM_LS_RTS)
918		sc->sc_callback->ucom_cfg_set_rts(sc,
919		    (prev_value & UCOM_LS_RTS) ? 1 : 0);
920	if (notch_bits & UCOM_LS_BREAK)
921		sc->sc_callback->ucom_cfg_set_break(sc,
922		    (prev_value & UCOM_LS_BREAK) ? 1 : 0);
923	if (notch_bits & UCOM_LS_RING)
924		sc->sc_callback->ucom_cfg_set_ring(sc,
925		    (prev_value & UCOM_LS_RING) ? 1 : 0);
926
927	/* set last value */
928	if (any_bits & UCOM_LS_DTR)
929		sc->sc_callback->ucom_cfg_set_dtr(sc,
930		    (last_value & UCOM_LS_DTR) ? 1 : 0);
931	if (any_bits & UCOM_LS_RTS)
932		sc->sc_callback->ucom_cfg_set_rts(sc,
933		    (last_value & UCOM_LS_RTS) ? 1 : 0);
934	if (any_bits & UCOM_LS_BREAK)
935		sc->sc_callback->ucom_cfg_set_break(sc,
936		    (last_value & UCOM_LS_BREAK) ? 1 : 0);
937	if (any_bits & UCOM_LS_RING)
938		sc->sc_callback->ucom_cfg_set_ring(sc,
939		    (last_value & UCOM_LS_RING) ? 1 : 0);
940}
941
942static void
943ucom_line_state(struct ucom_softc *sc,
944    uint8_t set_bits, uint8_t clear_bits)
945{
946	UCOM_MTX_ASSERT(sc, MA_OWNED);
947
948	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
949		return;
950	}
951
952	DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
953
954	/* update current programmed line state */
955	sc->sc_pls_curr |= set_bits;
956	sc->sc_pls_curr &= ~clear_bits;
957	sc->sc_pls_set |= set_bits;
958	sc->sc_pls_clr |= clear_bits;
959
960	/* defer driver programming */
961	ucom_queue_command(sc, ucom_cfg_line_state, NULL,
962	    &sc->sc_line_state_task[0].hdr,
963	    &sc->sc_line_state_task[1].hdr);
964}
965
966static void
967ucom_ring(struct ucom_softc *sc, uint8_t onoff)
968{
969	DPRINTF("onoff = %d\n", onoff);
970
971	if (onoff)
972		ucom_line_state(sc, UCOM_LS_RING, 0);
973	else
974		ucom_line_state(sc, 0, UCOM_LS_RING);
975}
976
977static void
978ucom_break(struct ucom_softc *sc, uint8_t onoff)
979{
980	DPRINTF("onoff = %d\n", onoff);
981
982	if (onoff)
983		ucom_line_state(sc, UCOM_LS_BREAK, 0);
984	else
985		ucom_line_state(sc, 0, UCOM_LS_BREAK);
986}
987
988static void
989ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
990{
991	DPRINTF("onoff = %d\n", onoff);
992
993	if (onoff)
994		ucom_line_state(sc, UCOM_LS_DTR, 0);
995	else
996		ucom_line_state(sc, 0, UCOM_LS_DTR);
997}
998
999static void
1000ucom_rts(struct ucom_softc *sc, uint8_t onoff)
1001{
1002	DPRINTF("onoff = %d\n", onoff);
1003
1004	if (onoff)
1005		ucom_line_state(sc, UCOM_LS_RTS, 0);
1006	else
1007		ucom_line_state(sc, 0, UCOM_LS_RTS);
1008}
1009
1010static void
1011ucom_cfg_status_change(struct usb_proc_msg *_task)
1012{
1013	struct ucom_cfg_task *task =
1014	    (struct ucom_cfg_task *)_task;
1015	struct ucom_softc *sc = task->sc;
1016	struct tty *tp;
1017	uint8_t new_msr;
1018	uint8_t new_lsr;
1019	uint8_t onoff;
1020	uint8_t lsr_delta;
1021
1022	tp = sc->sc_tty;
1023
1024	UCOM_MTX_ASSERT(sc, MA_OWNED);
1025
1026	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1027		return;
1028	}
1029	if (sc->sc_callback->ucom_cfg_get_status == NULL) {
1030		return;
1031	}
1032	/* get status */
1033
1034	new_msr = 0;
1035	new_lsr = 0;
1036
1037	(sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
1038
1039	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1040		/* TTY device closed */
1041		return;
1042	}
1043	onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
1044	lsr_delta = (sc->sc_lsr ^ new_lsr);
1045
1046	sc->sc_msr = new_msr;
1047	sc->sc_lsr = new_lsr;
1048
1049	if (onoff) {
1050
1051		onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
1052
1053		DPRINTF("DCD changed to %d\n", onoff);
1054
1055		ttydisc_modem(tp, onoff);
1056	}
1057
1058	if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
1059
1060		DPRINTF("BREAK detected\n");
1061
1062		ttydisc_rint(tp, 0, TRE_BREAK);
1063		ttydisc_rint_done(tp);
1064	}
1065
1066	if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) {
1067
1068		DPRINTF("Frame error detected\n");
1069
1070		ttydisc_rint(tp, 0, TRE_FRAMING);
1071		ttydisc_rint_done(tp);
1072	}
1073
1074	if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) {
1075
1076		DPRINTF("Parity error detected\n");
1077
1078		ttydisc_rint(tp, 0, TRE_PARITY);
1079		ttydisc_rint_done(tp);
1080	}
1081}
1082
1083void
1084ucom_status_change(struct ucom_softc *sc)
1085{
1086	UCOM_MTX_ASSERT(sc, MA_OWNED);
1087
1088	if (sc->sc_flag & UCOM_FLAG_CONSOLE)
1089		return;		/* not supported */
1090
1091	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1092		return;
1093	}
1094	DPRINTF("\n");
1095
1096	ucom_queue_command(sc, ucom_cfg_status_change, NULL,
1097	    &sc->sc_status_task[0].hdr,
1098	    &sc->sc_status_task[1].hdr);
1099}
1100
1101static void
1102ucom_cfg_param(struct usb_proc_msg *_task)
1103{
1104	struct ucom_param_task *task =
1105	    (struct ucom_param_task *)_task;
1106	struct ucom_softc *sc = task->sc;
1107
1108	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1109		return;
1110	}
1111	if (sc->sc_callback->ucom_cfg_param == NULL) {
1112		return;
1113	}
1114
1115	(sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
1116
1117	/* wait a little */
1118	usb_pause_mtx(sc->sc_mtx, hz / 10);
1119}
1120
1121static int
1122ucom_param(struct tty *tp, struct termios *t)
1123{
1124	struct ucom_softc *sc = tty_softc(tp);
1125	uint8_t opened;
1126	int error;
1127
1128	UCOM_MTX_ASSERT(sc, MA_OWNED);
1129
1130	opened = 0;
1131	error = 0;
1132
1133	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1134
1135		/* XXX the TTY layer should call "open()" first! */
1136
1137		error = ucom_open(tp);
1138		if (error) {
1139			goto done;
1140		}
1141		opened = 1;
1142	}
1143	DPRINTF("sc = %p\n", sc);
1144
1145	/* Check requested parameters. */
1146	if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
1147		DPRINTF("mismatch ispeed and ospeed\n");
1148		error = EINVAL;
1149		goto done;
1150	}
1151	t->c_ispeed = t->c_ospeed;
1152
1153	if (sc->sc_callback->ucom_pre_param) {
1154		/* Let the lower layer verify the parameters */
1155		error = (sc->sc_callback->ucom_pre_param) (sc, t);
1156		if (error) {
1157			DPRINTF("callback error = %d\n", error);
1158			goto done;
1159		}
1160	}
1161
1162	/* Disable transfers */
1163	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
1164
1165	/* Queue baud rate programming command first */
1166	ucom_queue_command(sc, ucom_cfg_param, t,
1167	    &sc->sc_param_task[0].hdr,
1168	    &sc->sc_param_task[1].hdr);
1169
1170	/* Queue transfer enable command last */
1171	ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
1172	    &sc->sc_start_task[0].hdr,
1173	    &sc->sc_start_task[1].hdr);
1174
1175	if (t->c_cflag & CRTS_IFLOW) {
1176		sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
1177	} else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
1178		sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
1179		ucom_modem(tp, SER_RTS, 0);
1180	}
1181done:
1182	if (error) {
1183		if (opened) {
1184			ucom_close(tp);
1185		}
1186	}
1187	return (error);
1188}
1189
1190static void
1191ucom_outwakeup(struct tty *tp)
1192{
1193	struct ucom_softc *sc = tty_softc(tp);
1194
1195	UCOM_MTX_ASSERT(sc, MA_OWNED);
1196
1197	DPRINTF("sc = %p\n", sc);
1198
1199	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1200		/* The higher layer is not ready */
1201		return;
1202	}
1203	ucom_start_transfers(sc);
1204}
1205
1206/*------------------------------------------------------------------------*
1207 *	ucom_get_data
1208 *
1209 * Return values:
1210 * 0: No data is available.
1211 * Else: Data is available.
1212 *------------------------------------------------------------------------*/
1213uint8_t
1214ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1215    uint32_t offset, uint32_t len, uint32_t *actlen)
1216{
1217	struct usb_page_search res;
1218	struct tty *tp = sc->sc_tty;
1219	uint32_t cnt;
1220	uint32_t offset_orig;
1221
1222	UCOM_MTX_ASSERT(sc, MA_OWNED);
1223
1224	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1225		unsigned int temp;
1226
1227		/* get total TX length */
1228
1229		temp = ucom_cons_tx_high - ucom_cons_tx_low;
1230		temp %= UCOM_CONS_BUFSIZE;
1231
1232		/* limit TX length */
1233
1234		if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low))
1235			temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
1236
1237		if (temp > len)
1238			temp = len;
1239
1240		/* copy in data */
1241
1242		usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
1243
1244		/* update counters */
1245
1246		ucom_cons_tx_low += temp;
1247		ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
1248
1249		/* store actual length */
1250
1251		*actlen = temp;
1252
1253		return (temp ? 1 : 0);
1254	}
1255
1256	if (tty_gone(tp) ||
1257	    !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
1258		actlen[0] = 0;
1259		return (0);		/* multiport device polling */
1260	}
1261	offset_orig = offset;
1262
1263	while (len != 0) {
1264
1265		usbd_get_page(pc, offset, &res);
1266
1267		if (res.length > len) {
1268			res.length = len;
1269		}
1270		/* copy data directly into USB buffer */
1271		cnt = ttydisc_getc(tp, res.buffer, res.length);
1272
1273		offset += cnt;
1274		len -= cnt;
1275
1276		if (cnt < res.length) {
1277			/* end of buffer */
1278			break;
1279		}
1280	}
1281
1282	actlen[0] = offset - offset_orig;
1283
1284	DPRINTF("cnt=%d\n", actlen[0]);
1285
1286	if (actlen[0] == 0) {
1287		return (0);
1288	}
1289	return (1);
1290}
1291
1292void
1293ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1294    uint32_t offset, uint32_t len)
1295{
1296	struct usb_page_search res;
1297	struct tty *tp = sc->sc_tty;
1298	char *buf;
1299	uint32_t cnt;
1300
1301	UCOM_MTX_ASSERT(sc, MA_OWNED);
1302
1303	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1304		unsigned int temp;
1305
1306		/* get maximum RX length */
1307
1308		temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
1309		temp %= UCOM_CONS_BUFSIZE;
1310
1311		/* limit RX length */
1312
1313		if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high))
1314			temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
1315
1316		if (temp > len)
1317			temp = len;
1318
1319		/* copy out data */
1320
1321		usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
1322
1323		/* update counters */
1324
1325		ucom_cons_rx_high += temp;
1326		ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
1327
1328		return;
1329	}
1330
1331	if (tty_gone(tp))
1332		return;			/* multiport device polling */
1333
1334	if (len == 0)
1335		return;			/* no data */
1336
1337	/* set a flag to prevent recursation ? */
1338
1339	while (len > 0) {
1340
1341		usbd_get_page(pc, offset, &res);
1342
1343		if (res.length > len) {
1344			res.length = len;
1345		}
1346		len -= res.length;
1347		offset += res.length;
1348
1349		/* pass characters to tty layer */
1350
1351		buf = res.buffer;
1352		cnt = res.length;
1353
1354		/* first check if we can pass the buffer directly */
1355
1356		if (ttydisc_can_bypass(tp)) {
1357			if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1358				DPRINTF("tp=%p, data lost\n", tp);
1359			}
1360			continue;
1361		}
1362		/* need to loop */
1363
1364		for (cnt = 0; cnt != res.length; cnt++) {
1365			if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
1366				/* XXX what should we do? */
1367
1368				DPRINTF("tp=%p, lost %d "
1369				    "chars\n", tp, res.length - cnt);
1370				break;
1371			}
1372		}
1373	}
1374	ttydisc_rint_done(tp);
1375}
1376
1377static void
1378ucom_free(void *xsc)
1379{
1380	struct ucom_softc *sc = xsc;
1381
1382	if (sc->sc_callback->ucom_free != NULL)
1383		sc->sc_callback->ucom_free(sc);
1384	else
1385		ucom_unref(sc->sc_super);
1386
1387	mtx_lock(&ucom_mtx);
1388	ucom_close_refs--;
1389	mtx_unlock(&ucom_mtx);
1390}
1391
1392static cn_probe_t ucom_cnprobe;
1393static cn_init_t ucom_cninit;
1394static cn_term_t ucom_cnterm;
1395static cn_getc_t ucom_cngetc;
1396static cn_putc_t ucom_cnputc;
1397static cn_grab_t ucom_cngrab;
1398static cn_ungrab_t ucom_cnungrab;
1399
1400CONSOLE_DRIVER(ucom);
1401
1402static void
1403ucom_cnprobe(struct consdev  *cp)
1404{
1405	if (ucom_cons_unit != -1)
1406		cp->cn_pri = CN_NORMAL;
1407	else
1408		cp->cn_pri = CN_DEAD;
1409
1410	strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name));
1411}
1412
1413static void
1414ucom_cninit(struct consdev  *cp)
1415{
1416}
1417
1418static void
1419ucom_cnterm(struct consdev  *cp)
1420{
1421}
1422
1423static void
1424ucom_cngrab(struct consdev *cp)
1425{
1426}
1427
1428static void
1429ucom_cnungrab(struct consdev *cp)
1430{
1431}
1432
1433static int
1434ucom_cngetc(struct consdev *cd)
1435{
1436	struct ucom_softc *sc = ucom_cons_softc;
1437	int c;
1438
1439	if (sc == NULL)
1440		return (-1);
1441
1442	UCOM_MTX_LOCK(sc);
1443
1444	if (ucom_cons_rx_low != ucom_cons_rx_high) {
1445		c = ucom_cons_rx_buf[ucom_cons_rx_low];
1446		ucom_cons_rx_low ++;
1447		ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
1448	} else {
1449		c = -1;
1450	}
1451
1452	/* start USB transfers */
1453	ucom_outwakeup(sc->sc_tty);
1454
1455	UCOM_MTX_UNLOCK(sc);
1456
1457	/* poll if necessary */
1458	if (kdb_active && sc->sc_callback->ucom_poll)
1459		(sc->sc_callback->ucom_poll) (sc);
1460
1461	return (c);
1462}
1463
1464static void
1465ucom_cnputc(struct consdev *cd, int c)
1466{
1467	struct ucom_softc *sc = ucom_cons_softc;
1468	unsigned int temp;
1469
1470	if (sc == NULL)
1471		return;
1472
1473 repeat:
1474
1475	UCOM_MTX_LOCK(sc);
1476
1477	/* compute maximum TX length */
1478
1479	temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
1480	temp %= UCOM_CONS_BUFSIZE;
1481
1482	if (temp) {
1483		ucom_cons_tx_buf[ucom_cons_tx_high] = c;
1484		ucom_cons_tx_high ++;
1485		ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
1486	}
1487
1488	/* start USB transfers */
1489	ucom_outwakeup(sc->sc_tty);
1490
1491	UCOM_MTX_UNLOCK(sc);
1492
1493	/* poll if necessary */
1494	if (kdb_active && sc->sc_callback->ucom_poll) {
1495		(sc->sc_callback->ucom_poll) (sc);
1496		/* simple flow control */
1497		if (temp == 0)
1498			goto repeat;
1499	}
1500}
1501
1502/*------------------------------------------------------------------------*
1503 *	ucom_ref
1504 *
1505 * This function will increment the super UCOM reference count.
1506 *------------------------------------------------------------------------*/
1507void
1508ucom_ref(struct ucom_super_softc *ssc)
1509{
1510	mtx_lock(&ucom_mtx);
1511	ssc->sc_refs++;
1512	mtx_unlock(&ucom_mtx);
1513}
1514
1515/*------------------------------------------------------------------------*
1516 *	ucom_unref
1517 *
1518 * This function will decrement the super UCOM reference count.
1519 *
1520 * Return values:
1521 * 0: UCOM structures are still referenced.
1522 * Else: UCOM structures are no longer referenced.
1523 *------------------------------------------------------------------------*/
1524int
1525ucom_unref(struct ucom_super_softc *ssc)
1526{
1527	int retval;
1528	int free_unit;
1529
1530	mtx_lock(&ucom_mtx);
1531	retval = (ssc->sc_refs < 2);
1532	free_unit = (ssc->sc_refs == 1);
1533	ssc->sc_refs--;
1534	mtx_unlock(&ucom_mtx);
1535
1536	/*
1537	 * This function might be called when the "ssc" is only zero
1538	 * initialized and in that case the unit number should not be
1539	 * freed.
1540	 */
1541	if (free_unit)
1542		ucom_unit_free(ssc->sc_unit);
1543	return (retval);
1544}
1545
1546#if defined(GDB)
1547
1548#include <gdb/gdb.h>
1549
1550static gdb_probe_f ucom_gdbprobe;
1551static gdb_init_f ucom_gdbinit;
1552static gdb_term_f ucom_gdbterm;
1553static gdb_getc_f ucom_gdbgetc;
1554static gdb_putc_f ucom_gdbputc;
1555
1556GDB_DBGPORT(sio, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc);
1557
1558static int
1559ucom_gdbprobe(void)
1560{
1561	return ((ucom_cons_softc != NULL) ? 0 : -1);
1562}
1563
1564static void
1565ucom_gdbinit(void)
1566{
1567}
1568
1569static void
1570ucom_gdbterm(void)
1571{
1572}
1573
1574static void
1575ucom_gdbputc(int c)
1576{
1577        ucom_cnputc(NULL, c);
1578}
1579
1580static int
1581ucom_gdbgetc(void)
1582{
1583        return (ucom_cngetc(NULL));
1584}
1585
1586#endif
1587