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