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