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