usb_serial.c revision 194677
1121934Sharti/*	$NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $	*/
2121934Sharti
3121934Sharti/*-
4121934Sharti * Copyright (c) 2001-2003, 2005, 2008
5121934Sharti *	Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
6131826Sharti * All rights reserved.
7131826Sharti *
8121934Sharti * Redistribution and use in source and binary forms, with or without
9121934Sharti * modification, are permitted provided that the following conditions
10121934Sharti * are met:
11121934Sharti * 1. Redistributions of source code must retain the above copyright
12121934Sharti *    notice, this list of conditions and the following disclaimer.
13121934Sharti * 2. Redistributions in binary form must reproduce the above copyright
14121934Sharti *    notice, this list of conditions and the following disclaimer in the
15121934Sharti *    documentation and/or other materials provided with the distribution.
16121934Sharti *
17121934Sharti * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18121934Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19121934Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20121934Sharti * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21121934Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22121934Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23121934Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24121934Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25121934Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26121934Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27121934Sharti * SUCH DAMAGE.
28121934Sharti */
29131826Sharti
30121934Sharti#include <sys/cdefs.h>
31121934Sharti__FBSDID("$FreeBSD: head/sys/dev/usb/serial/usb_serial.c 194677 2009-06-23 02:19:59Z thompsa $");
32121934Sharti
33121934Sharti/*-
34121934Sharti * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
35121934Sharti * All rights reserved.
36121934Sharti *
37121934Sharti * This code is derived from software contributed to The NetBSD Foundation
38121934Sharti * by Lennart Augustsson (lennart@augustsson.net) at
39121934Sharti * Carlstedt Research & Technology.
40121934Sharti *
41121934Sharti * Redistribution and use in source and binary forms, with or without
42121934Sharti * modification, are permitted provided that the following conditions
43121934Sharti * are met:
44121934Sharti * 1. Redistributions of source code must retain the above copyright
45121934Sharti *    notice, this list of conditions and the following disclaimer.
46121934Sharti * 2. Redistributions in binary form must reproduce the above copyright
47121934Sharti *    notice, this list of conditions and the following disclaimer in the
48121934Sharti *    documentation and/or other materials provided with the distribution.
49121934Sharti * 3. All advertising materials mentioning features or use of this software
50121934Sharti *    must display the following acknowledgement:
51121934Sharti *        This product includes software developed by the NetBSD
52121934Sharti *        Foundation, Inc. and its contributors.
53121934Sharti * 4. Neither the name of The NetBSD Foundation nor the names of its
54121934Sharti *    contributors may be used to endorse or promote products derived
55121934Sharti *    from this software without specific prior written permission.
56121934Sharti *
57121934Sharti * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
58121934Sharti * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
59121934Sharti * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
60121934Sharti * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
61121934Sharti * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
62121934Sharti * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
63121934Sharti * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
64121934Sharti * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65121934Sharti * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
66121934Sharti * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
67121934Sharti * POSSIBILITY OF SUCH DAMAGE.
68121934Sharti */
69121934Sharti
70121934Sharti#include <sys/stdint.h>
71121934Sharti#include <sys/stddef.h>
72121934Sharti#include <sys/param.h>
73121934Sharti#include <sys/queue.h>
74121934Sharti#include <sys/types.h>
75121934Sharti#include <sys/systm.h>
76121934Sharti#include <sys/kernel.h>
77121934Sharti#include <sys/bus.h>
78121934Sharti#include <sys/linker_set.h>
79121934Sharti#include <sys/module.h>
80121934Sharti#include <sys/lock.h>
81121934Sharti#include <sys/mutex.h>
82121934Sharti#include <sys/condvar.h>
83121934Sharti#include <sys/sysctl.h>
84121934Sharti#include <sys/sx.h>
85121934Sharti#include <sys/unistd.h>
86121934Sharti#include <sys/callout.h>
87121934Sharti#include <sys/malloc.h>
88121934Sharti#include <sys/priv.h>
89121934Sharti
90121934Sharti#include <dev/usb/usb.h>
91121934Sharti#include <dev/usb/usbdi.h>
92121934Sharti#include <dev/usb/usbdi_util.h>
93121934Sharti
94121934Sharti#define	USB_DEBUG_VAR ucom_debug
95121934Sharti#include <dev/usb/usb_debug.h>
96121934Sharti#include <dev/usb/usb_busdma.h>
97121934Sharti#include <dev/usb/usb_process.h>
98121934Sharti
99121934Sharti#include <dev/usb/serial/usb_serial.h>
100121934Sharti
101121934Sharti#if USB_DEBUG
102121934Shartistatic int ucom_debug = 0;
103121934Sharti
104121934ShartiSYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
105121934ShartiSYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
106121934Sharti    &ucom_debug, 0, "ucom debug level");
107121934Sharti#endif
108121934Sharti
109121934Shartistatic usb_proc_callback_t ucom_cfg_start_transfers;
110121934Shartistatic usb_proc_callback_t ucom_cfg_open;
111121934Shartistatic usb_proc_callback_t ucom_cfg_close;
112121934Shartistatic usb_proc_callback_t ucom_cfg_line_state;
113121934Shartistatic usb_proc_callback_t ucom_cfg_status_change;
114121934Shartistatic usb_proc_callback_t ucom_cfg_param;
115121934Sharti
116121934Shartistatic uint8_t	ucom_units_alloc(uint32_t, uint32_t *);
117121934Shartistatic void	ucom_units_free(uint32_t, uint32_t);
118121934Shartistatic int	ucom_attach_tty(struct ucom_softc *, uint32_t);
119121934Shartistatic void	ucom_detach_tty(struct ucom_softc *);
120121934Shartistatic void	ucom_queue_command(struct ucom_softc *,
121121934Sharti		    usb_proc_callback_t *, struct termios *pt,
122121934Sharti		    struct usb_proc_msg *t0, struct usb_proc_msg *t1);
123121934Shartistatic void	ucom_shutdown(struct ucom_softc *);
124121934Shartistatic void	ucom_break(struct ucom_softc *, uint8_t);
125121934Shartistatic void	ucom_dtr(struct ucom_softc *, uint8_t);
126121934Shartistatic void	ucom_rts(struct ucom_softc *, uint8_t);
127121934Sharti
128121934Shartistatic tsw_open_t ucom_open;
129121934Shartistatic tsw_close_t ucom_close;
130121934Shartistatic tsw_ioctl_t ucom_ioctl;
131121934Shartistatic tsw_modem_t ucom_modem;
132121934Shartistatic tsw_param_t ucom_param;
133121934Shartistatic tsw_outwakeup_t ucom_outwakeup;
134121934Shartistatic tsw_free_t ucom_free;
135121934Sharti
136121934Shartistatic struct ttydevsw ucom_class = {
137121934Sharti	.tsw_flags = TF_INITLOCK | TF_CALLOUT,
138121934Sharti	.tsw_open = ucom_open,
139121934Sharti	.tsw_close = ucom_close,
140121934Sharti	.tsw_outwakeup = ucom_outwakeup,
141121934Sharti	.tsw_ioctl = ucom_ioctl,
142121934Sharti	.tsw_param = ucom_param,
143121934Sharti	.tsw_modem = ucom_modem,
144121934Sharti	.tsw_free = ucom_free,
145121934Sharti};
146121934Sharti
147121934ShartiMODULE_DEPEND(ucom, usb, 1, 1, 1);
148121934ShartiMODULE_VERSION(ucom, 1);
149121934Sharti
150121934Sharti#define	UCOM_UNIT_MAX 0x1000		/* exclusive */
151121934Sharti#define	UCOM_SUB_UNIT_MAX 0x100		/* exclusive */
152121934Sharti
153121934Shartistatic uint8_t ucom_bitmap[(UCOM_UNIT_MAX + 7) / 8];
154121934Sharti
155121934Shartistatic uint8_t
156121934Shartiucom_units_alloc(uint32_t sub_units, uint32_t *p_root_unit)
157121934Sharti{
158121934Sharti	uint32_t n;
159121934Sharti	uint32_t o;
160121934Sharti	uint32_t x;
161121934Sharti	uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units);
162121934Sharti	uint8_t error = 1;
163121934Sharti
164121934Sharti	mtx_lock(&Giant);
165121934Sharti
166121934Sharti	for (n = 0; n < max; n += sub_units) {
167121934Sharti
168121934Sharti		/* check for free consecutive bits */
169121934Sharti
170121934Sharti		for (o = 0; o < sub_units; o++) {
171121934Sharti
172121934Sharti			x = n + o;
173121934Sharti
174121934Sharti			if (ucom_bitmap[x / 8] & (1 << (x % 8))) {
175121934Sharti				goto skip;
176121934Sharti			}
177121934Sharti		}
178121934Sharti
179121934Sharti		/* allocate */
180121934Sharti
181121934Sharti		for (o = 0; o < sub_units; o++) {
182121934Sharti
183121934Sharti			x = n + o;
184121934Sharti
185121934Sharti			ucom_bitmap[x / 8] |= (1 << (x % 8));
186121934Sharti		}
187121934Sharti
188121934Sharti		error = 0;
189121934Sharti
190121934Sharti		break;
191121934Sharti
192121934Shartiskip:		;
193121934Sharti	}
194121934Sharti
195121934Sharti	mtx_unlock(&Giant);
196121934Sharti
197121934Sharti	/*
198121934Sharti	 * Always set the variable pointed to by "p_root_unit" so that
199121934Sharti	 * the compiler does not think that it is used uninitialised:
200121934Sharti	 */
201121934Sharti	*p_root_unit = n;
202121934Sharti
203121934Sharti	return (error);
204121934Sharti}
205121934Sharti
206121934Shartistatic void
207121934Shartiucom_units_free(uint32_t root_unit, uint32_t sub_units)
208121934Sharti{
209121934Sharti	uint32_t x;
210121934Sharti
211121934Sharti	mtx_lock(&Giant);
212121934Sharti
213121934Sharti	while (sub_units--) {
214121934Sharti		x = root_unit + sub_units;
215121934Sharti		ucom_bitmap[x / 8] &= ~(1 << (x % 8));
216121934Sharti	}
217121934Sharti
218121934Sharti	mtx_unlock(&Giant);
219121934Sharti}
220121934Sharti
221121934Sharti/*
222121934Sharti * "N" sub_units are setup at a time. All sub-units will
223121934Sharti * be given sequential unit numbers. The number of
224121934Sharti * sub-units can be used to differentiate among
225121934Sharti * different types of devices.
226121934Sharti *
227121934Sharti * The mutex pointed to by "mtx" is applied before all
228121934Sharti * callbacks are called back. Also "mtx" must be applied
229121934Sharti * before calling into the ucom-layer!
230121934Sharti */
231121934Shartiint
232121934Shartiucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
233121934Sharti    uint32_t sub_units, void *parent,
234121934Sharti    const struct ucom_callback *callback, struct mtx *mtx)
235121934Sharti{
236	uint32_t n;
237	uint32_t root_unit;
238	int error = 0;
239
240	if ((sc == NULL) ||
241	    (sub_units == 0) ||
242	    (sub_units > UCOM_SUB_UNIT_MAX) ||
243	    (callback == NULL)) {
244		return (EINVAL);
245	}
246
247	/* XXX unit management does not really belong here */
248	if (ucom_units_alloc(sub_units, &root_unit)) {
249		return (ENOMEM);
250	}
251
252	error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
253	if (error) {
254		ucom_units_free(root_unit, sub_units);
255		return (error);
256	}
257
258	for (n = 0; n != sub_units; n++, sc++) {
259		sc->sc_unit = root_unit + n;
260		sc->sc_local_unit = n;
261		sc->sc_super = ssc;
262		sc->sc_mtx = mtx;
263		sc->sc_parent = parent;
264		sc->sc_callback = callback;
265
266		error = ucom_attach_tty(sc, sub_units);
267		if (error) {
268			ucom_detach(ssc, sc - n, n);
269			ucom_units_free(root_unit + n, sub_units - n);
270			return (error);
271		}
272		sc->sc_flag |= UCOM_FLAG_ATTACHED;
273	}
274	return (0);
275}
276
277/*
278 * NOTE: the following function will do nothing if
279 * the structure pointed to by "ssc" and "sc" is zero.
280 */
281void
282ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
283    uint32_t sub_units)
284{
285	uint32_t n;
286
287	usb_proc_drain(&ssc->sc_tq);
288
289	for (n = 0; n != sub_units; n++, sc++) {
290		if (sc->sc_flag & UCOM_FLAG_ATTACHED) {
291
292			ucom_detach_tty(sc);
293
294			ucom_units_free(sc->sc_unit, 1);
295
296			/* avoid duplicate detach: */
297			sc->sc_flag &= ~UCOM_FLAG_ATTACHED;
298		}
299	}
300	usb_proc_free(&ssc->sc_tq);
301}
302
303static int
304ucom_attach_tty(struct ucom_softc *sc, uint32_t sub_units)
305{
306	struct tty *tp;
307	int error = 0;
308	char buf[32];			/* temporary TTY device name buffer */
309
310	tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx);
311	if (tp == NULL) {
312		error = ENOMEM;
313		goto done;
314	}
315	DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit);
316
317	buf[0] = 0;			/* set some default value */
318
319	/* Check if the client has a custom TTY name */
320	if (sc->sc_callback->ucom_tty_name) {
321		sc->sc_callback->ucom_tty_name(sc, buf,
322		    sizeof(buf), sc->sc_local_unit);
323	}
324	if (buf[0] == 0) {
325		/* Use default TTY name */
326		if (sub_units > 1) {
327			/* multiple modems in one */
328			if (snprintf(buf, sizeof(buf), "U%u.%u",
329			    sc->sc_unit - sc->sc_local_unit,
330			    sc->sc_local_unit)) {
331				/* ignore */
332			}
333		} else {
334			/* single modem */
335			if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) {
336				/* ignore */
337			}
338		}
339	}
340	tty_makedev(tp, NULL, "%s", buf);
341
342	sc->sc_tty = tp;
343
344	DPRINTF("ttycreate: %s\n", buf);
345	cv_init(&sc->sc_cv, "ucom");
346
347done:
348	return (error);
349}
350
351static void
352ucom_detach_tty(struct ucom_softc *sc)
353{
354	struct tty *tp = sc->sc_tty;
355
356	DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
357
358	/* the config thread has been stopped when we get here */
359
360	mtx_lock(sc->sc_mtx);
361	sc->sc_flag |= UCOM_FLAG_GONE;
362	sc->sc_flag &= ~(UCOM_FLAG_HL_READY |
363	    UCOM_FLAG_LL_READY);
364	mtx_unlock(sc->sc_mtx);
365	if (tp) {
366		tty_lock(tp);
367
368		ucom_close(tp);	/* close, if any */
369
370		tty_rel_gone(tp);
371
372		mtx_lock(sc->sc_mtx);
373		/* Wait for the callback after the TTY is torn down */
374		while (sc->sc_ttyfreed == 0)
375			cv_wait(&sc->sc_cv, sc->sc_mtx);
376		/*
377		 * make sure that read and write transfers are stopped
378		 */
379		if (sc->sc_callback->ucom_stop_read) {
380			(sc->sc_callback->ucom_stop_read) (sc);
381		}
382		if (sc->sc_callback->ucom_stop_write) {
383			(sc->sc_callback->ucom_stop_write) (sc);
384		}
385		mtx_unlock(sc->sc_mtx);
386	}
387	cv_destroy(&sc->sc_cv);
388}
389
390static void
391ucom_queue_command(struct ucom_softc *sc,
392    usb_proc_callback_t *fn, struct termios *pt,
393    struct usb_proc_msg *t0, struct usb_proc_msg *t1)
394{
395	struct ucom_super_softc *ssc = sc->sc_super;
396	struct ucom_param_task *task;
397
398	mtx_assert(sc->sc_mtx, MA_OWNED);
399
400	if (usb_proc_is_gone(&ssc->sc_tq)) {
401		DPRINTF("proc is gone\n");
402		return;         /* nothing to do */
403	}
404	/*
405	 * NOTE: The task cannot get executed before we drop the
406	 * "sc_mtx" mutex. It is safe to update fields in the message
407	 * structure after that the message got queued.
408	 */
409	task = (struct ucom_param_task *)
410	  usb_proc_msignal(&ssc->sc_tq, t0, t1);
411
412	/* Setup callback and softc pointers */
413	task->hdr.pm_callback = fn;
414	task->sc = sc;
415
416	/*
417	 * Make a copy of the termios. This field is only present if
418	 * the "pt" field is not NULL.
419	 */
420	if (pt != NULL)
421		task->termios_copy = *pt;
422
423	/*
424	 * Closing the device should be synchronous.
425	 */
426	if (fn == ucom_cfg_close)
427		usb_proc_mwait(&ssc->sc_tq, t0, t1);
428
429	/*
430	 * In case of multiple configure requests,
431	 * keep track of the last one!
432	 */
433	if (fn == ucom_cfg_start_transfers)
434		sc->sc_last_start_xfer = &task->hdr;
435}
436
437static void
438ucom_shutdown(struct ucom_softc *sc)
439{
440	struct tty *tp = sc->sc_tty;
441
442	mtx_assert(sc->sc_mtx, MA_OWNED);
443
444	DPRINTF("\n");
445
446	/*
447	 * Hang up if necessary:
448	 */
449	if (tp->t_termios.c_cflag & HUPCL) {
450		ucom_modem(tp, 0, SER_DTR);
451	}
452}
453
454/*
455 * Return values:
456 *    0: normal
457 * else: taskqueue is draining or gone
458 */
459uint8_t
460ucom_cfg_is_gone(struct ucom_softc *sc)
461{
462	struct ucom_super_softc *ssc = sc->sc_super;
463
464	return (usb_proc_is_gone(&ssc->sc_tq));
465}
466
467static void
468ucom_cfg_start_transfers(struct usb_proc_msg *_task)
469{
470	struct ucom_cfg_task *task =
471	    (struct ucom_cfg_task *)_task;
472	struct ucom_softc *sc = task->sc;
473
474	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
475		return;
476	}
477	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
478		/* TTY device closed */
479		return;
480	}
481
482	if (_task == sc->sc_last_start_xfer)
483		sc->sc_flag |= UCOM_FLAG_GP_DATA;
484
485	if (sc->sc_callback->ucom_start_read) {
486		(sc->sc_callback->ucom_start_read) (sc);
487	}
488	if (sc->sc_callback->ucom_start_write) {
489		(sc->sc_callback->ucom_start_write) (sc);
490	}
491}
492
493static void
494ucom_start_transfers(struct ucom_softc *sc)
495{
496	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
497		return;
498	}
499	/*
500	 * Make sure that data transfers are started in both
501	 * directions:
502	 */
503	if (sc->sc_callback->ucom_start_read) {
504		(sc->sc_callback->ucom_start_read) (sc);
505	}
506	if (sc->sc_callback->ucom_start_write) {
507		(sc->sc_callback->ucom_start_write) (sc);
508	}
509}
510
511static void
512ucom_cfg_open(struct usb_proc_msg *_task)
513{
514	struct ucom_cfg_task *task =
515	    (struct ucom_cfg_task *)_task;
516	struct ucom_softc *sc = task->sc;
517
518	DPRINTF("\n");
519
520	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
521
522		/* already opened */
523
524	} else {
525
526		sc->sc_flag |= UCOM_FLAG_LL_READY;
527
528		if (sc->sc_callback->ucom_cfg_open) {
529			(sc->sc_callback->ucom_cfg_open) (sc);
530
531			/* wait a little */
532			usb_pause_mtx(sc->sc_mtx, hz / 10);
533		}
534	}
535}
536
537static int
538ucom_open(struct tty *tp)
539{
540	struct ucom_softc *sc = tty_softc(tp);
541	int error;
542
543	mtx_assert(sc->sc_mtx, MA_OWNED);
544
545	if (sc->sc_flag & UCOM_FLAG_GONE) {
546		return (ENXIO);
547	}
548	if (sc->sc_flag & UCOM_FLAG_HL_READY) {
549		/* already opened */
550		return (0);
551	}
552	DPRINTF("tp = %p\n", tp);
553
554	if (sc->sc_callback->ucom_pre_open) {
555		/*
556		 * give the lower layer a chance to disallow TTY open, for
557		 * example if the device is not present:
558		 */
559		error = (sc->sc_callback->ucom_pre_open) (sc);
560		if (error) {
561			return (error);
562		}
563	}
564	sc->sc_flag |= UCOM_FLAG_HL_READY;
565
566	/* Disable transfers */
567	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
568
569	sc->sc_lsr = 0;
570	sc->sc_msr = 0;
571	sc->sc_mcr = 0;
572
573	/* reset programmed line state */
574	sc->sc_pls_curr = 0;
575	sc->sc_pls_set = 0;
576	sc->sc_pls_clr = 0;
577
578	ucom_queue_command(sc, ucom_cfg_open, NULL,
579	    &sc->sc_open_task[0].hdr,
580	    &sc->sc_open_task[1].hdr);
581
582	/* Queue transfer enable command last */
583	ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
584	    &sc->sc_start_task[0].hdr,
585	    &sc->sc_start_task[1].hdr);
586
587	ucom_modem(tp, SER_DTR | SER_RTS, 0);
588
589	ucom_break(sc, 0);
590
591	ucom_status_change(sc);
592
593	return (0);
594}
595
596static void
597ucom_cfg_close(struct usb_proc_msg *_task)
598{
599	struct ucom_cfg_task *task =
600	    (struct ucom_cfg_task *)_task;
601	struct ucom_softc *sc = task->sc;
602
603	DPRINTF("\n");
604
605	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
606		sc->sc_flag &= ~UCOM_FLAG_LL_READY;
607		if (sc->sc_callback->ucom_cfg_close)
608			(sc->sc_callback->ucom_cfg_close) (sc);
609	} else {
610		/* already closed */
611	}
612}
613
614static void
615ucom_close(struct tty *tp)
616{
617	struct ucom_softc *sc = tty_softc(tp);
618
619	mtx_assert(sc->sc_mtx, MA_OWNED);
620
621	DPRINTF("tp=%p\n", tp);
622
623	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
624		DPRINTF("tp=%p already closed\n", tp);
625		return;
626	}
627	ucom_shutdown(sc);
628
629	ucom_queue_command(sc, ucom_cfg_close, NULL,
630	    &sc->sc_close_task[0].hdr,
631	    &sc->sc_close_task[1].hdr);
632
633	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
634
635	if (sc->sc_callback->ucom_stop_read) {
636		(sc->sc_callback->ucom_stop_read) (sc);
637	}
638}
639
640static int
641ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
642{
643	struct ucom_softc *sc = tty_softc(tp);
644	int error;
645
646	mtx_assert(sc->sc_mtx, MA_OWNED);
647
648	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
649		return (EIO);
650	}
651	DPRINTF("cmd = 0x%08lx\n", cmd);
652
653	switch (cmd) {
654	case TIOCSBRK:
655		ucom_break(sc, 1);
656		error = 0;
657		break;
658	case TIOCCBRK:
659		ucom_break(sc, 0);
660		error = 0;
661		break;
662	default:
663		if (sc->sc_callback->ucom_ioctl) {
664			error = (sc->sc_callback->ucom_ioctl)
665			    (sc, cmd, data, 0, td);
666		} else {
667			error = ENOIOCTL;
668		}
669		break;
670	}
671	return (error);
672}
673
674static int
675ucom_modem(struct tty *tp, int sigon, int sigoff)
676{
677	struct ucom_softc *sc = tty_softc(tp);
678	uint8_t onoff;
679
680	mtx_assert(sc->sc_mtx, MA_OWNED);
681
682	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
683		return (0);
684	}
685	if ((sigon == 0) && (sigoff == 0)) {
686
687		if (sc->sc_mcr & SER_DTR) {
688			sigon |= SER_DTR;
689		}
690		if (sc->sc_mcr & SER_RTS) {
691			sigon |= SER_RTS;
692		}
693		if (sc->sc_msr & SER_CTS) {
694			sigon |= SER_CTS;
695		}
696		if (sc->sc_msr & SER_DCD) {
697			sigon |= SER_DCD;
698		}
699		if (sc->sc_msr & SER_DSR) {
700			sigon |= SER_DSR;
701		}
702		if (sc->sc_msr & SER_RI) {
703			sigon |= SER_RI;
704		}
705		return (sigon);
706	}
707	if (sigon & SER_DTR) {
708		sc->sc_mcr |= SER_DTR;
709	}
710	if (sigoff & SER_DTR) {
711		sc->sc_mcr &= ~SER_DTR;
712	}
713	if (sigon & SER_RTS) {
714		sc->sc_mcr |= SER_RTS;
715	}
716	if (sigoff & SER_RTS) {
717		sc->sc_mcr &= ~SER_RTS;
718	}
719	onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
720	ucom_dtr(sc, onoff);
721
722	onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
723	ucom_rts(sc, onoff);
724
725	return (0);
726}
727
728static void
729ucom_cfg_line_state(struct usb_proc_msg *_task)
730{
731	struct ucom_cfg_task *task =
732	    (struct ucom_cfg_task *)_task;
733	struct ucom_softc *sc = task->sc;
734	uint8_t notch_bits;
735	uint8_t any_bits;
736	uint8_t prev_value;
737	uint8_t last_value;
738	uint8_t mask;
739
740	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
741		return;
742	}
743
744	mask = 0;
745	/* compute callback mask */
746	if (sc->sc_callback->ucom_cfg_set_dtr)
747		mask |= UCOM_LS_DTR;
748	if (sc->sc_callback->ucom_cfg_set_rts)
749		mask |= UCOM_LS_RTS;
750	if (sc->sc_callback->ucom_cfg_set_break)
751		mask |= UCOM_LS_BREAK;
752
753	/* compute the bits we are to program */
754	notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
755	any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
756	prev_value = sc->sc_pls_curr ^ notch_bits;
757	last_value = sc->sc_pls_curr;
758
759	/* reset programmed line state */
760	sc->sc_pls_curr = 0;
761	sc->sc_pls_set = 0;
762	sc->sc_pls_clr = 0;
763
764	/* ensure that we don't loose any levels */
765	if (notch_bits & UCOM_LS_DTR)
766		sc->sc_callback->ucom_cfg_set_dtr(sc,
767		    (prev_value & UCOM_LS_DTR) ? 1 : 0);
768	if (notch_bits & UCOM_LS_RTS)
769		sc->sc_callback->ucom_cfg_set_rts(sc,
770		    (prev_value & UCOM_LS_RTS) ? 1 : 0);
771	if (notch_bits & UCOM_LS_BREAK)
772		sc->sc_callback->ucom_cfg_set_break(sc,
773		    (prev_value & UCOM_LS_BREAK) ? 1 : 0);
774
775	/* set last value */
776	if (any_bits & UCOM_LS_DTR)
777		sc->sc_callback->ucom_cfg_set_dtr(sc,
778		    (last_value & UCOM_LS_DTR) ? 1 : 0);
779	if (any_bits & UCOM_LS_RTS)
780		sc->sc_callback->ucom_cfg_set_rts(sc,
781		    (last_value & UCOM_LS_RTS) ? 1 : 0);
782	if (any_bits & UCOM_LS_BREAK)
783		sc->sc_callback->ucom_cfg_set_break(sc,
784		    (last_value & UCOM_LS_BREAK) ? 1 : 0);
785}
786
787static void
788ucom_line_state(struct ucom_softc *sc,
789    uint8_t set_bits, uint8_t clear_bits)
790{
791	mtx_assert(sc->sc_mtx, MA_OWNED);
792
793	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
794		return;
795	}
796
797	DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
798
799	/* update current programmed line state */
800	sc->sc_pls_curr |= set_bits;
801	sc->sc_pls_curr &= ~clear_bits;
802	sc->sc_pls_set |= set_bits;
803	sc->sc_pls_clr |= clear_bits;
804
805	/* defer driver programming */
806	ucom_queue_command(sc, ucom_cfg_line_state, NULL,
807	    &sc->sc_line_state_task[0].hdr,
808	    &sc->sc_line_state_task[1].hdr);
809}
810
811static void
812ucom_break(struct ucom_softc *sc, uint8_t onoff)
813{
814	DPRINTF("onoff = %d\n", onoff);
815
816	if (onoff)
817		ucom_line_state(sc, UCOM_LS_BREAK, 0);
818	else
819		ucom_line_state(sc, 0, UCOM_LS_BREAK);
820}
821
822static void
823ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
824{
825	DPRINTF("onoff = %d\n", onoff);
826
827	if (onoff)
828		ucom_line_state(sc, UCOM_LS_DTR, 0);
829	else
830		ucom_line_state(sc, 0, UCOM_LS_DTR);
831}
832
833static void
834ucom_rts(struct ucom_softc *sc, uint8_t onoff)
835{
836	DPRINTF("onoff = %d\n", onoff);
837
838	if (onoff)
839		ucom_line_state(sc, UCOM_LS_RTS, 0);
840	else
841		ucom_line_state(sc, 0, UCOM_LS_RTS);
842}
843
844static void
845ucom_cfg_status_change(struct usb_proc_msg *_task)
846{
847	struct ucom_cfg_task *task =
848	    (struct ucom_cfg_task *)_task;
849	struct ucom_softc *sc = task->sc;
850	struct tty *tp;
851	uint8_t new_msr;
852	uint8_t new_lsr;
853	uint8_t onoff;
854
855	tp = sc->sc_tty;
856
857	mtx_assert(sc->sc_mtx, MA_OWNED);
858
859	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
860		return;
861	}
862	if (sc->sc_callback->ucom_cfg_get_status == NULL) {
863		return;
864	}
865	/* get status */
866
867	new_msr = 0;
868	new_lsr = 0;
869
870	(sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
871
872	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
873		/* TTY device closed */
874		return;
875	}
876	onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
877
878	sc->sc_msr = new_msr;
879	sc->sc_lsr = new_lsr;
880
881	if (onoff) {
882
883		onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
884
885		DPRINTF("DCD changed to %d\n", onoff);
886
887		ttydisc_modem(tp, onoff);
888	}
889}
890
891void
892ucom_status_change(struct ucom_softc *sc)
893{
894	mtx_assert(sc->sc_mtx, MA_OWNED);
895
896	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
897		return;
898	}
899	DPRINTF("\n");
900
901	ucom_queue_command(sc, ucom_cfg_status_change, NULL,
902	    &sc->sc_status_task[0].hdr,
903	    &sc->sc_status_task[1].hdr);
904}
905
906static void
907ucom_cfg_param(struct usb_proc_msg *_task)
908{
909	struct ucom_param_task *task =
910	    (struct ucom_param_task *)_task;
911	struct ucom_softc *sc = task->sc;
912
913	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
914		return;
915	}
916	if (sc->sc_callback->ucom_cfg_param == NULL) {
917		return;
918	}
919
920	(sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
921
922	/* wait a little */
923	usb_pause_mtx(sc->sc_mtx, hz / 10);
924}
925
926static int
927ucom_param(struct tty *tp, struct termios *t)
928{
929	struct ucom_softc *sc = tty_softc(tp);
930	uint8_t opened;
931	int error;
932
933	mtx_assert(sc->sc_mtx, MA_OWNED);
934
935	opened = 0;
936	error = 0;
937
938	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
939
940		/* XXX the TTY layer should call "open()" first! */
941
942		error = ucom_open(tp);
943		if (error) {
944			goto done;
945		}
946		opened = 1;
947	}
948	DPRINTF("sc = %p\n", sc);
949
950	/* Check requested parameters. */
951	if (t->c_ospeed < 0) {
952		DPRINTF("negative ospeed\n");
953		error = EINVAL;
954		goto done;
955	}
956	if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
957		DPRINTF("mismatch ispeed and ospeed\n");
958		error = EINVAL;
959		goto done;
960	}
961	t->c_ispeed = t->c_ospeed;
962
963	if (sc->sc_callback->ucom_pre_param) {
964		/* Let the lower layer verify the parameters */
965		error = (sc->sc_callback->ucom_pre_param) (sc, t);
966		if (error) {
967			DPRINTF("callback error = %d\n", error);
968			goto done;
969		}
970	}
971
972	/* Disable transfers */
973	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
974
975	/* Queue baud rate programming command first */
976	ucom_queue_command(sc, ucom_cfg_param, t,
977	    &sc->sc_param_task[0].hdr,
978	    &sc->sc_param_task[1].hdr);
979
980	/* Queue transfer enable command last */
981	ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
982	    &sc->sc_start_task[0].hdr,
983	    &sc->sc_start_task[1].hdr);
984
985	if (t->c_cflag & CRTS_IFLOW) {
986		sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
987	} else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
988		sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
989		ucom_modem(tp, SER_RTS, 0);
990	}
991done:
992	if (error) {
993		if (opened) {
994			ucom_close(tp);
995		}
996	}
997	return (error);
998}
999
1000static void
1001ucom_outwakeup(struct tty *tp)
1002{
1003	struct ucom_softc *sc = tty_softc(tp);
1004
1005	mtx_assert(sc->sc_mtx, MA_OWNED);
1006
1007	DPRINTF("sc = %p\n", sc);
1008
1009	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1010		/* The higher layer is not ready */
1011		return;
1012	}
1013	ucom_start_transfers(sc);
1014}
1015
1016/*------------------------------------------------------------------------*
1017 *	ucom_get_data
1018 *
1019 * Return values:
1020 * 0: No data is available.
1021 * Else: Data is available.
1022 *------------------------------------------------------------------------*/
1023uint8_t
1024ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1025    uint32_t offset, uint32_t len, uint32_t *actlen)
1026{
1027	struct usb_page_search res;
1028	struct tty *tp = sc->sc_tty;
1029	uint32_t cnt;
1030	uint32_t offset_orig;
1031
1032	mtx_assert(sc->sc_mtx, MA_OWNED);
1033
1034	if (tty_gone(tp) ||
1035	    !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
1036		actlen[0] = 0;
1037		return (0);		/* multiport device polling */
1038	}
1039	offset_orig = offset;
1040
1041	while (len != 0) {
1042
1043		usbd_get_page(pc, offset, &res);
1044
1045		if (res.length > len) {
1046			res.length = len;
1047		}
1048		/* copy data directly into USB buffer */
1049		cnt = ttydisc_getc(tp, res.buffer, res.length);
1050
1051		offset += cnt;
1052		len -= cnt;
1053
1054		if (cnt < res.length) {
1055			/* end of buffer */
1056			break;
1057		}
1058	}
1059
1060	actlen[0] = offset - offset_orig;
1061
1062	DPRINTF("cnt=%d\n", actlen[0]);
1063
1064	if (actlen[0] == 0) {
1065		return (0);
1066	}
1067	return (1);
1068}
1069
1070void
1071ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1072    uint32_t offset, uint32_t len)
1073{
1074	struct usb_page_search res;
1075	struct tty *tp = sc->sc_tty;
1076	char *buf;
1077	uint32_t cnt;
1078
1079	mtx_assert(sc->sc_mtx, MA_OWNED);
1080
1081	if (tty_gone(tp))
1082		return;			/* multiport device polling */
1083
1084	if (len == 0)
1085		return;			/* no data */
1086
1087	/* set a flag to prevent recursation ? */
1088
1089	while (len > 0) {
1090
1091		usbd_get_page(pc, offset, &res);
1092
1093		if (res.length > len) {
1094			res.length = len;
1095		}
1096		len -= res.length;
1097		offset += res.length;
1098
1099		/* pass characters to tty layer */
1100
1101		buf = res.buffer;
1102		cnt = res.length;
1103
1104		/* first check if we can pass the buffer directly */
1105
1106		if (ttydisc_can_bypass(tp)) {
1107			if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1108				DPRINTF("tp=%p, data lost\n", tp);
1109			}
1110			continue;
1111		}
1112		/* need to loop */
1113
1114		for (cnt = 0; cnt != res.length; cnt++) {
1115			if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
1116				/* XXX what should we do? */
1117
1118				DPRINTF("tp=%p, lost %d "
1119				    "chars\n", tp, res.length - cnt);
1120				break;
1121			}
1122		}
1123	}
1124	ttydisc_rint_done(tp);
1125}
1126
1127static void
1128ucom_free(void *xsc)
1129{
1130	struct ucom_softc *sc = xsc;
1131
1132	mtx_lock(sc->sc_mtx);
1133	sc->sc_ttyfreed = 1;
1134	cv_signal(&sc->sc_cv);
1135	mtx_unlock(sc->sc_mtx);
1136}
1137