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