usb_serial.c revision 190581
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 190581 2009-03-30 22:18:38Z mav $");
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
417static void
418usb2_com_shutdown(struct usb2_com_softc *sc)
419{
420	struct tty *tp = sc->sc_tty;
421
422	mtx_assert(sc->sc_mtx, MA_OWNED);
423
424	DPRINTF("\n");
425
426	/*
427	 * Hang up if necessary:
428	 */
429	if (tp->t_termios.c_cflag & HUPCL) {
430		usb2_com_modem(tp, 0, SER_DTR);
431	}
432}
433
434/*
435 * Return values:
436 *    0: normal
437 * else: taskqueue is draining or gone
438 */
439uint8_t
440usb2_com_cfg_is_gone(struct usb2_com_softc *sc)
441{
442	struct usb2_com_super_softc *ssc = sc->sc_super;
443
444	return (usb2_proc_is_gone(&ssc->sc_tq));
445}
446
447static void
448usb2_com_cfg_start_transfers(struct usb2_proc_msg *_task)
449{
450	struct usb2_com_cfg_task *task =
451	    (struct usb2_com_cfg_task *)_task;
452	struct usb2_com_softc *sc = task->sc;
453
454	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
455		return;
456	}
457	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
458		/* TTY device closed */
459		return;
460	}
461	sc->sc_flag |= UCOM_FLAG_GP_DATA;
462
463	if (sc->sc_callback->usb2_com_start_read) {
464		(sc->sc_callback->usb2_com_start_read) (sc);
465	}
466	if (sc->sc_callback->usb2_com_start_write) {
467		(sc->sc_callback->usb2_com_start_write) (sc);
468	}
469}
470
471static void
472usb2_com_start_transfers(struct usb2_com_softc *sc)
473{
474	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
475		return;
476	}
477	/*
478	 * Make sure that data transfers are started in both
479	 * directions:
480	 */
481	if (sc->sc_callback->usb2_com_start_read) {
482		(sc->sc_callback->usb2_com_start_read) (sc);
483	}
484	if (sc->sc_callback->usb2_com_start_write) {
485		(sc->sc_callback->usb2_com_start_write) (sc);
486	}
487}
488
489static void
490usb2_com_cfg_open(struct usb2_proc_msg *_task)
491{
492	struct usb2_com_cfg_task *task =
493	    (struct usb2_com_cfg_task *)_task;
494	struct usb2_com_softc *sc = task->sc;
495
496	DPRINTF("\n");
497
498	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
499
500		/* already opened */
501
502	} else {
503
504		sc->sc_flag |= UCOM_FLAG_LL_READY;
505
506		if (sc->sc_callback->usb2_com_cfg_open) {
507			(sc->sc_callback->usb2_com_cfg_open) (sc);
508
509			/* wait a little */
510			usb2_pause_mtx(sc->sc_mtx, hz / 10);
511		}
512	}
513}
514
515static int
516usb2_com_open(struct tty *tp)
517{
518	struct usb2_com_softc *sc = tty_softc(tp);
519	int error;
520
521	mtx_assert(sc->sc_mtx, MA_OWNED);
522
523	if (sc->sc_flag & UCOM_FLAG_GONE) {
524		return (ENXIO);
525	}
526	if (sc->sc_flag & UCOM_FLAG_HL_READY) {
527		/* already opened */
528		return (0);
529	}
530	DPRINTF("tp = %p\n", tp);
531
532	if (sc->sc_callback->usb2_com_pre_open) {
533		/*
534		 * give the lower layer a chance to disallow TTY open, for
535		 * example if the device is not present:
536		 */
537		error = (sc->sc_callback->usb2_com_pre_open) (sc);
538		if (error) {
539			return (error);
540		}
541	}
542	sc->sc_flag |= UCOM_FLAG_HL_READY;
543
544	/* Disable transfers */
545	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
546
547	sc->sc_lsr = 0;
548	sc->sc_msr = 0;
549	sc->sc_mcr = 0;
550
551	/* reset programmed line state */
552	sc->sc_pls_curr = 0;
553	sc->sc_pls_set = 0;
554	sc->sc_pls_clr = 0;
555
556	usb2_com_queue_command(sc, usb2_com_cfg_open, NULL,
557	    &sc->sc_open_task[0].hdr,
558	    &sc->sc_open_task[1].hdr);
559
560	/* Queue transfer enable command last */
561	usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL,
562	    &sc->sc_start_task[0].hdr,
563	    &sc->sc_start_task[1].hdr);
564
565	usb2_com_modem(tp, SER_DTR | SER_RTS, 0);
566
567	usb2_com_break(sc, 0);
568
569	usb2_com_status_change(sc);
570
571	return (0);
572}
573
574static void
575usb2_com_cfg_close(struct usb2_proc_msg *_task)
576{
577	struct usb2_com_cfg_task *task =
578	    (struct usb2_com_cfg_task *)_task;
579	struct usb2_com_softc *sc = task->sc;
580
581	DPRINTF("\n");
582
583	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
584
585		sc->sc_flag &= ~(UCOM_FLAG_LL_READY |
586		    UCOM_FLAG_GP_DATA);
587
588		if (sc->sc_callback->usb2_com_cfg_close) {
589			(sc->sc_callback->usb2_com_cfg_close) (sc);
590		}
591	} else {
592		/* already closed */
593	}
594}
595
596static void
597usb2_com_close(struct tty *tp)
598{
599	struct usb2_com_softc *sc = tty_softc(tp);
600
601	mtx_assert(sc->sc_mtx, MA_OWNED);
602
603	DPRINTF("tp=%p\n", tp);
604
605	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
606		DPRINTF("tp=%p already closed\n", tp);
607		return;
608	}
609	usb2_com_shutdown(sc);
610
611	usb2_com_queue_command(sc, usb2_com_cfg_close, NULL,
612	    &sc->sc_close_task[0].hdr,
613	    &sc->sc_close_task[1].hdr);
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}
626
627static int
628usb2_com_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
629{
630	struct usb2_com_softc *sc = tty_softc(tp);
631	int error;
632
633	mtx_assert(sc->sc_mtx, MA_OWNED);
634
635	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
636		return (EIO);
637	}
638	DPRINTF("cmd = 0x%08lx\n", cmd);
639
640	switch (cmd) {
641	case TIOCSBRK:
642		usb2_com_break(sc, 1);
643		error = 0;
644		break;
645	case TIOCCBRK:
646		usb2_com_break(sc, 0);
647		error = 0;
648		break;
649	default:
650		if (sc->sc_callback->usb2_com_ioctl) {
651			error = (sc->sc_callback->usb2_com_ioctl)
652			    (sc, cmd, data, 0, td);
653		} else {
654			error = ENOIOCTL;
655		}
656		break;
657	}
658	return (error);
659}
660
661static int
662usb2_com_modem(struct tty *tp, int sigon, int sigoff)
663{
664	struct usb2_com_softc *sc = tty_softc(tp);
665	uint8_t onoff;
666
667	mtx_assert(sc->sc_mtx, MA_OWNED);
668
669	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
670		return (0);
671	}
672	if ((sigon == 0) && (sigoff == 0)) {
673
674		if (sc->sc_mcr & SER_DTR) {
675			sigon |= SER_DTR;
676		}
677		if (sc->sc_mcr & SER_RTS) {
678			sigon |= SER_RTS;
679		}
680		if (sc->sc_msr & SER_CTS) {
681			sigon |= SER_CTS;
682		}
683		if (sc->sc_msr & SER_DCD) {
684			sigon |= SER_DCD;
685		}
686		if (sc->sc_msr & SER_DSR) {
687			sigon |= SER_DSR;
688		}
689		if (sc->sc_msr & SER_RI) {
690			sigon |= SER_RI;
691		}
692		return (sigon);
693	}
694	if (sigon & SER_DTR) {
695		sc->sc_mcr |= SER_DTR;
696	}
697	if (sigoff & SER_DTR) {
698		sc->sc_mcr &= ~SER_DTR;
699	}
700	if (sigon & SER_RTS) {
701		sc->sc_mcr |= SER_RTS;
702	}
703	if (sigoff & SER_RTS) {
704		sc->sc_mcr &= ~SER_RTS;
705	}
706	onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
707	usb2_com_dtr(sc, onoff);
708
709	onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
710	usb2_com_rts(sc, onoff);
711
712	return (0);
713}
714
715static void
716usb2_com_cfg_line_state(struct usb2_proc_msg *_task)
717{
718	struct usb2_com_cfg_task *task =
719	    (struct usb2_com_cfg_task *)_task;
720	struct usb2_com_softc *sc = task->sc;
721	uint8_t notch_bits;
722	uint8_t any_bits;
723	uint8_t prev_value;
724	uint8_t last_value;
725	uint8_t mask;
726
727	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
728		return;
729	}
730
731	mask = 0;
732	/* compute callback mask */
733	if (sc->sc_callback->usb2_com_cfg_set_dtr)
734		mask |= UCOM_LS_DTR;
735	if (sc->sc_callback->usb2_com_cfg_set_rts)
736		mask |= UCOM_LS_RTS;
737	if (sc->sc_callback->usb2_com_cfg_set_break)
738		mask |= UCOM_LS_BREAK;
739
740	/* compute the bits we are to program */
741	notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
742	any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
743	prev_value = sc->sc_pls_curr ^ notch_bits;
744	last_value = sc->sc_pls_curr;
745
746	/* reset programmed line state */
747	sc->sc_pls_curr = 0;
748	sc->sc_pls_set = 0;
749	sc->sc_pls_clr = 0;
750
751	/* ensure that we don't loose any levels */
752	if (notch_bits & UCOM_LS_DTR)
753		sc->sc_callback->usb2_com_cfg_set_dtr(sc,
754		    (prev_value & UCOM_LS_DTR) ? 1 : 0);
755	if (notch_bits & UCOM_LS_RTS)
756		sc->sc_callback->usb2_com_cfg_set_rts(sc,
757		    (prev_value & UCOM_LS_RTS) ? 1 : 0);
758	if (notch_bits & UCOM_LS_BREAK)
759		sc->sc_callback->usb2_com_cfg_set_break(sc,
760		    (prev_value & UCOM_LS_BREAK) ? 1 : 0);
761
762	/* set last value */
763	if (any_bits & UCOM_LS_DTR)
764		sc->sc_callback->usb2_com_cfg_set_dtr(sc,
765		    (last_value & UCOM_LS_DTR) ? 1 : 0);
766	if (any_bits & UCOM_LS_RTS)
767		sc->sc_callback->usb2_com_cfg_set_rts(sc,
768		    (last_value & UCOM_LS_RTS) ? 1 : 0);
769	if (any_bits & UCOM_LS_BREAK)
770		sc->sc_callback->usb2_com_cfg_set_break(sc,
771		    (last_value & UCOM_LS_BREAK) ? 1 : 0);
772}
773
774static void
775usb2_com_line_state(struct usb2_com_softc *sc,
776    uint8_t set_bits, uint8_t clear_bits)
777{
778	mtx_assert(sc->sc_mtx, MA_OWNED);
779
780	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
781		return;
782	}
783
784	DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
785
786	/* update current programmed line state */
787	sc->sc_pls_curr |= set_bits;
788	sc->sc_pls_curr &= ~clear_bits;
789	sc->sc_pls_set |= set_bits;
790	sc->sc_pls_clr |= clear_bits;
791
792	/* defer driver programming */
793	usb2_com_queue_command(sc, usb2_com_cfg_line_state, NULL,
794	    &sc->sc_line_state_task[0].hdr,
795	    &sc->sc_line_state_task[1].hdr);
796}
797
798static void
799usb2_com_break(struct usb2_com_softc *sc, uint8_t onoff)
800{
801	DPRINTF("onoff = %d\n", onoff);
802
803	if (onoff)
804		usb2_com_line_state(sc, UCOM_LS_BREAK, 0);
805	else
806		usb2_com_line_state(sc, 0, UCOM_LS_BREAK);
807}
808
809static void
810usb2_com_dtr(struct usb2_com_softc *sc, uint8_t onoff)
811{
812	DPRINTF("onoff = %d\n", onoff);
813
814	if (onoff)
815		usb2_com_line_state(sc, UCOM_LS_DTR, 0);
816	else
817		usb2_com_line_state(sc, 0, UCOM_LS_DTR);
818}
819
820static void
821usb2_com_rts(struct usb2_com_softc *sc, uint8_t onoff)
822{
823	DPRINTF("onoff = %d\n", onoff);
824
825	if (onoff)
826		usb2_com_line_state(sc, UCOM_LS_RTS, 0);
827	else
828		usb2_com_line_state(sc, 0, UCOM_LS_RTS);
829}
830
831static void
832usb2_com_cfg_status_change(struct usb2_proc_msg *_task)
833{
834	struct usb2_com_cfg_task *task =
835	    (struct usb2_com_cfg_task *)_task;
836	struct usb2_com_softc *sc = task->sc;
837	struct tty *tp;
838	uint8_t new_msr;
839	uint8_t new_lsr;
840	uint8_t onoff;
841
842	tp = sc->sc_tty;
843
844	mtx_assert(sc->sc_mtx, MA_OWNED);
845
846	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
847		return;
848	}
849	if (sc->sc_callback->usb2_com_cfg_get_status == NULL) {
850		return;
851	}
852	/* get status */
853
854	new_msr = 0;
855	new_lsr = 0;
856
857	(sc->sc_callback->usb2_com_cfg_get_status) (sc, &new_lsr, &new_msr);
858
859	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
860		/* TTY device closed */
861		return;
862	}
863	onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
864
865	sc->sc_msr = new_msr;
866	sc->sc_lsr = new_lsr;
867
868	if (onoff) {
869
870		onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
871
872		DPRINTF("DCD changed to %d\n", onoff);
873
874		ttydisc_modem(tp, onoff);
875	}
876}
877
878void
879usb2_com_status_change(struct usb2_com_softc *sc)
880{
881	mtx_assert(sc->sc_mtx, MA_OWNED);
882
883	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
884		return;
885	}
886	DPRINTF("\n");
887
888	usb2_com_queue_command(sc, usb2_com_cfg_status_change, NULL,
889	    &sc->sc_status_task[0].hdr,
890	    &sc->sc_status_task[1].hdr);
891}
892
893static void
894usb2_com_cfg_param(struct usb2_proc_msg *_task)
895{
896	struct usb2_com_param_task *task =
897	    (struct usb2_com_param_task *)_task;
898	struct usb2_com_softc *sc = task->sc;
899
900	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
901		return;
902	}
903	if (sc->sc_callback->usb2_com_cfg_param == NULL) {
904		return;
905	}
906
907	(sc->sc_callback->usb2_com_cfg_param) (sc, &task->termios_copy);
908
909	/* wait a little */
910	usb2_pause_mtx(sc->sc_mtx, hz / 10);
911}
912
913static int
914usb2_com_param(struct tty *tp, struct termios *t)
915{
916	struct usb2_com_softc *sc = tty_softc(tp);
917	uint8_t opened;
918	int error;
919
920	mtx_assert(sc->sc_mtx, MA_OWNED);
921
922	opened = 0;
923	error = 0;
924
925	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
926
927		/* XXX the TTY layer should call "open()" first! */
928
929		error = usb2_com_open(tp);
930		if (error) {
931			goto done;
932		}
933		opened = 1;
934	}
935	DPRINTF("sc = %p\n", sc);
936
937	/* Check requested parameters. */
938	if (t->c_ospeed < 0) {
939		DPRINTF("negative ospeed\n");
940		error = EINVAL;
941		goto done;
942	}
943	if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
944		DPRINTF("mismatch ispeed and ospeed\n");
945		error = EINVAL;
946		goto done;
947	}
948	t->c_ispeed = t->c_ospeed;
949
950	if (sc->sc_callback->usb2_com_pre_param) {
951		/* Let the lower layer verify the parameters */
952		error = (sc->sc_callback->usb2_com_pre_param) (sc, t);
953		if (error) {
954			DPRINTF("callback error = %d\n", error);
955			goto done;
956		}
957	}
958
959	/* Disable transfers */
960	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
961
962	/* Queue baud rate programming command first */
963	usb2_com_queue_command(sc, usb2_com_cfg_param, t,
964	    &sc->sc_param_task[0].hdr,
965	    &sc->sc_param_task[1].hdr);
966
967	/* Queue transfer enable command last */
968	usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL,
969	    &sc->sc_start_task[0].hdr,
970	    &sc->sc_start_task[1].hdr);
971
972	if (t->c_cflag & CRTS_IFLOW) {
973		sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
974	} else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
975		sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
976		usb2_com_modem(tp, SER_RTS, 0);
977	}
978done:
979	if (error) {
980		if (opened) {
981			usb2_com_close(tp);
982		}
983	}
984	return (error);
985}
986
987static void
988usb2_com_outwakeup(struct tty *tp)
989{
990	struct usb2_com_softc *sc = tty_softc(tp);
991
992	mtx_assert(sc->sc_mtx, MA_OWNED);
993
994	DPRINTF("sc = %p\n", sc);
995
996	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
997		/* The higher layer is not ready */
998		return;
999	}
1000	sc->sc_flag |= UCOM_FLAG_WR_START;
1001
1002	usb2_com_start_transfers(sc);
1003}
1004
1005/*------------------------------------------------------------------------*
1006 *	usb2_com_get_data
1007 *
1008 * Return values:
1009 * 0: No data is available.
1010 * Else: Data is available.
1011 *------------------------------------------------------------------------*/
1012uint8_t
1013usb2_com_get_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
1014    uint32_t offset, uint32_t len, uint32_t *actlen)
1015{
1016	struct usb2_page_search res;
1017	struct tty *tp = sc->sc_tty;
1018	uint32_t cnt;
1019	uint32_t offset_orig;
1020
1021	mtx_assert(sc->sc_mtx, MA_OWNED);
1022
1023	if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) ||
1024	    (!(sc->sc_flag & UCOM_FLAG_GP_DATA)) ||
1025	    (!(sc->sc_flag & UCOM_FLAG_WR_START))) {
1026		actlen[0] = 0;
1027		return (0);		/* multiport device polling */
1028	}
1029	offset_orig = offset;
1030
1031	while (len != 0) {
1032
1033		usb2_get_page(pc, offset, &res);
1034
1035		if (res.length > len) {
1036			res.length = len;
1037		}
1038		/* copy data directly into USB buffer */
1039		cnt = ttydisc_getc(tp, res.buffer, res.length);
1040
1041		offset += cnt;
1042		len -= cnt;
1043
1044		if (cnt < res.length) {
1045			/* end of buffer */
1046			break;
1047		}
1048	}
1049
1050	actlen[0] = offset - offset_orig;
1051
1052	DPRINTF("cnt=%d\n", actlen[0]);
1053
1054	if (actlen[0] == 0) {
1055		return (0);
1056	}
1057	return (1);
1058}
1059
1060void
1061usb2_com_put_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
1062    uint32_t offset, uint32_t len)
1063{
1064	struct usb2_page_search res;
1065	struct tty *tp = sc->sc_tty;
1066	char *buf;
1067	uint32_t cnt;
1068
1069	mtx_assert(sc->sc_mtx, MA_OWNED);
1070
1071	if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) ||
1072	    (!(sc->sc_flag & UCOM_FLAG_GP_DATA))) {
1073		return;			/* multiport device polling */
1074	}
1075	if (len == 0)
1076		return;			/* no data */
1077
1078	/* set a flag to prevent recursation ? */
1079
1080	while (len > 0) {
1081
1082		usb2_get_page(pc, offset, &res);
1083
1084		if (res.length > len) {
1085			res.length = len;
1086		}
1087		len -= res.length;
1088		offset += res.length;
1089
1090		/* pass characters to tty layer */
1091
1092		buf = res.buffer;
1093		cnt = res.length;
1094
1095		/* first check if we can pass the buffer directly */
1096
1097		if (ttydisc_can_bypass(tp)) {
1098			if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1099				DPRINTF("tp=%p, data lost\n", tp);
1100			}
1101			continue;
1102		}
1103		/* need to loop */
1104
1105		for (cnt = 0; cnt != res.length; cnt++) {
1106			if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
1107				/* XXX what should we do? */
1108
1109				DPRINTF("tp=%p, lost %d "
1110				    "chars\n", tp, res.length - cnt);
1111				break;
1112			}
1113		}
1114	}
1115	ttydisc_rint_done(tp);
1116}
1117
1118static void
1119usb2_com_free(void *xsc)
1120{
1121	struct usb2_com_softc *sc = xsc;
1122
1123	mtx_lock(sc->sc_mtx);
1124	sc->sc_ttyfreed = 1;
1125	usb2_cv_signal(&sc->sc_cv);
1126	mtx_unlock(sc->sc_mtx);
1127}
1128