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