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