xref: /freebsd/sys/dev/usb/serial/usb_serial.c (revision 830940567b49bb0c08dfaed40418999e76616909)
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  * 3. All advertising materials mentioning features or use of this software
50  *    must display the following acknowledgement:
51  *        This product includes software developed by the NetBSD
52  *        Foundation, Inc. and its contributors.
53  * 4. Neither the name of The NetBSD Foundation nor the names of its
54  *    contributors may be used to endorse or promote products derived
55  *    from this software without specific prior written permission.
56  *
57  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
58  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
59  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
60  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
61  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
62  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
63  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
64  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
66  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
67  * POSSIBILITY OF SUCH DAMAGE.
68  */
69 
70 #include <sys/stdint.h>
71 #include <sys/stddef.h>
72 #include <sys/param.h>
73 #include <sys/queue.h>
74 #include <sys/types.h>
75 #include <sys/systm.h>
76 #include <sys/kernel.h>
77 #include <sys/bus.h>
78 #include <sys/linker_set.h>
79 #include <sys/module.h>
80 #include <sys/lock.h>
81 #include <sys/mutex.h>
82 #include <sys/condvar.h>
83 #include <sys/sysctl.h>
84 #include <sys/sx.h>
85 #include <sys/unistd.h>
86 #include <sys/callout.h>
87 #include <sys/malloc.h>
88 #include <sys/priv.h>
89 
90 #include <dev/usb/usb.h>
91 #include <dev/usb/usbdi.h>
92 #include <dev/usb/usbdi_util.h>
93 
94 #define	USB_DEBUG_VAR ucom_debug
95 #include <dev/usb/usb_debug.h>
96 #include <dev/usb/usb_busdma.h>
97 #include <dev/usb/usb_process.h>
98 
99 #include <dev/usb/serial/usb_serial.h>
100 
101 #if USB_DEBUG
102 static int ucom_debug = 0;
103 
104 SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
105 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
106     &ucom_debug, 0, "ucom debug level");
107 #endif
108 
109 static usb_proc_callback_t ucom_cfg_start_transfers;
110 static usb_proc_callback_t ucom_cfg_open;
111 static usb_proc_callback_t ucom_cfg_close;
112 static usb_proc_callback_t ucom_cfg_line_state;
113 static usb_proc_callback_t ucom_cfg_status_change;
114 static usb_proc_callback_t ucom_cfg_param;
115 
116 static uint8_t	ucom_units_alloc(uint32_t, uint32_t *);
117 static void	ucom_units_free(uint32_t, uint32_t);
118 static int	ucom_attach_tty(struct ucom_softc *, uint32_t);
119 static void	ucom_detach_tty(struct ucom_softc *);
120 static void	ucom_queue_command(struct ucom_softc *,
121 		    usb_proc_callback_t *, struct termios *pt,
122 		    struct usb_proc_msg *t0, struct usb_proc_msg *t1);
123 static void	ucom_shutdown(struct ucom_softc *);
124 static void	ucom_break(struct ucom_softc *, uint8_t);
125 static void	ucom_dtr(struct ucom_softc *, uint8_t);
126 static void	ucom_rts(struct ucom_softc *, uint8_t);
127 
128 static tsw_open_t ucom_open;
129 static tsw_close_t ucom_close;
130 static tsw_ioctl_t ucom_ioctl;
131 static tsw_modem_t ucom_modem;
132 static tsw_param_t ucom_param;
133 static tsw_outwakeup_t ucom_outwakeup;
134 static tsw_free_t ucom_free;
135 
136 static struct ttydevsw ucom_class = {
137 	.tsw_flags = TF_INITLOCK | TF_CALLOUT,
138 	.tsw_open = ucom_open,
139 	.tsw_close = ucom_close,
140 	.tsw_outwakeup = ucom_outwakeup,
141 	.tsw_ioctl = ucom_ioctl,
142 	.tsw_param = ucom_param,
143 	.tsw_modem = ucom_modem,
144 	.tsw_free = ucom_free,
145 };
146 
147 MODULE_DEPEND(ucom, usb, 1, 1, 1);
148 MODULE_VERSION(ucom, 1);
149 
150 #define	UCOM_UNIT_MAX 0x1000		/* exclusive */
151 #define	UCOM_SUB_UNIT_MAX 0x100		/* exclusive */
152 
153 static uint8_t ucom_bitmap[(UCOM_UNIT_MAX + 7) / 8];
154 static struct mtx ucom_bitmap_mtx;
155 MTX_SYSINIT(ucom_bitmap_mtx, &ucom_bitmap_mtx, "ucom bitmap", MTX_DEF);
156 
157 static uint8_t
158 ucom_units_alloc(uint32_t sub_units, uint32_t *p_root_unit)
159 {
160 	uint32_t n;
161 	uint32_t o;
162 	uint32_t x;
163 	uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units);
164 	uint8_t error = 1;
165 
166 	mtx_lock(&ucom_bitmap_mtx);
167 
168 	for (n = 0; n < max; n += sub_units) {
169 
170 		/* check for free consecutive bits */
171 
172 		for (o = 0; o < sub_units; o++) {
173 
174 			x = n + o;
175 
176 			if (ucom_bitmap[x / 8] & (1 << (x % 8))) {
177 				goto skip;
178 			}
179 		}
180 
181 		/* allocate */
182 
183 		for (o = 0; o < sub_units; o++) {
184 
185 			x = n + o;
186 
187 			ucom_bitmap[x / 8] |= (1 << (x % 8));
188 		}
189 
190 		error = 0;
191 
192 		break;
193 
194 skip:		;
195 	}
196 
197 	mtx_unlock(&ucom_bitmap_mtx);
198 
199 	/*
200 	 * Always set the variable pointed to by "p_root_unit" so that
201 	 * the compiler does not think that it is used uninitialised:
202 	 */
203 	*p_root_unit = n;
204 
205 	return (error);
206 }
207 
208 static void
209 ucom_units_free(uint32_t root_unit, uint32_t sub_units)
210 {
211 	uint32_t x;
212 
213 	mtx_lock(&ucom_bitmap_mtx);
214 
215 	while (sub_units--) {
216 		x = root_unit + sub_units;
217 		ucom_bitmap[x / 8] &= ~(1 << (x % 8));
218 	}
219 
220 	mtx_unlock(&ucom_bitmap_mtx);
221 }
222 
223 /*
224  * "N" sub_units are setup at a time. All sub-units will
225  * be given sequential unit numbers. The number of
226  * sub-units can be used to differentiate among
227  * different types of devices.
228  *
229  * The mutex pointed to by "mtx" is applied before all
230  * callbacks are called back. Also "mtx" must be applied
231  * before calling into the ucom-layer!
232  */
233 int
234 ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
235     uint32_t sub_units, void *parent,
236     const struct ucom_callback *callback, struct mtx *mtx)
237 {
238 	uint32_t n;
239 	uint32_t root_unit;
240 	int error = 0;
241 
242 	if ((sc == NULL) ||
243 	    (sub_units == 0) ||
244 	    (sub_units > UCOM_SUB_UNIT_MAX) ||
245 	    (callback == NULL)) {
246 		return (EINVAL);
247 	}
248 
249 	/* XXX unit management does not really belong here */
250 	if (ucom_units_alloc(sub_units, &root_unit)) {
251 		return (ENOMEM);
252 	}
253 
254 	error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
255 	if (error) {
256 		ucom_units_free(root_unit, sub_units);
257 		return (error);
258 	}
259 
260 	for (n = 0; n != sub_units; n++, sc++) {
261 		sc->sc_unit = root_unit + n;
262 		sc->sc_local_unit = n;
263 		sc->sc_super = ssc;
264 		sc->sc_mtx = mtx;
265 		sc->sc_parent = parent;
266 		sc->sc_callback = callback;
267 
268 		error = ucom_attach_tty(sc, sub_units);
269 		if (error) {
270 			ucom_detach(ssc, sc - n, n);
271 			ucom_units_free(root_unit + n, sub_units - n);
272 			return (error);
273 		}
274 		sc->sc_flag |= UCOM_FLAG_ATTACHED;
275 	}
276 	return (0);
277 }
278 
279 /*
280  * NOTE: the following function will do nothing if
281  * the structure pointed to by "ssc" and "sc" is zero.
282  */
283 void
284 ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
285     uint32_t sub_units)
286 {
287 	uint32_t n;
288 
289 	usb_proc_drain(&ssc->sc_tq);
290 
291 	for (n = 0; n != sub_units; n++, sc++) {
292 		if (sc->sc_flag & UCOM_FLAG_ATTACHED) {
293 
294 			ucom_detach_tty(sc);
295 
296 			ucom_units_free(sc->sc_unit, 1);
297 
298 			/* avoid duplicate detach: */
299 			sc->sc_flag &= ~UCOM_FLAG_ATTACHED;
300 		}
301 	}
302 	usb_proc_free(&ssc->sc_tq);
303 }
304 
305 static int
306 ucom_attach_tty(struct ucom_softc *sc, uint32_t sub_units)
307 {
308 	struct tty *tp;
309 	int error = 0;
310 	char buf[32];			/* temporary TTY device name buffer */
311 
312 	tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx);
313 	if (tp == NULL) {
314 		error = ENOMEM;
315 		goto done;
316 	}
317 	DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit);
318 
319 	buf[0] = 0;			/* set some default value */
320 
321 	/* Check if the client has a custom TTY name */
322 	if (sc->sc_callback->ucom_tty_name) {
323 		sc->sc_callback->ucom_tty_name(sc, buf,
324 		    sizeof(buf), sc->sc_local_unit);
325 	}
326 	if (buf[0] == 0) {
327 		/* Use default TTY name */
328 		if (sub_units > 1) {
329 			/* multiple modems in one */
330 			if (snprintf(buf, sizeof(buf), "U%u.%u",
331 			    sc->sc_unit - sc->sc_local_unit,
332 			    sc->sc_local_unit)) {
333 				/* ignore */
334 			}
335 		} else {
336 			/* single modem */
337 			if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) {
338 				/* ignore */
339 			}
340 		}
341 	}
342 	tty_makedev(tp, NULL, "%s", buf);
343 
344 	sc->sc_tty = tp;
345 
346 	DPRINTF("ttycreate: %s\n", buf);
347 	cv_init(&sc->sc_cv, "ucom");
348 
349 done:
350 	return (error);
351 }
352 
353 static void
354 ucom_detach_tty(struct ucom_softc *sc)
355 {
356 	struct tty *tp = sc->sc_tty;
357 
358 	DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
359 
360 	/* the config thread has been stopped when we get here */
361 
362 	mtx_lock(sc->sc_mtx);
363 	sc->sc_flag |= UCOM_FLAG_GONE;
364 	sc->sc_flag &= ~(UCOM_FLAG_HL_READY |
365 	    UCOM_FLAG_LL_READY);
366 	mtx_unlock(sc->sc_mtx);
367 	if (tp) {
368 		tty_lock(tp);
369 
370 		ucom_close(tp);	/* close, if any */
371 
372 		tty_rel_gone(tp);
373 
374 		mtx_lock(sc->sc_mtx);
375 		/* Wait for the callback after the TTY is torn down */
376 		while (sc->sc_ttyfreed == 0)
377 			cv_wait(&sc->sc_cv, sc->sc_mtx);
378 		/*
379 		 * make sure that read and write transfers are stopped
380 		 */
381 		if (sc->sc_callback->ucom_stop_read) {
382 			(sc->sc_callback->ucom_stop_read) (sc);
383 		}
384 		if (sc->sc_callback->ucom_stop_write) {
385 			(sc->sc_callback->ucom_stop_write) (sc);
386 		}
387 		mtx_unlock(sc->sc_mtx);
388 	}
389 	cv_destroy(&sc->sc_cv);
390 }
391 
392 static void
393 ucom_queue_command(struct ucom_softc *sc,
394     usb_proc_callback_t *fn, struct termios *pt,
395     struct usb_proc_msg *t0, struct usb_proc_msg *t1)
396 {
397 	struct ucom_super_softc *ssc = sc->sc_super;
398 	struct ucom_param_task *task;
399 
400 	mtx_assert(sc->sc_mtx, MA_OWNED);
401 
402 	if (usb_proc_is_gone(&ssc->sc_tq)) {
403 		DPRINTF("proc is gone\n");
404 		return;         /* nothing to do */
405 	}
406 	/*
407 	 * NOTE: The task cannot get executed before we drop the
408 	 * "sc_mtx" mutex. It is safe to update fields in the message
409 	 * structure after that the message got queued.
410 	 */
411 	task = (struct ucom_param_task *)
412 	  usb_proc_msignal(&ssc->sc_tq, t0, t1);
413 
414 	/* Setup callback and softc pointers */
415 	task->hdr.pm_callback = fn;
416 	task->sc = sc;
417 
418 	/*
419 	 * Make a copy of the termios. This field is only present if
420 	 * the "pt" field is not NULL.
421 	 */
422 	if (pt != NULL)
423 		task->termios_copy = *pt;
424 
425 	/*
426 	 * Closing the device should be synchronous.
427 	 */
428 	if (fn == ucom_cfg_close)
429 		usb_proc_mwait(&ssc->sc_tq, t0, t1);
430 
431 	/*
432 	 * In case of multiple configure requests,
433 	 * keep track of the last one!
434 	 */
435 	if (fn == ucom_cfg_start_transfers)
436 		sc->sc_last_start_xfer = &task->hdr;
437 }
438 
439 static void
440 ucom_shutdown(struct ucom_softc *sc)
441 {
442 	struct tty *tp = sc->sc_tty;
443 
444 	mtx_assert(sc->sc_mtx, MA_OWNED);
445 
446 	DPRINTF("\n");
447 
448 	/*
449 	 * Hang up if necessary:
450 	 */
451 	if (tp->t_termios.c_cflag & HUPCL) {
452 		ucom_modem(tp, 0, SER_DTR);
453 	}
454 }
455 
456 /*
457  * Return values:
458  *    0: normal
459  * else: taskqueue is draining or gone
460  */
461 uint8_t
462 ucom_cfg_is_gone(struct ucom_softc *sc)
463 {
464 	struct ucom_super_softc *ssc = sc->sc_super;
465 
466 	return (usb_proc_is_gone(&ssc->sc_tq));
467 }
468 
469 static void
470 ucom_cfg_start_transfers(struct usb_proc_msg *_task)
471 {
472 	struct ucom_cfg_task *task =
473 	    (struct ucom_cfg_task *)_task;
474 	struct ucom_softc *sc = task->sc;
475 
476 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
477 		return;
478 	}
479 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
480 		/* TTY device closed */
481 		return;
482 	}
483 
484 	if (_task == sc->sc_last_start_xfer)
485 		sc->sc_flag |= UCOM_FLAG_GP_DATA;
486 
487 	if (sc->sc_callback->ucom_start_read) {
488 		(sc->sc_callback->ucom_start_read) (sc);
489 	}
490 	if (sc->sc_callback->ucom_start_write) {
491 		(sc->sc_callback->ucom_start_write) (sc);
492 	}
493 }
494 
495 static void
496 ucom_start_transfers(struct ucom_softc *sc)
497 {
498 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
499 		return;
500 	}
501 	/*
502 	 * Make sure that data transfers are started in both
503 	 * directions:
504 	 */
505 	if (sc->sc_callback->ucom_start_read) {
506 		(sc->sc_callback->ucom_start_read) (sc);
507 	}
508 	if (sc->sc_callback->ucom_start_write) {
509 		(sc->sc_callback->ucom_start_write) (sc);
510 	}
511 }
512 
513 static void
514 ucom_cfg_open(struct usb_proc_msg *_task)
515 {
516 	struct ucom_cfg_task *task =
517 	    (struct ucom_cfg_task *)_task;
518 	struct ucom_softc *sc = task->sc;
519 
520 	DPRINTF("\n");
521 
522 	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
523 
524 		/* already opened */
525 
526 	} else {
527 
528 		sc->sc_flag |= UCOM_FLAG_LL_READY;
529 
530 		if (sc->sc_callback->ucom_cfg_open) {
531 			(sc->sc_callback->ucom_cfg_open) (sc);
532 
533 			/* wait a little */
534 			usb_pause_mtx(sc->sc_mtx, hz / 10);
535 		}
536 	}
537 }
538 
539 static int
540 ucom_open(struct tty *tp)
541 {
542 	struct ucom_softc *sc = tty_softc(tp);
543 	int error;
544 
545 	mtx_assert(sc->sc_mtx, MA_OWNED);
546 
547 	if (sc->sc_flag & UCOM_FLAG_GONE) {
548 		return (ENXIO);
549 	}
550 	if (sc->sc_flag & UCOM_FLAG_HL_READY) {
551 		/* already opened */
552 		return (0);
553 	}
554 	DPRINTF("tp = %p\n", tp);
555 
556 	if (sc->sc_callback->ucom_pre_open) {
557 		/*
558 		 * give the lower layer a chance to disallow TTY open, for
559 		 * example if the device is not present:
560 		 */
561 		error = (sc->sc_callback->ucom_pre_open) (sc);
562 		if (error) {
563 			return (error);
564 		}
565 	}
566 	sc->sc_flag |= UCOM_FLAG_HL_READY;
567 
568 	/* Disable transfers */
569 	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
570 
571 	sc->sc_lsr = 0;
572 	sc->sc_msr = 0;
573 	sc->sc_mcr = 0;
574 
575 	/* reset programmed line state */
576 	sc->sc_pls_curr = 0;
577 	sc->sc_pls_set = 0;
578 	sc->sc_pls_clr = 0;
579 
580 	ucom_queue_command(sc, ucom_cfg_open, NULL,
581 	    &sc->sc_open_task[0].hdr,
582 	    &sc->sc_open_task[1].hdr);
583 
584 	/* Queue transfer enable command last */
585 	ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
586 	    &sc->sc_start_task[0].hdr,
587 	    &sc->sc_start_task[1].hdr);
588 
589 	ucom_modem(tp, SER_DTR | SER_RTS, 0);
590 
591 	ucom_break(sc, 0);
592 
593 	ucom_status_change(sc);
594 
595 	return (0);
596 }
597 
598 static void
599 ucom_cfg_close(struct usb_proc_msg *_task)
600 {
601 	struct ucom_cfg_task *task =
602 	    (struct ucom_cfg_task *)_task;
603 	struct ucom_softc *sc = task->sc;
604 
605 	DPRINTF("\n");
606 
607 	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
608 		sc->sc_flag &= ~UCOM_FLAG_LL_READY;
609 		if (sc->sc_callback->ucom_cfg_close)
610 			(sc->sc_callback->ucom_cfg_close) (sc);
611 	} else {
612 		/* already closed */
613 	}
614 }
615 
616 static void
617 ucom_close(struct tty *tp)
618 {
619 	struct ucom_softc *sc = tty_softc(tp);
620 
621 	mtx_assert(sc->sc_mtx, MA_OWNED);
622 
623 	DPRINTF("tp=%p\n", tp);
624 
625 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
626 		DPRINTF("tp=%p already closed\n", tp);
627 		return;
628 	}
629 	ucom_shutdown(sc);
630 
631 	ucom_queue_command(sc, ucom_cfg_close, NULL,
632 	    &sc->sc_close_task[0].hdr,
633 	    &sc->sc_close_task[1].hdr);
634 
635 	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
636 
637 	if (sc->sc_callback->ucom_stop_read) {
638 		(sc->sc_callback->ucom_stop_read) (sc);
639 	}
640 }
641 
642 static int
643 ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
644 {
645 	struct ucom_softc *sc = tty_softc(tp);
646 	int error;
647 
648 	mtx_assert(sc->sc_mtx, MA_OWNED);
649 
650 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
651 		return (EIO);
652 	}
653 	DPRINTF("cmd = 0x%08lx\n", cmd);
654 
655 	switch (cmd) {
656 	case TIOCSBRK:
657 		ucom_break(sc, 1);
658 		error = 0;
659 		break;
660 	case TIOCCBRK:
661 		ucom_break(sc, 0);
662 		error = 0;
663 		break;
664 	default:
665 		if (sc->sc_callback->ucom_ioctl) {
666 			error = (sc->sc_callback->ucom_ioctl)
667 			    (sc, cmd, data, 0, td);
668 		} else {
669 			error = ENOIOCTL;
670 		}
671 		break;
672 	}
673 	return (error);
674 }
675 
676 static int
677 ucom_modem(struct tty *tp, int sigon, int sigoff)
678 {
679 	struct ucom_softc *sc = tty_softc(tp);
680 	uint8_t onoff;
681 
682 	mtx_assert(sc->sc_mtx, MA_OWNED);
683 
684 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
685 		return (0);
686 	}
687 	if ((sigon == 0) && (sigoff == 0)) {
688 
689 		if (sc->sc_mcr & SER_DTR) {
690 			sigon |= SER_DTR;
691 		}
692 		if (sc->sc_mcr & SER_RTS) {
693 			sigon |= SER_RTS;
694 		}
695 		if (sc->sc_msr & SER_CTS) {
696 			sigon |= SER_CTS;
697 		}
698 		if (sc->sc_msr & SER_DCD) {
699 			sigon |= SER_DCD;
700 		}
701 		if (sc->sc_msr & SER_DSR) {
702 			sigon |= SER_DSR;
703 		}
704 		if (sc->sc_msr & SER_RI) {
705 			sigon |= SER_RI;
706 		}
707 		return (sigon);
708 	}
709 	if (sigon & SER_DTR) {
710 		sc->sc_mcr |= SER_DTR;
711 	}
712 	if (sigoff & SER_DTR) {
713 		sc->sc_mcr &= ~SER_DTR;
714 	}
715 	if (sigon & SER_RTS) {
716 		sc->sc_mcr |= SER_RTS;
717 	}
718 	if (sigoff & SER_RTS) {
719 		sc->sc_mcr &= ~SER_RTS;
720 	}
721 	onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
722 	ucom_dtr(sc, onoff);
723 
724 	onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
725 	ucom_rts(sc, onoff);
726 
727 	return (0);
728 }
729 
730 static void
731 ucom_cfg_line_state(struct usb_proc_msg *_task)
732 {
733 	struct ucom_cfg_task *task =
734 	    (struct ucom_cfg_task *)_task;
735 	struct ucom_softc *sc = task->sc;
736 	uint8_t notch_bits;
737 	uint8_t any_bits;
738 	uint8_t prev_value;
739 	uint8_t last_value;
740 	uint8_t mask;
741 
742 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
743 		return;
744 	}
745 
746 	mask = 0;
747 	/* compute callback mask */
748 	if (sc->sc_callback->ucom_cfg_set_dtr)
749 		mask |= UCOM_LS_DTR;
750 	if (sc->sc_callback->ucom_cfg_set_rts)
751 		mask |= UCOM_LS_RTS;
752 	if (sc->sc_callback->ucom_cfg_set_break)
753 		mask |= UCOM_LS_BREAK;
754 
755 	/* compute the bits we are to program */
756 	notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
757 	any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
758 	prev_value = sc->sc_pls_curr ^ notch_bits;
759 	last_value = sc->sc_pls_curr;
760 
761 	/* reset programmed line state */
762 	sc->sc_pls_curr = 0;
763 	sc->sc_pls_set = 0;
764 	sc->sc_pls_clr = 0;
765 
766 	/* ensure that we don't loose any levels */
767 	if (notch_bits & UCOM_LS_DTR)
768 		sc->sc_callback->ucom_cfg_set_dtr(sc,
769 		    (prev_value & UCOM_LS_DTR) ? 1 : 0);
770 	if (notch_bits & UCOM_LS_RTS)
771 		sc->sc_callback->ucom_cfg_set_rts(sc,
772 		    (prev_value & UCOM_LS_RTS) ? 1 : 0);
773 	if (notch_bits & UCOM_LS_BREAK)
774 		sc->sc_callback->ucom_cfg_set_break(sc,
775 		    (prev_value & UCOM_LS_BREAK) ? 1 : 0);
776 
777 	/* set last value */
778 	if (any_bits & UCOM_LS_DTR)
779 		sc->sc_callback->ucom_cfg_set_dtr(sc,
780 		    (last_value & UCOM_LS_DTR) ? 1 : 0);
781 	if (any_bits & UCOM_LS_RTS)
782 		sc->sc_callback->ucom_cfg_set_rts(sc,
783 		    (last_value & UCOM_LS_RTS) ? 1 : 0);
784 	if (any_bits & UCOM_LS_BREAK)
785 		sc->sc_callback->ucom_cfg_set_break(sc,
786 		    (last_value & UCOM_LS_BREAK) ? 1 : 0);
787 }
788 
789 static void
790 ucom_line_state(struct ucom_softc *sc,
791     uint8_t set_bits, uint8_t clear_bits)
792 {
793 	mtx_assert(sc->sc_mtx, MA_OWNED);
794 
795 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
796 		return;
797 	}
798 
799 	DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
800 
801 	/* update current programmed line state */
802 	sc->sc_pls_curr |= set_bits;
803 	sc->sc_pls_curr &= ~clear_bits;
804 	sc->sc_pls_set |= set_bits;
805 	sc->sc_pls_clr |= clear_bits;
806 
807 	/* defer driver programming */
808 	ucom_queue_command(sc, ucom_cfg_line_state, NULL,
809 	    &sc->sc_line_state_task[0].hdr,
810 	    &sc->sc_line_state_task[1].hdr);
811 }
812 
813 static void
814 ucom_break(struct ucom_softc *sc, uint8_t onoff)
815 {
816 	DPRINTF("onoff = %d\n", onoff);
817 
818 	if (onoff)
819 		ucom_line_state(sc, UCOM_LS_BREAK, 0);
820 	else
821 		ucom_line_state(sc, 0, UCOM_LS_BREAK);
822 }
823 
824 static void
825 ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
826 {
827 	DPRINTF("onoff = %d\n", onoff);
828 
829 	if (onoff)
830 		ucom_line_state(sc, UCOM_LS_DTR, 0);
831 	else
832 		ucom_line_state(sc, 0, UCOM_LS_DTR);
833 }
834 
835 static void
836 ucom_rts(struct ucom_softc *sc, uint8_t onoff)
837 {
838 	DPRINTF("onoff = %d\n", onoff);
839 
840 	if (onoff)
841 		ucom_line_state(sc, UCOM_LS_RTS, 0);
842 	else
843 		ucom_line_state(sc, 0, UCOM_LS_RTS);
844 }
845 
846 static void
847 ucom_cfg_status_change(struct usb_proc_msg *_task)
848 {
849 	struct ucom_cfg_task *task =
850 	    (struct ucom_cfg_task *)_task;
851 	struct ucom_softc *sc = task->sc;
852 	struct tty *tp;
853 	uint8_t new_msr;
854 	uint8_t new_lsr;
855 	uint8_t onoff;
856 
857 	tp = sc->sc_tty;
858 
859 	mtx_assert(sc->sc_mtx, MA_OWNED);
860 
861 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
862 		return;
863 	}
864 	if (sc->sc_callback->ucom_cfg_get_status == NULL) {
865 		return;
866 	}
867 	/* get status */
868 
869 	new_msr = 0;
870 	new_lsr = 0;
871 
872 	(sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
873 
874 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
875 		/* TTY device closed */
876 		return;
877 	}
878 	onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
879 
880 	sc->sc_msr = new_msr;
881 	sc->sc_lsr = new_lsr;
882 
883 	if (onoff) {
884 
885 		onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
886 
887 		DPRINTF("DCD changed to %d\n", onoff);
888 
889 		ttydisc_modem(tp, onoff);
890 	}
891 }
892 
893 void
894 ucom_status_change(struct ucom_softc *sc)
895 {
896 	mtx_assert(sc->sc_mtx, MA_OWNED);
897 
898 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
899 		return;
900 	}
901 	DPRINTF("\n");
902 
903 	ucom_queue_command(sc, ucom_cfg_status_change, NULL,
904 	    &sc->sc_status_task[0].hdr,
905 	    &sc->sc_status_task[1].hdr);
906 }
907 
908 static void
909 ucom_cfg_param(struct usb_proc_msg *_task)
910 {
911 	struct ucom_param_task *task =
912 	    (struct ucom_param_task *)_task;
913 	struct ucom_softc *sc = task->sc;
914 
915 	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
916 		return;
917 	}
918 	if (sc->sc_callback->ucom_cfg_param == NULL) {
919 		return;
920 	}
921 
922 	(sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
923 
924 	/* wait a little */
925 	usb_pause_mtx(sc->sc_mtx, hz / 10);
926 }
927 
928 static int
929 ucom_param(struct tty *tp, struct termios *t)
930 {
931 	struct ucom_softc *sc = tty_softc(tp);
932 	uint8_t opened;
933 	int error;
934 
935 	mtx_assert(sc->sc_mtx, MA_OWNED);
936 
937 	opened = 0;
938 	error = 0;
939 
940 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
941 
942 		/* XXX the TTY layer should call "open()" first! */
943 
944 		error = ucom_open(tp);
945 		if (error) {
946 			goto done;
947 		}
948 		opened = 1;
949 	}
950 	DPRINTF("sc = %p\n", sc);
951 
952 	/* Check requested parameters. */
953 	if (t->c_ospeed < 0) {
954 		DPRINTF("negative ospeed\n");
955 		error = EINVAL;
956 		goto done;
957 	}
958 	if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
959 		DPRINTF("mismatch ispeed and ospeed\n");
960 		error = EINVAL;
961 		goto done;
962 	}
963 	t->c_ispeed = t->c_ospeed;
964 
965 	if (sc->sc_callback->ucom_pre_param) {
966 		/* Let the lower layer verify the parameters */
967 		error = (sc->sc_callback->ucom_pre_param) (sc, t);
968 		if (error) {
969 			DPRINTF("callback error = %d\n", error);
970 			goto done;
971 		}
972 	}
973 
974 	/* Disable transfers */
975 	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
976 
977 	/* Queue baud rate programming command first */
978 	ucom_queue_command(sc, ucom_cfg_param, t,
979 	    &sc->sc_param_task[0].hdr,
980 	    &sc->sc_param_task[1].hdr);
981 
982 	/* Queue transfer enable command last */
983 	ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
984 	    &sc->sc_start_task[0].hdr,
985 	    &sc->sc_start_task[1].hdr);
986 
987 	if (t->c_cflag & CRTS_IFLOW) {
988 		sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
989 	} else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
990 		sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
991 		ucom_modem(tp, SER_RTS, 0);
992 	}
993 done:
994 	if (error) {
995 		if (opened) {
996 			ucom_close(tp);
997 		}
998 	}
999 	return (error);
1000 }
1001 
1002 static void
1003 ucom_outwakeup(struct tty *tp)
1004 {
1005 	struct ucom_softc *sc = tty_softc(tp);
1006 
1007 	mtx_assert(sc->sc_mtx, MA_OWNED);
1008 
1009 	DPRINTF("sc = %p\n", sc);
1010 
1011 	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1012 		/* The higher layer is not ready */
1013 		return;
1014 	}
1015 	ucom_start_transfers(sc);
1016 }
1017 
1018 /*------------------------------------------------------------------------*
1019  *	ucom_get_data
1020  *
1021  * Return values:
1022  * 0: No data is available.
1023  * Else: Data is available.
1024  *------------------------------------------------------------------------*/
1025 uint8_t
1026 ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1027     uint32_t offset, uint32_t len, uint32_t *actlen)
1028 {
1029 	struct usb_page_search res;
1030 	struct tty *tp = sc->sc_tty;
1031 	uint32_t cnt;
1032 	uint32_t offset_orig;
1033 
1034 	mtx_assert(sc->sc_mtx, MA_OWNED);
1035 
1036 	if (tty_gone(tp) ||
1037 	    !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
1038 		actlen[0] = 0;
1039 		return (0);		/* multiport device polling */
1040 	}
1041 	offset_orig = offset;
1042 
1043 	while (len != 0) {
1044 
1045 		usbd_get_page(pc, offset, &res);
1046 
1047 		if (res.length > len) {
1048 			res.length = len;
1049 		}
1050 		/* copy data directly into USB buffer */
1051 		cnt = ttydisc_getc(tp, res.buffer, res.length);
1052 
1053 		offset += cnt;
1054 		len -= cnt;
1055 
1056 		if (cnt < res.length) {
1057 			/* end of buffer */
1058 			break;
1059 		}
1060 	}
1061 
1062 	actlen[0] = offset - offset_orig;
1063 
1064 	DPRINTF("cnt=%d\n", actlen[0]);
1065 
1066 	if (actlen[0] == 0) {
1067 		return (0);
1068 	}
1069 	return (1);
1070 }
1071 
1072 void
1073 ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1074     uint32_t offset, uint32_t len)
1075 {
1076 	struct usb_page_search res;
1077 	struct tty *tp = sc->sc_tty;
1078 	char *buf;
1079 	uint32_t cnt;
1080 
1081 	mtx_assert(sc->sc_mtx, MA_OWNED);
1082 
1083 	if (tty_gone(tp))
1084 		return;			/* multiport device polling */
1085 
1086 	if (len == 0)
1087 		return;			/* no data */
1088 
1089 	/* set a flag to prevent recursation ? */
1090 
1091 	while (len > 0) {
1092 
1093 		usbd_get_page(pc, offset, &res);
1094 
1095 		if (res.length > len) {
1096 			res.length = len;
1097 		}
1098 		len -= res.length;
1099 		offset += res.length;
1100 
1101 		/* pass characters to tty layer */
1102 
1103 		buf = res.buffer;
1104 		cnt = res.length;
1105 
1106 		/* first check if we can pass the buffer directly */
1107 
1108 		if (ttydisc_can_bypass(tp)) {
1109 			if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1110 				DPRINTF("tp=%p, data lost\n", tp);
1111 			}
1112 			continue;
1113 		}
1114 		/* need to loop */
1115 
1116 		for (cnt = 0; cnt != res.length; cnt++) {
1117 			if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
1118 				/* XXX what should we do? */
1119 
1120 				DPRINTF("tp=%p, lost %d "
1121 				    "chars\n", tp, res.length - cnt);
1122 				break;
1123 			}
1124 		}
1125 	}
1126 	ttydisc_rint_done(tp);
1127 }
1128 
1129 static void
1130 ucom_free(void *xsc)
1131 {
1132 	struct ucom_softc *sc = xsc;
1133 
1134 	mtx_lock(sc->sc_mtx);
1135 	sc->sc_ttyfreed = 1;
1136 	cv_signal(&sc->sc_cv);
1137 	mtx_unlock(sc->sc_mtx);
1138 }
1139