usb_serial.c revision 190742
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 190742 2009-04-05 18:22:03Z 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_usb2, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
91SYSCTL_INT(_hw_usb2_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 usb2_com_softc *, uint32_t);
105static void	usb2_com_detach_tty(struct usb2_com_softc *);
106static void	usb2_com_queue_command(struct usb2_com_softc *,
107		    usb2_proc_callback_t *, struct termios *pt,
108		    struct usb2_proc_msg *t0, struct usb2_proc_msg *t1);
109static void	usb2_com_shutdown(struct usb2_com_softc *);
110static void	usb2_com_break(struct usb2_com_softc *, uint8_t);
111static void	usb2_com_dtr(struct usb2_com_softc *, uint8_t);
112static void	usb2_com_rts(struct usb2_com_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 usb2_com_super_softc *ssc, struct usb2_com_softc *sc,
219    uint32_t sub_units, void *parent,
220    const struct usb2_com_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 usb2_com_super_softc *ssc, struct usb2_com_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 usb2_com_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 usb2_com_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 usb2_com_softc *sc,
378    usb2_proc_callback_t *fn, struct termios *pt,
379    struct usb2_proc_msg *t0, struct usb2_proc_msg *t1)
380{
381	struct usb2_com_super_softc *ssc = sc->sc_super;
382	struct usb2_com_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 usb2_com_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 usb2_com_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 usb2_com_softc *sc)
447{
448	struct usb2_com_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 usb2_proc_msg *_task)
455{
456	struct usb2_com_cfg_task *task =
457	    (struct usb2_com_cfg_task *)_task;
458	struct usb2_com_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 usb2_com_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 usb2_proc_msg *_task)
499{
500	struct usb2_com_cfg_task *task =
501	    (struct usb2_com_cfg_task *)_task;
502	struct usb2_com_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 usb2_com_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 usb2_proc_msg *_task)
584{
585	struct usb2_com_cfg_task *task =
586	    (struct usb2_com_cfg_task *)_task;
587	struct usb2_com_softc *sc = task->sc;
588
589	DPRINTF("\n");
590
591	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
592
593		sc->sc_flag &= ~(UCOM_FLAG_LL_READY |
594		    UCOM_FLAG_GP_DATA);
595
596		if (sc->sc_callback->usb2_com_cfg_close) {
597			(sc->sc_callback->usb2_com_cfg_close) (sc);
598		}
599	} else {
600		/* already closed */
601	}
602}
603
604static void
605usb2_com_close(struct tty *tp)
606{
607	struct usb2_com_softc *sc = tty_softc(tp);
608
609	mtx_assert(sc->sc_mtx, MA_OWNED);
610
611	DPRINTF("tp=%p\n", tp);
612
613	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
614		DPRINTF("tp=%p already closed\n", tp);
615		return;
616	}
617	usb2_com_shutdown(sc);
618
619	usb2_com_queue_command(sc, usb2_com_cfg_close, NULL,
620	    &sc->sc_close_task[0].hdr,
621	    &sc->sc_close_task[1].hdr);
622
623	sc->sc_flag &= ~(UCOM_FLAG_HL_READY |
624	    UCOM_FLAG_WR_START |
625	    UCOM_FLAG_RTS_IFLOW);
626
627	if (sc->sc_callback->usb2_com_stop_read) {
628		(sc->sc_callback->usb2_com_stop_read) (sc);
629	}
630	if (sc->sc_callback->usb2_com_stop_write) {
631		(sc->sc_callback->usb2_com_stop_write) (sc);
632	}
633}
634
635static int
636usb2_com_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
637{
638	struct usb2_com_softc *sc = tty_softc(tp);
639	int error;
640
641	mtx_assert(sc->sc_mtx, MA_OWNED);
642
643	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
644		return (EIO);
645	}
646	DPRINTF("cmd = 0x%08lx\n", cmd);
647
648	switch (cmd) {
649	case TIOCSBRK:
650		usb2_com_break(sc, 1);
651		error = 0;
652		break;
653	case TIOCCBRK:
654		usb2_com_break(sc, 0);
655		error = 0;
656		break;
657	default:
658		if (sc->sc_callback->usb2_com_ioctl) {
659			error = (sc->sc_callback->usb2_com_ioctl)
660			    (sc, cmd, data, 0, td);
661		} else {
662			error = ENOIOCTL;
663		}
664		break;
665	}
666	return (error);
667}
668
669static int
670usb2_com_modem(struct tty *tp, int sigon, int sigoff)
671{
672	struct usb2_com_softc *sc = tty_softc(tp);
673	uint8_t onoff;
674
675	mtx_assert(sc->sc_mtx, MA_OWNED);
676
677	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
678		return (0);
679	}
680	if ((sigon == 0) && (sigoff == 0)) {
681
682		if (sc->sc_mcr & SER_DTR) {
683			sigon |= SER_DTR;
684		}
685		if (sc->sc_mcr & SER_RTS) {
686			sigon |= SER_RTS;
687		}
688		if (sc->sc_msr & SER_CTS) {
689			sigon |= SER_CTS;
690		}
691		if (sc->sc_msr & SER_DCD) {
692			sigon |= SER_DCD;
693		}
694		if (sc->sc_msr & SER_DSR) {
695			sigon |= SER_DSR;
696		}
697		if (sc->sc_msr & SER_RI) {
698			sigon |= SER_RI;
699		}
700		return (sigon);
701	}
702	if (sigon & SER_DTR) {
703		sc->sc_mcr |= SER_DTR;
704	}
705	if (sigoff & SER_DTR) {
706		sc->sc_mcr &= ~SER_DTR;
707	}
708	if (sigon & SER_RTS) {
709		sc->sc_mcr |= SER_RTS;
710	}
711	if (sigoff & SER_RTS) {
712		sc->sc_mcr &= ~SER_RTS;
713	}
714	onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
715	usb2_com_dtr(sc, onoff);
716
717	onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
718	usb2_com_rts(sc, onoff);
719
720	return (0);
721}
722
723static void
724usb2_com_cfg_line_state(struct usb2_proc_msg *_task)
725{
726	struct usb2_com_cfg_task *task =
727	    (struct usb2_com_cfg_task *)_task;
728	struct usb2_com_softc *sc = task->sc;
729	uint8_t notch_bits;
730	uint8_t any_bits;
731	uint8_t prev_value;
732	uint8_t last_value;
733	uint8_t mask;
734
735	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
736		return;
737	}
738
739	mask = 0;
740	/* compute callback mask */
741	if (sc->sc_callback->usb2_com_cfg_set_dtr)
742		mask |= UCOM_LS_DTR;
743	if (sc->sc_callback->usb2_com_cfg_set_rts)
744		mask |= UCOM_LS_RTS;
745	if (sc->sc_callback->usb2_com_cfg_set_break)
746		mask |= UCOM_LS_BREAK;
747
748	/* compute the bits we are to program */
749	notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
750	any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
751	prev_value = sc->sc_pls_curr ^ notch_bits;
752	last_value = sc->sc_pls_curr;
753
754	/* reset programmed line state */
755	sc->sc_pls_curr = 0;
756	sc->sc_pls_set = 0;
757	sc->sc_pls_clr = 0;
758
759	/* ensure that we don't loose any levels */
760	if (notch_bits & UCOM_LS_DTR)
761		sc->sc_callback->usb2_com_cfg_set_dtr(sc,
762		    (prev_value & UCOM_LS_DTR) ? 1 : 0);
763	if (notch_bits & UCOM_LS_RTS)
764		sc->sc_callback->usb2_com_cfg_set_rts(sc,
765		    (prev_value & UCOM_LS_RTS) ? 1 : 0);
766	if (notch_bits & UCOM_LS_BREAK)
767		sc->sc_callback->usb2_com_cfg_set_break(sc,
768		    (prev_value & UCOM_LS_BREAK) ? 1 : 0);
769
770	/* set last value */
771	if (any_bits & UCOM_LS_DTR)
772		sc->sc_callback->usb2_com_cfg_set_dtr(sc,
773		    (last_value & UCOM_LS_DTR) ? 1 : 0);
774	if (any_bits & UCOM_LS_RTS)
775		sc->sc_callback->usb2_com_cfg_set_rts(sc,
776		    (last_value & UCOM_LS_RTS) ? 1 : 0);
777	if (any_bits & UCOM_LS_BREAK)
778		sc->sc_callback->usb2_com_cfg_set_break(sc,
779		    (last_value & UCOM_LS_BREAK) ? 1 : 0);
780}
781
782static void
783usb2_com_line_state(struct usb2_com_softc *sc,
784    uint8_t set_bits, uint8_t clear_bits)
785{
786	mtx_assert(sc->sc_mtx, MA_OWNED);
787
788	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
789		return;
790	}
791
792	DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
793
794	/* update current programmed line state */
795	sc->sc_pls_curr |= set_bits;
796	sc->sc_pls_curr &= ~clear_bits;
797	sc->sc_pls_set |= set_bits;
798	sc->sc_pls_clr |= clear_bits;
799
800	/* defer driver programming */
801	usb2_com_queue_command(sc, usb2_com_cfg_line_state, NULL,
802	    &sc->sc_line_state_task[0].hdr,
803	    &sc->sc_line_state_task[1].hdr);
804}
805
806static void
807usb2_com_break(struct usb2_com_softc *sc, uint8_t onoff)
808{
809	DPRINTF("onoff = %d\n", onoff);
810
811	if (onoff)
812		usb2_com_line_state(sc, UCOM_LS_BREAK, 0);
813	else
814		usb2_com_line_state(sc, 0, UCOM_LS_BREAK);
815}
816
817static void
818usb2_com_dtr(struct usb2_com_softc *sc, uint8_t onoff)
819{
820	DPRINTF("onoff = %d\n", onoff);
821
822	if (onoff)
823		usb2_com_line_state(sc, UCOM_LS_DTR, 0);
824	else
825		usb2_com_line_state(sc, 0, UCOM_LS_DTR);
826}
827
828static void
829usb2_com_rts(struct usb2_com_softc *sc, uint8_t onoff)
830{
831	DPRINTF("onoff = %d\n", onoff);
832
833	if (onoff)
834		usb2_com_line_state(sc, UCOM_LS_RTS, 0);
835	else
836		usb2_com_line_state(sc, 0, UCOM_LS_RTS);
837}
838
839static void
840usb2_com_cfg_status_change(struct usb2_proc_msg *_task)
841{
842	struct usb2_com_cfg_task *task =
843	    (struct usb2_com_cfg_task *)_task;
844	struct usb2_com_softc *sc = task->sc;
845	struct tty *tp;
846	uint8_t new_msr;
847	uint8_t new_lsr;
848	uint8_t onoff;
849
850	tp = sc->sc_tty;
851
852	mtx_assert(sc->sc_mtx, MA_OWNED);
853
854	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
855		return;
856	}
857	if (sc->sc_callback->usb2_com_cfg_get_status == NULL) {
858		return;
859	}
860	/* get status */
861
862	new_msr = 0;
863	new_lsr = 0;
864
865	(sc->sc_callback->usb2_com_cfg_get_status) (sc, &new_lsr, &new_msr);
866
867	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
868		/* TTY device closed */
869		return;
870	}
871	onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
872
873	sc->sc_msr = new_msr;
874	sc->sc_lsr = new_lsr;
875
876	if (onoff) {
877
878		onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
879
880		DPRINTF("DCD changed to %d\n", onoff);
881
882		ttydisc_modem(tp, onoff);
883	}
884}
885
886void
887usb2_com_status_change(struct usb2_com_softc *sc)
888{
889	mtx_assert(sc->sc_mtx, MA_OWNED);
890
891	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
892		return;
893	}
894	DPRINTF("\n");
895
896	usb2_com_queue_command(sc, usb2_com_cfg_status_change, NULL,
897	    &sc->sc_status_task[0].hdr,
898	    &sc->sc_status_task[1].hdr);
899}
900
901static void
902usb2_com_cfg_param(struct usb2_proc_msg *_task)
903{
904	struct usb2_com_param_task *task =
905	    (struct usb2_com_param_task *)_task;
906	struct usb2_com_softc *sc = task->sc;
907
908	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
909		return;
910	}
911	if (sc->sc_callback->usb2_com_cfg_param == NULL) {
912		return;
913	}
914
915	(sc->sc_callback->usb2_com_cfg_param) (sc, &task->termios_copy);
916
917	/* wait a little */
918	usb2_pause_mtx(sc->sc_mtx, hz / 10);
919}
920
921static int
922usb2_com_param(struct tty *tp, struct termios *t)
923{
924	struct usb2_com_softc *sc = tty_softc(tp);
925	uint8_t opened;
926	int error;
927
928	mtx_assert(sc->sc_mtx, MA_OWNED);
929
930	opened = 0;
931	error = 0;
932
933	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
934
935		/* XXX the TTY layer should call "open()" first! */
936
937		error = usb2_com_open(tp);
938		if (error) {
939			goto done;
940		}
941		opened = 1;
942	}
943	DPRINTF("sc = %p\n", sc);
944
945	/* Check requested parameters. */
946	if (t->c_ospeed < 0) {
947		DPRINTF("negative ospeed\n");
948		error = EINVAL;
949		goto done;
950	}
951	if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
952		DPRINTF("mismatch ispeed and ospeed\n");
953		error = EINVAL;
954		goto done;
955	}
956	t->c_ispeed = t->c_ospeed;
957
958	if (sc->sc_callback->usb2_com_pre_param) {
959		/* Let the lower layer verify the parameters */
960		error = (sc->sc_callback->usb2_com_pre_param) (sc, t);
961		if (error) {
962			DPRINTF("callback error = %d\n", error);
963			goto done;
964		}
965	}
966
967	/* Disable transfers */
968	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
969
970	/* Queue baud rate programming command first */
971	usb2_com_queue_command(sc, usb2_com_cfg_param, t,
972	    &sc->sc_param_task[0].hdr,
973	    &sc->sc_param_task[1].hdr);
974
975	/* Queue transfer enable command last */
976	usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL,
977	    &sc->sc_start_task[0].hdr,
978	    &sc->sc_start_task[1].hdr);
979
980	if (t->c_cflag & CRTS_IFLOW) {
981		sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
982	} else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
983		sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
984		usb2_com_modem(tp, SER_RTS, 0);
985	}
986done:
987	if (error) {
988		if (opened) {
989			usb2_com_close(tp);
990		}
991	}
992	return (error);
993}
994
995static void
996usb2_com_outwakeup(struct tty *tp)
997{
998	struct usb2_com_softc *sc = tty_softc(tp);
999
1000	mtx_assert(sc->sc_mtx, MA_OWNED);
1001
1002	DPRINTF("sc = %p\n", sc);
1003
1004	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1005		/* The higher layer is not ready */
1006		return;
1007	}
1008	sc->sc_flag |= UCOM_FLAG_WR_START;
1009
1010	usb2_com_start_transfers(sc);
1011}
1012
1013/*------------------------------------------------------------------------*
1014 *	usb2_com_get_data
1015 *
1016 * Return values:
1017 * 0: No data is available.
1018 * Else: Data is available.
1019 *------------------------------------------------------------------------*/
1020uint8_t
1021usb2_com_get_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
1022    uint32_t offset, uint32_t len, uint32_t *actlen)
1023{
1024	struct usb2_page_search res;
1025	struct tty *tp = sc->sc_tty;
1026	uint32_t cnt;
1027	uint32_t offset_orig;
1028
1029	mtx_assert(sc->sc_mtx, MA_OWNED);
1030
1031	if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) ||
1032	    (!(sc->sc_flag & UCOM_FLAG_GP_DATA)) ||
1033	    (!(sc->sc_flag & UCOM_FLAG_WR_START))) {
1034		actlen[0] = 0;
1035		return (0);		/* multiport device polling */
1036	}
1037	offset_orig = offset;
1038
1039	while (len != 0) {
1040
1041		usb2_get_page(pc, offset, &res);
1042
1043		if (res.length > len) {
1044			res.length = len;
1045		}
1046		/* copy data directly into USB buffer */
1047		cnt = ttydisc_getc(tp, res.buffer, res.length);
1048
1049		offset += cnt;
1050		len -= cnt;
1051
1052		if (cnt < res.length) {
1053			/* end of buffer */
1054			break;
1055		}
1056	}
1057
1058	actlen[0] = offset - offset_orig;
1059
1060	DPRINTF("cnt=%d\n", actlen[0]);
1061
1062	if (actlen[0] == 0) {
1063		return (0);
1064	}
1065	return (1);
1066}
1067
1068void
1069usb2_com_put_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
1070    uint32_t offset, uint32_t len)
1071{
1072	struct usb2_page_search res;
1073	struct tty *tp = sc->sc_tty;
1074	char *buf;
1075	uint32_t cnt;
1076
1077	mtx_assert(sc->sc_mtx, MA_OWNED);
1078
1079	if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) ||
1080	    (!(sc->sc_flag & UCOM_FLAG_GP_DATA))) {
1081		return;			/* multiport device polling */
1082	}
1083	if (len == 0)
1084		return;			/* no data */
1085
1086	/* set a flag to prevent recursation ? */
1087
1088	while (len > 0) {
1089
1090		usb2_get_page(pc, offset, &res);
1091
1092		if (res.length > len) {
1093			res.length = len;
1094		}
1095		len -= res.length;
1096		offset += res.length;
1097
1098		/* pass characters to tty layer */
1099
1100		buf = res.buffer;
1101		cnt = res.length;
1102
1103		/* first check if we can pass the buffer directly */
1104
1105		if (ttydisc_can_bypass(tp)) {
1106			if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1107				DPRINTF("tp=%p, data lost\n", tp);
1108			}
1109			continue;
1110		}
1111		/* need to loop */
1112
1113		for (cnt = 0; cnt != res.length; cnt++) {
1114			if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
1115				/* XXX what should we do? */
1116
1117				DPRINTF("tp=%p, lost %d "
1118				    "chars\n", tp, res.length - cnt);
1119				break;
1120			}
1121		}
1122	}
1123	ttydisc_rint_done(tp);
1124}
1125
1126static void
1127usb2_com_free(void *xsc)
1128{
1129	struct usb2_com_softc *sc = xsc;
1130
1131	mtx_lock(sc->sc_mtx);
1132	sc->sc_ttyfreed = 1;
1133	usb2_cv_signal(&sc->sc_cv);
1134	mtx_unlock(sc->sc_mtx);
1135}
1136