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