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