usb_serial.c revision 187176
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 187176 2009-01-13 19:03:47Z thompsa $");
32
33/*-
34 * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
35 * All rights reserved.
36 *
37 * This code is derived from software contributed to The NetBSD Foundation
38 * by Lennart Augustsson (lennart@augustsson.net) at
39 * Carlstedt Research & Technology.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 *    notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 *    notice, this list of conditions and the following disclaimer in the
48 *    documentation and/or other materials provided with the distribution.
49 * 3. All advertising materials mentioning features or use of this software
50 *    must display the following acknowledgement:
51 *        This product includes software developed by the NetBSD
52 *        Foundation, Inc. and its contributors.
53 * 4. Neither the name of The NetBSD Foundation nor the names of its
54 *    contributors may be used to endorse or promote products derived
55 *    from this software without specific prior written permission.
56 *
57 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
58 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
59 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
60 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
61 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
62 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
63 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
64 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
66 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
67 * POSSIBILITY OF SUCH DAMAGE.
68 */
69
70/*
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
82#include <dev/usb2/core/usb2_core.h>
83#include <dev/usb2/core/usb2_debug.h>
84#include <dev/usb2/core/usb2_process.h>
85#include <dev/usb2/core/usb2_request.h>
86#include <dev/usb2/core/usb2_busdma.h>
87#include <dev/usb2/core/usb2_util.h>
88
89#include <dev/usb2/serial/usb2_serial.h>
90
91#if USB_DEBUG
92static int usb2_com_debug = 0;
93
94SYSCTL_NODE(_hw_usb2, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
95SYSCTL_INT(_hw_usb2_ucom, OID_AUTO, debug, CTLFLAG_RW,
96    &usb2_com_debug, 0, "ucom debug level");
97#endif
98
99static usb2_proc_callback_t usb2_com_cfg_start_transfers;
100static usb2_proc_callback_t usb2_com_cfg_open;
101static usb2_proc_callback_t usb2_com_cfg_close;
102static usb2_proc_callback_t usb2_com_cfg_break_on;
103static usb2_proc_callback_t usb2_com_cfg_break_off;
104static usb2_proc_callback_t usb2_com_cfg_dtr_on;
105static usb2_proc_callback_t usb2_com_cfg_dtr_off;
106static usb2_proc_callback_t usb2_com_cfg_rts_on;
107static usb2_proc_callback_t usb2_com_cfg_rts_off;
108static usb2_proc_callback_t usb2_com_cfg_status_change;
109static usb2_proc_callback_t usb2_com_cfg_param;
110
111static uint8_t	usb2_com_units_alloc(uint32_t, uint32_t *);
112static void	usb2_com_units_free(uint32_t, uint32_t);
113static int	usb2_com_attach_sub(struct usb2_com_softc *);
114static void	usb2_com_detach_sub(struct usb2_com_softc *);
115static void	usb2_com_queue_command(struct usb2_com_softc *sc,
116		    uint8_t cmd);
117static void	usb2_com_wait_command(struct usb2_com_softc *sc,
118		    uint8_t cmd);
119static void	usb2_com_shutdown(struct usb2_com_softc *);
120static void	usb2_com_start_transfers(struct usb2_com_softc *);
121static void	usb2_com_break(struct usb2_com_softc *, uint8_t);
122static void	usb2_com_dtr(struct usb2_com_softc *, uint8_t);
123static void	usb2_com_rts(struct usb2_com_softc *, uint8_t);
124
125static tsw_open_t usb2_com_open;
126static tsw_close_t usb2_com_close;
127static tsw_ioctl_t usb2_com_ioctl;
128static tsw_modem_t usb2_com_modem;
129static tsw_param_t usb2_com_param;
130static tsw_outwakeup_t usb2_com_start_write;
131static tsw_free_t usb2_com_free;
132
133static struct ttydevsw usb2_com_class = {
134	.tsw_flags = TF_INITLOCK | TF_CALLOUT,
135	.tsw_open = usb2_com_open,
136	.tsw_close = usb2_com_close,
137	.tsw_outwakeup = usb2_com_start_write,
138	.tsw_ioctl = usb2_com_ioctl,
139	.tsw_param = usb2_com_param,
140	.tsw_modem = usb2_com_modem,
141	.tsw_free = usb2_com_free,
142};
143
144MODULE_DEPEND(usb2_serial, usb2_core, 1, 1, 1);
145MODULE_VERSION(usb2_serial, 1);
146
147#define	UCOM_UNIT_MAX 0x1000		/* exclusive */
148#define	UCOM_SUB_UNIT_MAX 0x100		/* exclusive */
149
150static uint8_t usb2_com_bitmap[(UCOM_UNIT_MAX + 7) / 8];
151
152static uint8_t
153usb2_com_units_alloc(uint32_t sub_units, uint32_t *p_root_unit)
154{
155	uint32_t n;
156	uint32_t o;
157	uint32_t x;
158	uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units);
159	uint8_t error = 1;
160
161	mtx_lock(&Giant);
162
163	for (n = 0; n < max; n += sub_units) {
164
165		/* check for free consecutive bits */
166
167		for (o = 0; o < sub_units; o++) {
168
169			x = n + o;
170
171			if (usb2_com_bitmap[x / 8] & (1 << (x % 8))) {
172				goto skip;
173			}
174		}
175
176		/* allocate */
177
178		for (o = 0; o < sub_units; o++) {
179
180			x = n + o;
181
182			usb2_com_bitmap[x / 8] |= (1 << (x % 8));
183		}
184
185		error = 0;
186
187		break;
188
189skip:		;
190	}
191
192	mtx_unlock(&Giant);
193
194	/*
195	 * Always set the variable pointed to by "p_root_unit" so that
196	 * the compiler does not think that it is used uninitialised:
197	 */
198	*p_root_unit = n;
199
200	return (error);
201}
202
203static void
204usb2_com_units_free(uint32_t root_unit, uint32_t sub_units)
205{
206	uint32_t x;
207
208	mtx_lock(&Giant);
209
210	while (sub_units--) {
211		x = root_unit + sub_units;
212		usb2_com_bitmap[x / 8] &= ~(1 << (x % 8));
213	}
214
215	mtx_unlock(&Giant);
216}
217
218/*
219 * "N" sub_units are setup at a time. All sub-units will
220 * be given sequential unit numbers. The number of
221 * sub-units can be used to differentiate among
222 * different types of devices.
223 *
224 * The mutex pointed to by "p_mtx" is applied before all
225 * callbacks are called back. Also "p_mtx" must be applied
226 * before calling into the ucom-layer! Currently only Giant
227 * is supported.
228 */
229int
230usb2_com_attach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc,
231    uint32_t sub_units, void *parent,
232    const struct usb2_com_callback *callback, struct mtx *p_mtx)
233{
234	uint32_t n;
235	uint32_t root_unit;
236	int error = 0;
237
238	if ((sc == NULL) ||
239	    (sub_units == 0) ||
240	    (sub_units > UCOM_SUB_UNIT_MAX) ||
241	    (callback == NULL)) {
242		return (EINVAL);
243	}
244	if (usb2_com_units_alloc(sub_units, &root_unit)) {
245		return (ENOMEM);
246	}
247	if (usb2_proc_setup(&ssc->sc_config_td, p_mtx, USB_PRI_MED)) {
248		usb2_com_units_free(root_unit, sub_units);
249		return (ENOMEM);
250	}
251	for (n = 0; n < sub_units; n++, sc++) {
252		sc->sc_unit = root_unit + n;
253		sc->sc_local_unit = n;
254		sc->sc_super = ssc;
255		sc->sc_parent_mtx = p_mtx;
256		sc->sc_parent = parent;
257		sc->sc_callback = callback;
258
259		error = usb2_com_attach_sub(sc);
260		if (error) {
261			usb2_com_detach(ssc, sc - n, n);
262			usb2_com_units_free(root_unit + n, sub_units - n);
263			break;
264		}
265		sc->sc_flag |= UCOM_FLAG_ATTACHED;
266	}
267	return (error);
268}
269
270/* NOTE: the following function will do nothing if
271 * the structure pointed to by "ssc" and "sc" is zero.
272 */
273void
274usb2_com_detach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc,
275    uint32_t sub_units)
276{
277	uint32_t n;
278
279	usb2_proc_drain(&ssc->sc_config_td);
280
281	for (n = 0; n < sub_units; n++, sc++) {
282		if (sc->sc_flag & UCOM_FLAG_ATTACHED) {
283
284			usb2_com_detach_sub(sc);
285
286			usb2_com_units_free(sc->sc_unit, 1);
287
288			/* avoid duplicate detach: */
289			sc->sc_flag &= ~UCOM_FLAG_ATTACHED;
290		}
291	}
292	usb2_proc_unsetup(&ssc->sc_config_td);
293}
294
295static int
296usb2_com_attach_sub(struct usb2_com_softc *sc)
297{
298	struct tty *tp;
299	int error = 0;
300	uint8_t n;
301	char buf[32];			/* temporary TTY device name buffer */
302
303	tp = tty_alloc(&usb2_com_class, sc, sc->sc_parent_mtx);
304	if (tp == NULL) {
305		error = ENOMEM;
306		goto done;
307	}
308	DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit);
309
310	buf[0] = 0;			/* set some default value */
311
312	/* Check if the client has a custom TTY name */
313	if (sc->sc_callback->usb2_com_tty_name) {
314		sc->sc_callback->usb2_com_tty_name(sc, buf,
315		    sizeof(buf), sc->sc_local_unit);
316	}
317	if (buf[0] == 0) {
318		/* Use default TTY name */
319		if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) {
320			/* ignore */
321		}
322	}
323	tty_makedev(tp, NULL, "%s", buf);
324
325	sc->sc_tty = tp;
326
327	DPRINTF("ttycreate: %s\n", buf);
328	usb2_cv_init(&sc->sc_cv, "usb2_com");
329
330	/*
331	 * Set all function callback pointers for deferred COM
332	 * operations:
333	 */
334	for (n = 0; n != 2; n++) {
335		sc->sc_cmds[(2*USB_COM_CFG_START_TRANSFERS) + n].hdr.pm_callback =
336		    &usb2_com_cfg_start_transfers;
337		sc->sc_cmds[(2*USB_COM_CFG_OPEN) + n].hdr.pm_callback =
338		    &usb2_com_cfg_open;
339		sc->sc_cmds[(2*USB_COM_CFG_CLOSE) + n].hdr.pm_callback =
340		    &usb2_com_cfg_close;
341		sc->sc_cmds[(2*USB_COM_CFG_BREAK_ON) + n].hdr.pm_callback =
342		    &usb2_com_cfg_break_on;
343		sc->sc_cmds[(2*USB_COM_CFG_BREAK_OFF) + n].hdr.pm_callback =
344		    &usb2_com_cfg_break_off;
345		sc->sc_cmds[(2*USB_COM_CFG_DTR_ON) + n].hdr.pm_callback =
346		    &usb2_com_cfg_dtr_on;
347		sc->sc_cmds[(2*USB_COM_CFG_DTR_OFF) + n].hdr.pm_callback =
348		    &usb2_com_cfg_dtr_off;
349		sc->sc_cmds[(2*USB_COM_CFG_RTS_ON) + n].hdr.pm_callback =
350		    &usb2_com_cfg_rts_on;
351		sc->sc_cmds[(2*USB_COM_CFG_RTS_OFF) + n].hdr.pm_callback =
352		    &usb2_com_cfg_rts_off;
353		sc->sc_cmds[(2*USB_COM_CFG_STATUS_CHANGE) + n].hdr.pm_callback =
354		    &usb2_com_cfg_status_change;
355		sc->sc_cmds[(2*USB_COM_CFG_PARAM) + n].hdr.pm_callback =
356		    &usb2_com_cfg_param;
357	}
358
359	/* initialise all callback pointer arguments */
360	for (n = 0; n != (2*USB_COM_CFG_MAX); n++) {
361		sc->sc_cmds[n].cc_softc = sc;
362	}
363done:
364	return (error);
365}
366
367static void
368usb2_com_detach_sub(struct usb2_com_softc *sc)
369{
370	struct tty *tp = sc->sc_tty;
371
372	DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
373
374	/* the config thread has been stopped when we get here */
375
376	mtx_lock(sc->sc_parent_mtx);
377	sc->sc_flag |= UCOM_FLAG_GONE;
378	sc->sc_flag &= ~(UCOM_FLAG_HL_READY |
379	    UCOM_FLAG_LL_READY);
380	mtx_unlock(sc->sc_parent_mtx);
381	if (tp) {
382		tty_lock(tp);
383
384		usb2_com_close(tp);	/* close, if any */
385
386		tty_rel_gone(tp);
387
388		mtx_lock(sc->sc_parent_mtx);
389		/* Wait for the callback after the TTY is torn down */
390		while (sc->sc_ttyfreed == 0)
391			usb2_cv_wait(&sc->sc_cv, sc->sc_parent_mtx);
392		/*
393		 * make sure that read and write transfers are stopped
394		 */
395		if (sc->sc_callback->usb2_com_stop_read) {
396			(sc->sc_callback->usb2_com_stop_read) (sc);
397		}
398		if (sc->sc_callback->usb2_com_stop_write) {
399			(sc->sc_callback->usb2_com_stop_write) (sc);
400		}
401		mtx_unlock(sc->sc_parent_mtx);
402	}
403	usb2_cv_destroy(&sc->sc_cv);
404}
405
406/*
407 * The following function queues a command for deferred execution.
408 * The following function must be called locked.
409 */
410static void
411usb2_com_queue_command(struct usb2_com_softc *sc, uint8_t cmd)
412{
413	struct usb2_com_super_softc *ssc = sc->sc_super;
414
415	if (usb2_proc_is_gone(&ssc->sc_config_td)) {
416		DPRINTF("proc is gone\n");
417		return;		/* nothing to do */
418	}
419
420	if (usb2_proc_msignal(&ssc->sc_config_td,
421	    &sc->sc_cmds[2*cmd], &sc->sc_cmds[(2*cmd)+1])) {
422		/* ignore */
423	}
424}
425
426/*
427 * The following function waits until a command has been executed.
428 * The following function must be called locked.
429 */
430static void
431usb2_com_wait_command(struct usb2_com_softc *sc, uint8_t cmd)
432{
433	struct usb2_com_super_softc *ssc = sc->sc_super;
434
435	if (usb2_proc_is_gone(&ssc->sc_config_td)) {
436		DPRINTF("proc is gone\n");
437		return;		/* nothing to do */
438	}
439	usb2_proc_mwait(&ssc->sc_config_td,
440	    &sc->sc_cmds[2*cmd], &sc->sc_cmds[(2*cmd)+1]);
441}
442
443static void
444usb2_com_shutdown(struct usb2_com_softc *sc)
445{
446	struct tty *tp = sc->sc_tty;
447
448	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
449
450	DPRINTF("\n");
451
452	/*
453	 * Hang up if necessary:
454	 */
455	if (tp->t_termios.c_cflag & HUPCL) {
456		usb2_com_modem(tp, 0, SER_DTR);
457	}
458}
459
460/*
461 * This function will sleep "timeout" system ticks.
462 *
463 * Return values:
464 *    0: normal delay
465 * else: config thread is gone
466 */
467uint8_t
468usb2_com_cfg_sleep(struct usb2_com_softc *sc, uint32_t timeout)
469{
470	struct usb2_com_super_softc *ssc = sc->sc_super;
471	uint8_t is_gone;
472
473	is_gone = usb2_proc_is_gone(&ssc->sc_config_td);
474	if (is_gone)
475		goto done;	/* we are detaching */
476	if (timeout == 0)
477		timeout = 1;	/* one tick is the least timeout */
478
479	mtx_unlock(sc->sc_parent_mtx);
480
481	if (pause("UCOMWAIT", timeout)) {
482		/* ignore */
483	}
484
485	mtx_lock(sc->sc_parent_mtx);
486
487	/* refresh gone status */
488	is_gone = usb2_proc_is_gone(&ssc->sc_config_td);
489done:
490	return (is_gone);
491}
492
493/*
494 * Return values:
495 *    0: normal
496 * else: config thread is gone
497 */
498uint8_t
499usb2_com_cfg_is_gone(struct usb2_com_softc *sc)
500{
501	struct usb2_com_super_softc *ssc = sc->sc_super;
502
503	return (usb2_proc_is_gone(&ssc->sc_config_td));
504}
505
506static void
507usb2_com_cfg_start_transfers(struct usb2_proc_msg *_cc)
508{
509	struct usb2_com_command_msg *cc = (void *)_cc;
510	struct usb2_com_softc *sc;
511
512	sc = cc->cc_softc;
513
514	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
515		return;
516	}
517	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
518		/* TTY device closed */
519		return;
520	}
521	sc->sc_flag |= UCOM_FLAG_GP_DATA;
522
523	if (sc->sc_callback->usb2_com_start_read) {
524		(sc->sc_callback->usb2_com_start_read) (sc);
525	}
526	if (sc->sc_callback->usb2_com_start_write) {
527		(sc->sc_callback->usb2_com_start_write) (sc);
528	}
529}
530
531static void
532usb2_com_start_transfers(struct usb2_com_softc *sc)
533{
534	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
535		return;
536	}
537	/*
538	 * do a direct call first, to get hardware buffers flushed
539	 */
540
541	if (sc->sc_callback->usb2_com_start_read) {
542		(sc->sc_callback->usb2_com_start_read) (sc);
543	}
544	if (sc->sc_callback->usb2_com_start_write) {
545		(sc->sc_callback->usb2_com_start_write) (sc);
546	}
547	if (!(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
548		usb2_com_queue_command(sc, USB_COM_CFG_START_TRANSFERS);
549	}
550}
551
552static void
553usb2_com_cfg_open(struct usb2_proc_msg *_cc)
554{
555	struct usb2_com_command_msg *cc = (void *)_cc;
556	struct usb2_com_softc *sc;
557
558	sc = cc->cc_softc;
559
560	DPRINTF("\n");
561
562	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
563
564		/* already opened */
565
566	} else {
567
568		sc->sc_flag |= UCOM_FLAG_LL_READY;
569
570		if (sc->sc_callback->usb2_com_cfg_open) {
571			(sc->sc_callback->usb2_com_cfg_open) (sc);
572
573			/* wait a little */
574			usb2_com_cfg_sleep(sc, hz / 10);
575		}
576	}
577}
578
579static int
580usb2_com_open(struct tty *tp)
581{
582	struct usb2_com_softc *sc = tty_softc(tp);
583	int error;
584
585	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
586
587	if (sc->sc_flag & UCOM_FLAG_GONE) {
588		return (ENXIO);
589	}
590	if (sc->sc_flag & UCOM_FLAG_HL_READY) {
591		/* already opened */
592		return (0);
593	}
594	DPRINTF("tp = %p\n", tp);
595
596	if (sc->sc_callback->usb2_com_pre_open) {
597		/*
598		 * give the lower layer a chance to disallow TTY open, for
599		 * example if the device is not present:
600		 */
601		error = (sc->sc_callback->usb2_com_pre_open) (sc);
602		if (error) {
603			return (error);
604		}
605	}
606	sc->sc_flag |= UCOM_FLAG_HL_READY;
607
608	/* Disable transfers */
609	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
610
611	sc->sc_lsr = 0;
612	sc->sc_msr = 0;
613	sc->sc_mcr = 0;
614
615	usb2_com_queue_command(sc, USB_COM_CFG_OPEN);
616
617	usb2_com_start_transfers(sc);
618
619	usb2_com_modem(tp, SER_DTR | SER_RTS, 0);
620
621	usb2_com_break(sc, 0);
622
623	usb2_com_status_change(sc);
624
625	return (0);
626}
627
628static void
629usb2_com_cfg_close(struct usb2_proc_msg *_cc)
630{
631	struct usb2_com_command_msg *cc = (void *)_cc;
632	struct usb2_com_softc *sc;
633
634	sc = cc->cc_softc;
635
636	DPRINTF("\n");
637
638	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
639
640		sc->sc_flag &= ~(UCOM_FLAG_LL_READY |
641		    UCOM_FLAG_GP_DATA);
642
643		if (sc->sc_callback->usb2_com_cfg_close) {
644			(sc->sc_callback->usb2_com_cfg_close) (sc);
645		}
646	} else {
647		/* already closed */
648	}
649}
650
651static void
652usb2_com_close(struct tty *tp)
653{
654	struct usb2_com_softc *sc = tty_softc(tp);
655
656	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
657
658	DPRINTF("tp=%p\n", tp);
659
660	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
661		DPRINTF("tp=%p already closed\n", tp);
662		return;
663	}
664	usb2_com_shutdown(sc);
665
666	/* Queue and wait for close command to complete */
667	usb2_com_queue_command(sc, USB_COM_CFG_CLOSE);
668	usb2_com_wait_command(sc, USB_COM_CFG_CLOSE);
669
670	sc->sc_flag &= ~(UCOM_FLAG_HL_READY |
671	    UCOM_FLAG_WR_START |
672	    UCOM_FLAG_RTS_IFLOW);
673
674	if (sc->sc_callback->usb2_com_stop_read) {
675		(sc->sc_callback->usb2_com_stop_read) (sc);
676	}
677	if (sc->sc_callback->usb2_com_stop_write) {
678		(sc->sc_callback->usb2_com_stop_write) (sc);
679	}
680}
681
682static int
683usb2_com_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
684{
685	struct usb2_com_softc *sc = tty_softc(tp);
686	int error;
687
688	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
689
690	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
691		return (EIO);
692	}
693	DPRINTF("cmd = 0x%08lx\n", cmd);
694
695	switch (cmd) {
696	case TIOCSBRK:
697		usb2_com_break(sc, 1);
698		error = 0;
699		break;
700	case TIOCCBRK:
701		usb2_com_break(sc, 0);
702		error = 0;
703		break;
704	default:
705		if (sc->sc_callback->usb2_com_ioctl) {
706			error = (sc->sc_callback->usb2_com_ioctl)
707			    (sc, cmd, data, 0, td);
708		} else {
709			error = ENOIOCTL;
710		}
711		break;
712	}
713	return (error);
714}
715
716static int
717usb2_com_modem(struct tty *tp, int sigon, int sigoff)
718{
719	struct usb2_com_softc *sc = tty_softc(tp);
720	uint8_t onoff;
721
722	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
723
724	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
725		return (0);
726	}
727	if ((sigon == 0) && (sigoff == 0)) {
728
729		if (sc->sc_mcr & SER_DTR) {
730			sigon |= SER_DTR;
731		}
732		if (sc->sc_mcr & SER_RTS) {
733			sigon |= SER_RTS;
734		}
735		if (sc->sc_msr & SER_CTS) {
736			sigon |= SER_CTS;
737		}
738		if (sc->sc_msr & SER_DCD) {
739			sigon |= SER_DCD;
740		}
741		if (sc->sc_msr & SER_DSR) {
742			sigon |= SER_DSR;
743		}
744		if (sc->sc_msr & SER_RI) {
745			sigon |= SER_RI;
746		}
747		return (sigon);
748	}
749	if (sigon & SER_DTR) {
750		sc->sc_mcr |= SER_DTR;
751	}
752	if (sigoff & SER_DTR) {
753		sc->sc_mcr &= ~SER_DTR;
754	}
755	if (sigon & SER_RTS) {
756		sc->sc_mcr |= SER_RTS;
757	}
758	if (sigoff & SER_RTS) {
759		sc->sc_mcr &= ~SER_RTS;
760	}
761	onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
762	usb2_com_dtr(sc, onoff);
763
764	onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
765	usb2_com_rts(sc, onoff);
766
767	return (0);
768}
769
770static void
771usb2_com_cfg_break(struct usb2_com_command_msg *cc, uint8_t onoff)
772{
773	struct usb2_com_softc *sc;
774
775	sc = cc->cc_softc;
776
777	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
778		return;
779	}
780	DPRINTF("onoff=%d\n", onoff);
781
782	if (sc->sc_callback->usb2_com_cfg_set_break) {
783		(sc->sc_callback->usb2_com_cfg_set_break) (sc, onoff);
784	}
785}
786
787static void
788usb2_com_cfg_break_on(struct usb2_proc_msg *_cc)
789{
790	usb2_com_cfg_break((void *)_cc, 1);
791}
792
793static void
794usb2_com_cfg_break_off(struct usb2_proc_msg *_cc)
795{
796	usb2_com_cfg_break((void *)_cc, 0);
797}
798
799static void
800usb2_com_break(struct usb2_com_softc *sc, uint8_t onoff)
801{
802	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
803
804	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
805		return;
806	}
807	DPRINTF("onoff = %d\n", onoff);
808
809	usb2_com_queue_command(sc, onoff ?
810	    USB_COM_CFG_BREAK_ON : USB_COM_CFG_BREAK_OFF);
811}
812
813static void
814usb2_com_cfg_dtr(struct usb2_com_command_msg *cc, uint8_t onoff)
815{
816	struct usb2_com_softc *sc;
817
818	sc = cc->cc_softc;
819
820	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
821		return;
822	}
823	DPRINTF("onoff=%d\n", onoff);
824
825	if (sc->sc_callback->usb2_com_cfg_set_dtr) {
826		(sc->sc_callback->usb2_com_cfg_set_dtr) (sc, onoff);
827	}
828}
829
830static void
831usb2_com_cfg_dtr_on(struct usb2_proc_msg *_cc)
832{
833	usb2_com_cfg_dtr((void *)_cc, 1);
834}
835
836static void
837usb2_com_cfg_dtr_off(struct usb2_proc_msg *_cc)
838{
839	usb2_com_cfg_dtr((void *)_cc, 0);
840}
841
842static void
843usb2_com_dtr(struct usb2_com_softc *sc, uint8_t onoff)
844{
845	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
846
847	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
848		return;
849	}
850	DPRINTF("onoff = %d\n", onoff);
851
852	usb2_com_queue_command(sc, onoff ?
853	    USB_COM_CFG_DTR_ON : USB_COM_CFG_DTR_OFF);
854}
855
856static void
857usb2_com_cfg_rts(struct usb2_com_command_msg *cc, uint8_t onoff)
858{
859	struct usb2_com_softc *sc;
860
861	sc = cc->cc_softc;
862
863	DPRINTF("onoff=%d\n", onoff);
864
865	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
866		return;
867	}
868	if (sc->sc_callback->usb2_com_cfg_set_rts) {
869		(sc->sc_callback->usb2_com_cfg_set_rts) (sc, onoff);
870	}
871}
872
873static void
874usb2_com_cfg_rts_on(struct usb2_proc_msg *_cc)
875{
876	usb2_com_cfg_rts((void *)_cc, 1);
877}
878
879static void
880usb2_com_cfg_rts_off(struct usb2_proc_msg *_cc)
881{
882	usb2_com_cfg_rts((void *)_cc, 0);
883}
884
885static void
886usb2_com_rts(struct usb2_com_softc *sc, uint8_t onoff)
887{
888	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
889
890	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
891		return;
892	}
893	DPRINTF("onoff = %d\n", onoff);
894
895	usb2_com_queue_command(sc, onoff ?
896	    USB_COM_CFG_RTS_ON : USB_COM_CFG_RTS_OFF);
897}
898
899static void
900usb2_com_cfg_status_change(struct usb2_proc_msg *_cc)
901{
902	struct usb2_com_command_msg *cc = (void *)_cc;
903	struct usb2_com_softc *sc;
904	struct tty *tp;
905
906	uint8_t new_msr;
907	uint8_t new_lsr;
908	uint8_t onoff;
909
910	sc = cc->cc_softc;
911	tp = sc->sc_tty;
912
913	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
914
915	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
916		return;
917	}
918	if (sc->sc_callback->usb2_com_cfg_get_status == NULL) {
919		return;
920	}
921	/* get status */
922
923	new_msr = 0;
924	new_lsr = 0;
925
926	(sc->sc_callback->usb2_com_cfg_get_status) (sc, &new_lsr, &new_msr);
927
928	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
929		/* TTY device closed */
930		return;
931	}
932	onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
933
934	sc->sc_msr = new_msr;
935	sc->sc_lsr = new_lsr;
936
937	if (onoff) {
938
939		onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
940
941		DPRINTF("DCD changed to %d\n", onoff);
942
943		ttydisc_modem(tp, onoff);
944	}
945}
946
947void
948usb2_com_status_change(struct usb2_com_softc *sc)
949{
950	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
951
952	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
953		return;
954	}
955	DPRINTF("\n");
956
957	usb2_com_queue_command(sc, USB_COM_CFG_STATUS_CHANGE);
958}
959
960static void
961usb2_com_cfg_param(struct usb2_proc_msg *_cc)
962{
963	struct usb2_com_command_msg *cc = (void *)_cc;
964	struct usb2_com_softc *sc;
965	struct termios t_copy;
966
967	sc = cc->cc_softc;
968
969	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
970		return;
971	}
972	if (sc->sc_callback->usb2_com_cfg_param == NULL) {
973		return;
974	}
975	t_copy = sc->sc_termios_copy;
976
977	(sc->sc_callback->usb2_com_cfg_param) (sc, &t_copy);
978
979	/* wait a little */
980	usb2_com_cfg_sleep(sc, hz / 10);
981}
982
983static int
984usb2_com_param(struct tty *tp, struct termios *t)
985{
986	struct usb2_com_softc *sc = tty_softc(tp);
987	uint8_t opened;
988	int error;
989
990	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
991
992	opened = 0;
993	error = 0;
994
995	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
996
997		/* XXX the TTY layer should call "open()" first! */
998
999		error = usb2_com_open(tp);
1000		if (error) {
1001			goto done;
1002		}
1003		opened = 1;
1004	}
1005	DPRINTF("sc = %p\n", sc);
1006
1007	/* Check requested parameters. */
1008	if (t->c_ospeed < 0) {
1009		DPRINTF("negative ospeed\n");
1010		error = EINVAL;
1011		goto done;
1012	}
1013	if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
1014		DPRINTF("mismatch ispeed and ospeed\n");
1015		error = EINVAL;
1016		goto done;
1017	}
1018	t->c_ispeed = t->c_ospeed;
1019
1020	if (sc->sc_callback->usb2_com_pre_param) {
1021		/* Let the lower layer verify the parameters */
1022		error = (sc->sc_callback->usb2_com_pre_param) (sc, t);
1023		if (error) {
1024			DPRINTF("callback error = %d\n", error);
1025			goto done;
1026		}
1027	}
1028	/* Make a copy of the termios parameters */
1029	sc->sc_termios_copy = *t;
1030
1031	/* Disable transfers */
1032	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
1033
1034	/* Queue baud rate programming command first */
1035	usb2_com_queue_command(sc, USB_COM_CFG_PARAM);
1036
1037	/* Queue transfer enable command last */
1038	usb2_com_start_transfers(sc);
1039
1040	if (t->c_cflag & CRTS_IFLOW) {
1041		sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
1042	} else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
1043		sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
1044		usb2_com_modem(tp, SER_RTS, 0);
1045	}
1046done:
1047	if (error) {
1048		if (opened) {
1049			usb2_com_close(tp);
1050		}
1051	}
1052	return (error);
1053}
1054
1055static void
1056usb2_com_start_write(struct tty *tp)
1057{
1058	struct usb2_com_softc *sc = tty_softc(tp);
1059
1060	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
1061
1062	DPRINTF("sc = %p\n", sc);
1063
1064	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1065		/* The higher layer is not ready */
1066		return;
1067	}
1068	sc->sc_flag |= UCOM_FLAG_WR_START;
1069
1070	usb2_com_start_transfers(sc);
1071}
1072
1073/*------------------------------------------------------------------------*
1074 *	usb2_com_get_data
1075 *
1076 * Return values:
1077 * 0: No data is available.
1078 * Else: Data is available.
1079 *------------------------------------------------------------------------*/
1080uint8_t
1081usb2_com_get_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
1082    uint32_t offset, uint32_t len, uint32_t *actlen)
1083{
1084	struct usb2_page_search res;
1085	struct tty *tp = sc->sc_tty;
1086	uint32_t cnt;
1087	uint32_t offset_orig;
1088
1089	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
1090
1091	if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) ||
1092	    (!(sc->sc_flag & UCOM_FLAG_GP_DATA)) ||
1093	    (!(sc->sc_flag & UCOM_FLAG_WR_START))) {
1094		actlen[0] = 0;
1095		return (0);		/* multiport device polling */
1096	}
1097	offset_orig = offset;
1098
1099	while (len != 0) {
1100
1101		usb2_get_page(pc, offset, &res);
1102
1103		if (res.length > len) {
1104			res.length = len;
1105		}
1106		/* copy data directly into USB buffer */
1107		cnt = ttydisc_getc(tp, res.buffer, res.length);
1108
1109		offset += cnt;
1110		len -= cnt;
1111
1112		if (cnt < res.length) {
1113			/* end of buffer */
1114			break;
1115		}
1116	}
1117
1118	actlen[0] = offset - offset_orig;
1119
1120	DPRINTF("cnt=%d\n", actlen[0]);
1121
1122	if (actlen[0] == 0) {
1123		return (0);
1124	}
1125	return (1);
1126}
1127
1128void
1129usb2_com_put_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
1130    uint32_t offset, uint32_t len)
1131{
1132	struct usb2_page_search res;
1133	struct tty *tp = sc->sc_tty;
1134	char *buf;
1135	uint32_t cnt;
1136
1137	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
1138
1139	if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) ||
1140	    (!(sc->sc_flag & UCOM_FLAG_GP_DATA))) {
1141		return;			/* multiport device polling */
1142	}
1143	if (len == 0)
1144		return;			/* no data */
1145
1146	/* set a flag to prevent recursation ? */
1147
1148	while (len > 0) {
1149
1150		usb2_get_page(pc, offset, &res);
1151
1152		if (res.length > len) {
1153			res.length = len;
1154		}
1155		len -= res.length;
1156		offset += res.length;
1157
1158		/* pass characters to tty layer */
1159
1160		buf = res.buffer;
1161		cnt = res.length;
1162
1163		/* first check if we can pass the buffer directly */
1164
1165		if (ttydisc_can_bypass(tp)) {
1166			if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1167				DPRINTF("tp=%p, data lost\n", tp);
1168			}
1169			continue;
1170		}
1171		/* need to loop */
1172
1173		for (cnt = 0; cnt != res.length; cnt++) {
1174			if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
1175				/* XXX what should we do? */
1176
1177				DPRINTF("tp=%p, lost %d "
1178				    "chars\n", tp, res.length - cnt);
1179				break;
1180			}
1181		}
1182	}
1183	ttydisc_rint_done(tp);
1184}
1185
1186static void
1187usb2_com_free(void *xsc)
1188{
1189	struct usb2_com_softc *sc = xsc;
1190
1191	mtx_lock(sc->sc_parent_mtx);
1192	sc->sc_ttyfreed = 1;
1193	usb2_cv_signal(&sc->sc_cv);
1194	mtx_unlock(sc->sc_parent_mtx);
1195}
1196