usb_serial.c revision 185948
1184610Salfred/*	$NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $	*/
2184610Salfred
3184610Salfred/*-
4184610Salfred * Copyright (c) 2001-2003, 2005, 2008
5184610Salfred *	Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
6184610Salfred * All rights reserved.
7184610Salfred *
8184610Salfred * Redistribution and use in source and binary forms, with or without
9184610Salfred * modification, are permitted provided that the following conditions
10184610Salfred * are met:
11184610Salfred * 1. Redistributions of source code must retain the above copyright
12184610Salfred *    notice, this list of conditions and the following disclaimer.
13184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
14184610Salfred *    notice, this list of conditions and the following disclaimer in the
15184610Salfred *    documentation and/or other materials provided with the distribution.
16184610Salfred *
17184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27184610Salfred * SUCH DAMAGE.
28184610Salfred */
29184610Salfred
30184610Salfred#include <sys/cdefs.h>
31184610Salfred__FBSDID("$FreeBSD: head/sys/dev/usb2/serial/usb2_serial.c 185948 2008-12-11 23:13:02Z thompsa $");
32184610Salfred
33184610Salfred/*-
34184610Salfred * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
35184610Salfred * All rights reserved.
36184610Salfred *
37184610Salfred * This code is derived from software contributed to The NetBSD Foundation
38184610Salfred * by Lennart Augustsson (lennart@augustsson.net) at
39184610Salfred * Carlstedt Research & Technology.
40184610Salfred *
41184610Salfred * Redistribution and use in source and binary forms, with or without
42184610Salfred * modification, are permitted provided that the following conditions
43184610Salfred * are met:
44184610Salfred * 1. Redistributions of source code must retain the above copyright
45184610Salfred *    notice, this list of conditions and the following disclaimer.
46184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
47184610Salfred *    notice, this list of conditions and the following disclaimer in the
48184610Salfred *    documentation and/or other materials provided with the distribution.
49184610Salfred * 3. All advertising materials mentioning features or use of this software
50184610Salfred *    must display the following acknowledgement:
51184610Salfred *        This product includes software developed by the NetBSD
52184610Salfred *        Foundation, Inc. and its contributors.
53184610Salfred * 4. Neither the name of The NetBSD Foundation nor the names of its
54184610Salfred *    contributors may be used to endorse or promote products derived
55184610Salfred *    from this software without specific prior written permission.
56184610Salfred *
57184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
58184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
59184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
60184610Salfred * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
61184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
62184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
63184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
64184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
66184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
67184610Salfred * POSSIBILITY OF SUCH DAMAGE.
68184610Salfred */
69184610Salfred
70184610Salfred/*
71184610Salfred * NOTE: all function names beginning like "usb2_com_cfg_" can only
72184610Salfred * be called from within the config thread function !
73184610Salfred */
74184610Salfred
75184610Salfred#include <dev/usb2/include/usb2_standard.h>
76184610Salfred#include <dev/usb2/include/usb2_mfunc.h>
77184610Salfred#include <dev/usb2/include/usb2_error.h>
78184610Salfred#include <dev/usb2/include/usb2_cdc.h>
79184610Salfred
80184610Salfred#define	USB_DEBUG_VAR usb2_com_debug
81184610Salfred#define	usb2_config_td_cc usb2_com_config_copy
82184610Salfred#define	usb2_config_td_softc usb2_com_softc
83184610Salfred
84184610Salfred#include <dev/usb2/core/usb2_core.h>
85184610Salfred#include <dev/usb2/core/usb2_debug.h>
86184610Salfred#include <dev/usb2/core/usb2_process.h>
87184610Salfred#include <dev/usb2/core/usb2_config_td.h>
88184610Salfred#include <dev/usb2/core/usb2_request.h>
89184610Salfred#include <dev/usb2/core/usb2_busdma.h>
90184610Salfred#include <dev/usb2/core/usb2_util.h>
91184610Salfred
92184610Salfred#include <dev/usb2/serial/usb2_serial.h>
93184610Salfred
94184610Salfred#if USB_DEBUG
95184610Salfredstatic int usb2_com_debug = 0;
96184610Salfred
97184610SalfredSYSCTL_NODE(_hw_usb2, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
98184610SalfredSYSCTL_INT(_hw_usb2_ucom, OID_AUTO, debug, CTLFLAG_RW,
99184610Salfred    &usb2_com_debug, 0, "ucom debug level");
100184610Salfred#endif
101184610Salfred
102184610Salfredstruct usb2_com_config_copy {
103184610Salfred	struct usb2_com_softc *cc_softc;
104184610Salfred	uint8_t	cc_flag0;
105184610Salfred	uint8_t	cc_flag1;
106184610Salfred	uint8_t	cc_flag2;
107184610Salfred	uint8_t	cc_flag3;
108184610Salfred};
109184610Salfred
110184610Salfredstatic usb2_config_td_command_t usb2_com_config_copy;
111184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_start_transfers;
112184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_open;
113184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_close;
114184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_break;
115184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_dtr;
116184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_rts;
117184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_status_change;
118184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_param;
119184610Salfred
120185948Sthompsastatic uint8_t	usb2_com_units_alloc(uint32_t, uint32_t *);
121185948Sthompsastatic void	usb2_com_units_free(uint32_t, uint32_t);
122185948Sthompsastatic int	usb2_com_attach_sub(struct usb2_com_softc *);
123185948Sthompsastatic void	usb2_com_detach_sub(struct usb2_com_softc *);
124185948Sthompsastatic void	usb2_com_queue_command(struct usb2_com_softc *,
125185948Sthompsa		    usb2_config_td_command_t *, int);
126185948Sthompsastatic void	usb2_com_shutdown(struct usb2_com_softc *);
127185948Sthompsastatic void	usb2_com_start_transfers(struct usb2_com_softc *);
128185948Sthompsastatic void	usb2_com_break(struct usb2_com_softc *, uint8_t);
129185948Sthompsastatic void	usb2_com_dtr(struct usb2_com_softc *, uint8_t);
130185948Sthompsastatic void	usb2_com_rts(struct usb2_com_softc *, uint8_t);
131184610Salfred
132184610Salfredstatic tsw_open_t usb2_com_open;
133184610Salfredstatic tsw_close_t usb2_com_close;
134184610Salfredstatic tsw_ioctl_t usb2_com_ioctl;
135184610Salfredstatic tsw_modem_t usb2_com_modem;
136184610Salfredstatic tsw_param_t usb2_com_param;
137184610Salfredstatic tsw_outwakeup_t usb2_com_start_write;
138184610Salfredstatic tsw_free_t usb2_com_free;
139184610Salfred
140184610Salfredstatic struct ttydevsw usb2_com_class = {
141184610Salfred	.tsw_flags = TF_INITLOCK | TF_CALLOUT,
142184610Salfred	.tsw_open = usb2_com_open,
143184610Salfred	.tsw_close = usb2_com_close,
144184610Salfred	.tsw_outwakeup = usb2_com_start_write,
145184610Salfred	.tsw_ioctl = usb2_com_ioctl,
146184610Salfred	.tsw_param = usb2_com_param,
147184610Salfred	.tsw_modem = usb2_com_modem,
148184610Salfred	.tsw_free = usb2_com_free,
149184610Salfred};
150184610Salfred
151184736SimpMODULE_DEPEND(usb2_serial, usb2_core, 1, 1, 1);
152184610SalfredMODULE_VERSION(usb2_serial, 1);
153184610Salfred
154184610Salfred#define	UCOM_UNIT_MAX 0x1000		/* exclusive */
155184610Salfred#define	UCOM_SUB_UNIT_MAX 0x100		/* exclusive */
156184610Salfred
157184610Salfredstatic uint8_t usb2_com_bitmap[(UCOM_UNIT_MAX + 7) / 8];
158184610Salfred
159184610Salfredstatic uint8_t
160184610Salfredusb2_com_units_alloc(uint32_t sub_units, uint32_t *p_root_unit)
161184610Salfred{
162184610Salfred	uint32_t n;
163184610Salfred	uint32_t o;
164184610Salfred	uint32_t x;
165184610Salfred	uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units);
166184610Salfred	uint8_t error = 1;
167184610Salfred
168184610Salfred	mtx_lock(&Giant);
169184610Salfred
170184610Salfred	for (n = 0; n < max; n += sub_units) {
171184610Salfred
172184610Salfred		/* check for free consecutive bits */
173184610Salfred
174184610Salfred		for (o = 0; o < sub_units; o++) {
175184610Salfred
176184610Salfred			x = n + o;
177184610Salfred
178184610Salfred			if (usb2_com_bitmap[x / 8] & (1 << (x % 8))) {
179184610Salfred				goto skip;
180184610Salfred			}
181184610Salfred		}
182184610Salfred
183184610Salfred		/* allocate */
184184610Salfred
185184610Salfred		for (o = 0; o < sub_units; o++) {
186184610Salfred
187184610Salfred			x = n + o;
188184610Salfred
189184610Salfred			usb2_com_bitmap[x / 8] |= (1 << (x % 8));
190184610Salfred		}
191184610Salfred
192184610Salfred		error = 0;
193184610Salfred
194184610Salfred		break;
195184610Salfred
196184610Salfredskip:		;
197184610Salfred	}
198184610Salfred
199184610Salfred	mtx_unlock(&Giant);
200184610Salfred
201184610Salfred	/*
202184610Salfred	 * Always set the variable pointed to by "p_root_unit" so that
203184610Salfred	 * the compiler does not think that it is used uninitialised:
204184610Salfred	 */
205184610Salfred	*p_root_unit = n;
206184610Salfred
207184610Salfred	return (error);
208184610Salfred}
209184610Salfred
210184610Salfredstatic void
211184610Salfredusb2_com_units_free(uint32_t root_unit, uint32_t sub_units)
212184610Salfred{
213184610Salfred	uint32_t x;
214184610Salfred
215184610Salfred	mtx_lock(&Giant);
216184610Salfred
217184610Salfred	while (sub_units--) {
218184610Salfred		x = root_unit + sub_units;
219184610Salfred		usb2_com_bitmap[x / 8] &= ~(1 << (x % 8));
220184610Salfred	}
221184610Salfred
222184610Salfred	mtx_unlock(&Giant);
223184610Salfred
224184610Salfred	return;
225184610Salfred}
226184610Salfred
227184610Salfred/*
228184610Salfred * "N" sub_units are setup at a time. All sub-units will
229184610Salfred * be given sequential unit numbers. The number of
230184610Salfred * sub-units can be used to differentiate among
231184610Salfred * different types of devices.
232184610Salfred *
233184610Salfred * The mutex pointed to by "p_mtx" is applied before all
234184610Salfred * callbacks are called back. Also "p_mtx" must be applied
235184610Salfred * before calling into the ucom-layer! Currently only Giant
236184610Salfred * is supported.
237184610Salfred */
238184610Salfredint
239184610Salfredusb2_com_attach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc,
240184610Salfred    uint32_t sub_units, void *parent,
241184610Salfred    const struct usb2_com_callback *callback, struct mtx *p_mtx)
242184610Salfred{
243184610Salfred	uint32_t n;
244184610Salfred	uint32_t root_unit;
245184610Salfred	int error = 0;
246184610Salfred
247184610Salfred	if ((sc == NULL) ||
248184610Salfred	    (sub_units == 0) ||
249184610Salfred	    (sub_units > UCOM_SUB_UNIT_MAX) ||
250184610Salfred	    (callback == NULL)) {
251184610Salfred		return (EINVAL);
252184610Salfred	}
253184610Salfred	if (usb2_com_units_alloc(sub_units, &root_unit)) {
254184610Salfred		return (ENOMEM);
255184610Salfred	}
256184610Salfred	if (usb2_config_td_setup
257184610Salfred	    (&ssc->sc_config_td, sc, p_mtx, NULL,
258184610Salfred	    sizeof(struct usb2_com_config_copy), 24 * sub_units)) {
259184610Salfred		usb2_com_units_free(root_unit, sub_units);
260184610Salfred		return (ENOMEM);
261184610Salfred	}
262184610Salfred	for (n = 0; n < sub_units; n++, sc++) {
263184610Salfred		sc->sc_unit = root_unit + n;
264184610Salfred		sc->sc_local_unit = n;
265184610Salfred		sc->sc_super = ssc;
266184610Salfred		sc->sc_parent_mtx = p_mtx;
267184610Salfred		sc->sc_parent = parent;
268184610Salfred		sc->sc_callback = callback;
269184610Salfred
270184610Salfred		error = usb2_com_attach_sub(sc);
271184610Salfred		if (error) {
272184610Salfred			usb2_com_detach(ssc, sc - n, n);
273184610Salfred			usb2_com_units_free(root_unit + n, sub_units - n);
274184610Salfred			break;
275184610Salfred		}
276184610Salfred		sc->sc_flag |= UCOM_FLAG_ATTACHED;
277184610Salfred	}
278184610Salfred	return (error);
279184610Salfred}
280184610Salfred
281184610Salfred/* NOTE: the following function will do nothing if
282184610Salfred * the structure pointed to by "ssc" and "sc" is zero.
283184610Salfred */
284184610Salfredvoid
285184610Salfredusb2_com_detach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc,
286184610Salfred    uint32_t sub_units)
287184610Salfred{
288184610Salfred	uint32_t n;
289184610Salfred
290184610Salfred	usb2_config_td_drain(&ssc->sc_config_td);
291184610Salfred
292184610Salfred	for (n = 0; n < sub_units; n++, sc++) {
293184610Salfred		if (sc->sc_flag & UCOM_FLAG_ATTACHED) {
294184610Salfred
295184610Salfred			usb2_com_detach_sub(sc);
296184610Salfred
297184610Salfred			usb2_com_units_free(sc->sc_unit, 1);
298184610Salfred
299184610Salfred			/* avoid duplicate detach: */
300184610Salfred			sc->sc_flag &= ~UCOM_FLAG_ATTACHED;
301184610Salfred		}
302184610Salfred	}
303184610Salfred
304184610Salfred	usb2_config_td_unsetup(&ssc->sc_config_td);
305184610Salfred
306184610Salfred	return;
307184610Salfred}
308184610Salfred
309184610Salfredstatic int
310184610Salfredusb2_com_attach_sub(struct usb2_com_softc *sc)
311184610Salfred{
312184610Salfred	struct tty *tp;
313184610Salfred	int error = 0;
314184610Salfred	char buf[32];			/* temporary TTY device name buffer */
315184610Salfred
316184610Salfred	tp = tty_alloc(&usb2_com_class, sc, sc->sc_parent_mtx);
317184610Salfred	if (tp == NULL) {
318184610Salfred		error = ENOMEM;
319184610Salfred		goto done;
320184610Salfred	}
321184610Salfred	DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit);
322184610Salfred
323184610Salfred	buf[0] = 0;			/* set some default value */
324184610Salfred
325184610Salfred	/* Check if the client has a custom TTY name */
326184610Salfred	if (sc->sc_callback->usb2_com_tty_name) {
327184610Salfred		sc->sc_callback->usb2_com_tty_name(sc, buf,
328184610Salfred		    sizeof(buf), sc->sc_local_unit);
329184610Salfred	}
330184610Salfred	if (buf[0] == 0) {
331184610Salfred		/* Use default TTY name */
332184610Salfred		if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) {
333184610Salfred			/* ignore */
334184610Salfred		}
335184610Salfred	}
336184610Salfred	tty_makedev(tp, NULL, "%s", buf);
337184610Salfred
338184610Salfred	sc->sc_tty = tp;
339184610Salfred
340184610Salfred	DPRINTF("ttycreate: %s\n", buf);
341184610Salfred	usb2_cv_init(&sc->sc_cv, "usb2_com");
342184610Salfred
343184610Salfreddone:
344184610Salfred	return (error);
345184610Salfred}
346184610Salfred
347184610Salfredstatic void
348184610Salfredusb2_com_detach_sub(struct usb2_com_softc *sc)
349184610Salfred{
350184610Salfred	struct tty *tp = sc->sc_tty;
351184610Salfred
352184610Salfred	DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
353184610Salfred
354184610Salfred	/* the config thread has been stopped when we get here */
355184610Salfred
356184610Salfred	mtx_lock(sc->sc_parent_mtx);
357184610Salfred	sc->sc_flag |= UCOM_FLAG_GONE;
358184610Salfred	sc->sc_flag &= ~(UCOM_FLAG_HL_READY |
359184610Salfred	    UCOM_FLAG_LL_READY);
360184610Salfred	mtx_unlock(sc->sc_parent_mtx);
361184610Salfred	if (tp) {
362184610Salfred		tty_lock(tp);
363184610Salfred
364184610Salfred		usb2_com_close(tp);	/* close, if any */
365184610Salfred
366184610Salfred		tty_rel_gone(tp);
367184610Salfred
368184610Salfred		mtx_lock(sc->sc_parent_mtx);
369184610Salfred		/* Wait for the callback after the TTY is torn down */
370184610Salfred		while (sc->sc_ttyfreed == 0)
371184610Salfred			usb2_cv_wait(&sc->sc_cv, sc->sc_parent_mtx);
372184610Salfred		/*
373184610Salfred		 * make sure that read and write transfers are stopped
374184610Salfred		 */
375184610Salfred		if (sc->sc_callback->usb2_com_stop_read) {
376184610Salfred			(sc->sc_callback->usb2_com_stop_read) (sc);
377184610Salfred		}
378184610Salfred		if (sc->sc_callback->usb2_com_stop_write) {
379184610Salfred			(sc->sc_callback->usb2_com_stop_write) (sc);
380184610Salfred		}
381184610Salfred		mtx_unlock(sc->sc_parent_mtx);
382184610Salfred	}
383184610Salfred	usb2_cv_destroy(&sc->sc_cv);
384184610Salfred	return;
385184610Salfred}
386184610Salfred
387184610Salfredstatic void
388184610Salfredusb2_com_config_copy(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc,
389184610Salfred    uint16_t refcount)
390184610Salfred{
391184610Salfred	cc->cc_softc = sc + (refcount % UCOM_SUB_UNIT_MAX);
392184610Salfred	cc->cc_flag0 = (refcount / (1 * UCOM_SUB_UNIT_MAX)) % 2;
393184610Salfred	cc->cc_flag1 = (refcount / (2 * UCOM_SUB_UNIT_MAX)) % 2;
394184610Salfred	cc->cc_flag2 = (refcount / (4 * UCOM_SUB_UNIT_MAX)) % 2;
395184610Salfred	cc->cc_flag3 = (refcount / (8 * UCOM_SUB_UNIT_MAX)) % 2;
396184610Salfred	return;
397184610Salfred}
398184610Salfred
399184610Salfredstatic void
400184610Salfredusb2_com_queue_command(struct usb2_com_softc *sc, usb2_config_td_command_t *cmd, int flag)
401184610Salfred{
402184610Salfred	struct usb2_com_super_softc *ssc = sc->sc_super;
403184610Salfred
404184610Salfred	usb2_config_td_queue_command
405184610Salfred	    (&ssc->sc_config_td, &usb2_com_config_copy,
406184610Salfred	    cmd, (cmd == &usb2_com_cfg_status_change) ? 1 : 0,
407184610Salfred	    ((sc->sc_local_unit % UCOM_SUB_UNIT_MAX) +
408184610Salfred	    (flag ? UCOM_SUB_UNIT_MAX : 0)));
409184610Salfred	return;
410184610Salfred}
411184610Salfred
412184610Salfredstatic void
413184610Salfredusb2_com_shutdown(struct usb2_com_softc *sc)
414184610Salfred{
415184610Salfred	struct tty *tp = sc->sc_tty;
416184610Salfred
417184610Salfred	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
418184610Salfred
419184610Salfred	DPRINTF("\n");
420184610Salfred
421184610Salfred	/*
422184610Salfred	 * Hang up if necessary:
423184610Salfred	 */
424184610Salfred	if (tp->t_termios.c_cflag & HUPCL) {
425184610Salfred		usb2_com_modem(tp, 0, SER_DTR);
426184610Salfred	}
427184610Salfred	return;
428184610Salfred}
429184610Salfred
430184610Salfred/*
431184610Salfred * Return values:
432184610Salfred *    0: normal delay
433184610Salfred * else: config thread is gone
434184610Salfred */
435184610Salfreduint8_t
436184610Salfredusb2_com_cfg_sleep(struct usb2_com_softc *sc, uint32_t timeout)
437184610Salfred{
438184610Salfred	struct usb2_com_super_softc *ssc = sc->sc_super;
439184610Salfred
440184610Salfred	return (usb2_config_td_sleep(&ssc->sc_config_td, timeout));
441184610Salfred}
442184610Salfred
443184610Salfred/*
444184610Salfred * Return values:
445184610Salfred *    0: normal
446184610Salfred * else: config thread is gone
447184610Salfred */
448184610Salfreduint8_t
449184610Salfredusb2_com_cfg_is_gone(struct usb2_com_softc *sc)
450184610Salfred{
451184610Salfred	struct usb2_com_super_softc *ssc = sc->sc_super;
452184610Salfred
453184610Salfred	return (usb2_config_td_is_gone(&ssc->sc_config_td));
454184610Salfred}
455184610Salfred
456184610Salfredstatic void
457184610Salfredusb2_com_cfg_start_transfers(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc,
458184610Salfred    uint16_t refcount)
459184610Salfred{
460184610Salfred	sc = cc->cc_softc;
461184610Salfred
462184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
463184610Salfred		return;
464184610Salfred	}
465184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
466184610Salfred		/* TTY device closed */
467184610Salfred		return;
468184610Salfred	}
469184610Salfred	sc->sc_flag |= UCOM_FLAG_GP_DATA;
470184610Salfred
471184610Salfred	if (sc->sc_callback->usb2_com_start_read) {
472184610Salfred		(sc->sc_callback->usb2_com_start_read) (sc);
473184610Salfred	}
474184610Salfred	if (sc->sc_callback->usb2_com_start_write) {
475184610Salfred		(sc->sc_callback->usb2_com_start_write) (sc);
476184610Salfred	}
477184610Salfred	return;
478184610Salfred}
479184610Salfred
480184610Salfredstatic void
481184610Salfredusb2_com_start_transfers(struct usb2_com_softc *sc)
482184610Salfred{
483184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
484184610Salfred		return;
485184610Salfred	}
486184610Salfred	/*
487184610Salfred	 * do a direct call first, to get hardware buffers flushed
488184610Salfred	 */
489184610Salfred
490184610Salfred	if (sc->sc_callback->usb2_com_start_read) {
491184610Salfred		(sc->sc_callback->usb2_com_start_read) (sc);
492184610Salfred	}
493184610Salfred	if (sc->sc_callback->usb2_com_start_write) {
494184610Salfred		(sc->sc_callback->usb2_com_start_write) (sc);
495184610Salfred	}
496184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
497184610Salfred		usb2_com_queue_command(sc, &usb2_com_cfg_start_transfers, 0);
498184610Salfred	}
499184610Salfred	return;
500184610Salfred}
501184610Salfred
502184610Salfredstatic void
503184610Salfredusb2_com_cfg_open(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc,
504184610Salfred    uint16_t refcount)
505184610Salfred{
506184610Salfred	sc = cc->cc_softc;
507184610Salfred
508184610Salfred	DPRINTF("\n");
509184610Salfred
510184610Salfred	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
511184610Salfred
512184610Salfred		/* already opened */
513184610Salfred
514184610Salfred	} else {
515184610Salfred
516184610Salfred		sc->sc_flag |= UCOM_FLAG_LL_READY;
517184610Salfred
518184610Salfred		if (sc->sc_callback->usb2_com_cfg_open) {
519184610Salfred			(sc->sc_callback->usb2_com_cfg_open) (sc);
520184610Salfred
521184610Salfred			/* wait a little */
522184610Salfred			usb2_com_cfg_sleep(sc, hz / 10);
523184610Salfred		}
524184610Salfred	}
525184610Salfred	return;
526184610Salfred}
527184610Salfred
528184610Salfredstatic int
529184610Salfredusb2_com_open(struct tty *tp)
530184610Salfred{
531184610Salfred	struct usb2_com_softc *sc = tty_softc(tp);
532184610Salfred	int error;
533184610Salfred
534184610Salfred	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
535184610Salfred
536184610Salfred	if (sc->sc_flag & UCOM_FLAG_GONE) {
537184610Salfred		return (ENXIO);
538184610Salfred	}
539184610Salfred	if (sc->sc_flag & UCOM_FLAG_HL_READY) {
540184610Salfred		/* already opened */
541184610Salfred		return (0);
542184610Salfred	}
543184610Salfred	DPRINTF("tp = %p\n", tp);
544184610Salfred
545184610Salfred	if (sc->sc_callback->usb2_com_pre_open) {
546184610Salfred		/*
547184610Salfred		 * give the lower layer a chance to disallow TTY open, for
548184610Salfred		 * example if the device is not present:
549184610Salfred		 */
550184610Salfred		error = (sc->sc_callback->usb2_com_pre_open) (sc);
551184610Salfred		if (error) {
552184610Salfred			return (error);
553184610Salfred		}
554184610Salfred	}
555184610Salfred	sc->sc_flag |= UCOM_FLAG_HL_READY;
556184610Salfred
557184610Salfred	/* Disable transfers */
558184610Salfred	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
559184610Salfred
560184610Salfred	sc->sc_lsr = 0;
561184610Salfred	sc->sc_msr = 0;
562184610Salfred	sc->sc_mcr = 0;
563184610Salfred
564184610Salfred	usb2_com_queue_command(sc, &usb2_com_cfg_open, 0);
565184610Salfred
566184610Salfred	usb2_com_start_transfers(sc);
567184610Salfred
568184610Salfred	usb2_com_modem(tp, SER_DTR | SER_RTS, 0);
569184610Salfred
570184610Salfred	usb2_com_break(sc, 0);
571184610Salfred
572184610Salfred	usb2_com_status_change(sc);
573184610Salfred
574184610Salfred	return (0);
575184610Salfred}
576184610Salfred
577184610Salfredstatic void
578184610Salfredusb2_com_cfg_close(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc,
579184610Salfred    uint16_t refcount)
580184610Salfred{
581184610Salfred	sc = cc->cc_softc;
582184610Salfred
583184610Salfred	DPRINTF("\n");
584184610Salfred
585184610Salfred	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
586184610Salfred
587184610Salfred		sc->sc_flag &= ~(UCOM_FLAG_LL_READY |
588184610Salfred		    UCOM_FLAG_GP_DATA);
589184610Salfred
590184610Salfred		if (sc->sc_callback->usb2_com_cfg_close) {
591184610Salfred			(sc->sc_callback->usb2_com_cfg_close) (sc);
592184610Salfred		}
593184610Salfred	} else {
594184610Salfred		/* already closed */
595184610Salfred	}
596184610Salfred	return;
597184610Salfred}
598184610Salfred
599184610Salfredstatic void
600184610Salfredusb2_com_close(struct tty *tp)
601184610Salfred{
602184610Salfred	struct usb2_com_softc *sc = tty_softc(tp);
603184610Salfred
604184610Salfred	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
605184610Salfred
606184610Salfred	DPRINTF("tp=%p\n", tp);
607184610Salfred
608184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
609184610Salfred		DPRINTF("tp=%p already closed\n", tp);
610184610Salfred		return;
611184610Salfred	}
612184610Salfred	usb2_com_shutdown(sc);
613184610Salfred
614184610Salfred	usb2_com_queue_command(sc, &usb2_com_cfg_close, 0);
615184610Salfred
616184610Salfred	sc->sc_flag &= ~(UCOM_FLAG_HL_READY |
617184610Salfred	    UCOM_FLAG_WR_START |
618184610Salfred	    UCOM_FLAG_RTS_IFLOW);
619184610Salfred
620184610Salfred	if (sc->sc_callback->usb2_com_stop_read) {
621184610Salfred		(sc->sc_callback->usb2_com_stop_read) (sc);
622184610Salfred	}
623184610Salfred	if (sc->sc_callback->usb2_com_stop_write) {
624184610Salfred		(sc->sc_callback->usb2_com_stop_write) (sc);
625184610Salfred	}
626184610Salfred	return;
627184610Salfred}
628184610Salfred
629184610Salfredstatic int
630184610Salfredusb2_com_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
631184610Salfred{
632184610Salfred	struct usb2_com_softc *sc = tty_softc(tp);
633184610Salfred	int error;
634184610Salfred
635184610Salfred	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
636184610Salfred
637184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
638184610Salfred		return (EIO);
639184610Salfred	}
640184610Salfred	DPRINTF("cmd = 0x%08lx\n", cmd);
641184610Salfred
642184610Salfred	switch (cmd) {
643184610Salfred	case TIOCSBRK:
644184610Salfred		usb2_com_break(sc, 1);
645184610Salfred		error = 0;
646184610Salfred		break;
647184610Salfred	case TIOCCBRK:
648184610Salfred		usb2_com_break(sc, 0);
649184610Salfred		error = 0;
650184610Salfred		break;
651184610Salfred	default:
652184610Salfred		if (sc->sc_callback->usb2_com_ioctl) {
653184610Salfred			error = (sc->sc_callback->usb2_com_ioctl)
654184610Salfred			    (sc, cmd, data, 0, td);
655184610Salfred		} else {
656184610Salfred			error = ENOIOCTL;
657184610Salfred		}
658184610Salfred		break;
659184610Salfred	}
660184610Salfred	return (error);
661184610Salfred}
662184610Salfred
663184610Salfredstatic int
664184610Salfredusb2_com_modem(struct tty *tp, int sigon, int sigoff)
665184610Salfred{
666184610Salfred	struct usb2_com_softc *sc = tty_softc(tp);
667184610Salfred	uint8_t onoff;
668184610Salfred
669184610Salfred	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
670184610Salfred
671184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
672184610Salfred		return (0);
673184610Salfred	}
674184610Salfred	if ((sigon == 0) && (sigoff == 0)) {
675184610Salfred
676184610Salfred		if (sc->sc_mcr & SER_DTR) {
677184610Salfred			sigon |= SER_DTR;
678184610Salfred		}
679184610Salfred		if (sc->sc_mcr & SER_RTS) {
680184610Salfred			sigon |= SER_RTS;
681184610Salfred		}
682184610Salfred		if (sc->sc_msr & SER_CTS) {
683184610Salfred			sigon |= SER_CTS;
684184610Salfred		}
685184610Salfred		if (sc->sc_msr & SER_DCD) {
686184610Salfred			sigon |= SER_DCD;
687184610Salfred		}
688184610Salfred		if (sc->sc_msr & SER_DSR) {
689184610Salfred			sigon |= SER_DSR;
690184610Salfred		}
691184610Salfred		if (sc->sc_msr & SER_RI) {
692184610Salfred			sigon |= SER_RI;
693184610Salfred		}
694184610Salfred		return (sigon);
695184610Salfred	}
696184610Salfred	if (sigon & SER_DTR) {
697184610Salfred		sc->sc_mcr |= SER_DTR;
698184610Salfred	}
699184610Salfred	if (sigoff & SER_DTR) {
700184610Salfred		sc->sc_mcr &= ~SER_DTR;
701184610Salfred	}
702184610Salfred	if (sigon & SER_RTS) {
703184610Salfred		sc->sc_mcr |= SER_RTS;
704184610Salfred	}
705184610Salfred	if (sigoff & SER_RTS) {
706184610Salfred		sc->sc_mcr &= ~SER_RTS;
707184610Salfred	}
708184610Salfred	onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
709184610Salfred	usb2_com_dtr(sc, onoff);
710184610Salfred
711184610Salfred	onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
712184610Salfred	usb2_com_rts(sc, onoff);
713184610Salfred
714184610Salfred	return (0);
715184610Salfred}
716184610Salfred
717184610Salfredstatic void
718184610Salfredusb2_com_cfg_break(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc,
719184610Salfred    uint16_t refcount)
720184610Salfred{
721184610Salfred	sc = cc->cc_softc;
722184610Salfred
723184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
724184610Salfred		return;
725184610Salfred	}
726184610Salfred	DPRINTF("onoff=%d\n", cc->cc_flag0);
727184610Salfred
728184610Salfred	if (sc->sc_callback->usb2_com_cfg_set_break) {
729184610Salfred		(sc->sc_callback->usb2_com_cfg_set_break) (sc, cc->cc_flag0);
730184610Salfred	}
731184610Salfred	return;
732184610Salfred}
733184610Salfred
734184610Salfredstatic void
735184610Salfredusb2_com_break(struct usb2_com_softc *sc, uint8_t onoff)
736184610Salfred{
737184610Salfred	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
738184610Salfred
739184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
740184610Salfred		return;
741184610Salfred	}
742184610Salfred	DPRINTF("onoff = %d\n", onoff);
743184610Salfred
744184610Salfred	usb2_com_queue_command(sc, &usb2_com_cfg_break, onoff);
745184610Salfred	return;
746184610Salfred}
747184610Salfred
748184610Salfredstatic void
749184610Salfredusb2_com_cfg_dtr(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc,
750184610Salfred    uint16_t refcount)
751184610Salfred{
752184610Salfred	sc = cc->cc_softc;
753184610Salfred
754184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
755184610Salfred		return;
756184610Salfred	}
757184610Salfred	DPRINTF("onoff=%d\n", cc->cc_flag0);
758184610Salfred
759184610Salfred	if (sc->sc_callback->usb2_com_cfg_set_dtr) {
760184610Salfred		(sc->sc_callback->usb2_com_cfg_set_dtr) (sc, cc->cc_flag0);
761184610Salfred	}
762184610Salfred	return;
763184610Salfred}
764184610Salfred
765184610Salfredstatic void
766184610Salfredusb2_com_dtr(struct usb2_com_softc *sc, uint8_t onoff)
767184610Salfred{
768184610Salfred	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
769184610Salfred
770184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
771184610Salfred		return;
772184610Salfred	}
773184610Salfred	DPRINTF("onoff = %d\n", onoff);
774184610Salfred
775184610Salfred	usb2_com_queue_command(sc, &usb2_com_cfg_dtr, onoff);
776184610Salfred	return;
777184610Salfred}
778184610Salfred
779184610Salfredstatic void
780184610Salfredusb2_com_cfg_rts(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc,
781184610Salfred    uint16_t refcount)
782184610Salfred{
783184610Salfred	sc = cc->cc_softc;
784184610Salfred
785184610Salfred	DPRINTF("onoff=%d\n", cc->cc_flag0);
786184610Salfred
787184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
788184610Salfred		return;
789184610Salfred	}
790184610Salfred	if (sc->sc_callback->usb2_com_cfg_set_rts) {
791184610Salfred		(sc->sc_callback->usb2_com_cfg_set_rts) (sc, cc->cc_flag0);
792184610Salfred	}
793184610Salfred	return;
794184610Salfred}
795184610Salfred
796184610Salfredstatic void
797184610Salfredusb2_com_rts(struct usb2_com_softc *sc, uint8_t onoff)
798184610Salfred{
799184610Salfred	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
800184610Salfred
801184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
802184610Salfred		return;
803184610Salfred	}
804184610Salfred	DPRINTF("onoff = %d\n", onoff);
805184610Salfred
806184610Salfred	usb2_com_queue_command(sc, &usb2_com_cfg_rts, onoff);
807184610Salfred
808184610Salfred	return;
809184610Salfred}
810184610Salfred
811184610Salfredstatic void
812184610Salfredusb2_com_cfg_status_change(struct usb2_com_softc *sc,
813184610Salfred    struct usb2_com_config_copy *cc, uint16_t refcount)
814184610Salfred{
815184610Salfred	struct tty *tp;
816184610Salfred
817184610Salfred	uint8_t new_msr;
818184610Salfred	uint8_t new_lsr;
819184610Salfred	uint8_t onoff;
820184610Salfred
821184610Salfred	sc = cc->cc_softc;
822184610Salfred	tp = sc->sc_tty;
823184610Salfred
824184610Salfred	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
825184610Salfred
826184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
827184610Salfred		return;
828184610Salfred	}
829184610Salfred	if (sc->sc_callback->usb2_com_cfg_get_status == NULL) {
830184610Salfred		return;
831184610Salfred	}
832184610Salfred	/* get status */
833184610Salfred
834184610Salfred	new_msr = 0;
835184610Salfred	new_lsr = 0;
836184610Salfred
837184610Salfred	(sc->sc_callback->usb2_com_cfg_get_status) (sc, &new_lsr, &new_msr);
838184610Salfred
839184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
840184610Salfred		/* TTY device closed */
841184610Salfred		return;
842184610Salfred	}
843184610Salfred	onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
844184610Salfred
845184610Salfred	sc->sc_msr = new_msr;
846184610Salfred	sc->sc_lsr = new_lsr;
847184610Salfred
848184610Salfred	if (onoff) {
849184610Salfred
850184610Salfred		onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
851184610Salfred
852184610Salfred		DPRINTF("DCD changed to %d\n", onoff);
853184610Salfred
854184610Salfred		ttydisc_modem(tp, onoff);
855184610Salfred	}
856184610Salfred	return;
857184610Salfred}
858184610Salfred
859184610Salfredvoid
860184610Salfredusb2_com_status_change(struct usb2_com_softc *sc)
861184610Salfred{
862184610Salfred	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
863184610Salfred
864184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
865184610Salfred		return;
866184610Salfred	}
867184610Salfred	DPRINTF("\n");
868184610Salfred
869184610Salfred	usb2_com_queue_command(sc, &usb2_com_cfg_status_change, 0);
870184610Salfred	return;
871184610Salfred}
872184610Salfred
873184610Salfredstatic void
874184610Salfredusb2_com_cfg_param(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc,
875184610Salfred    uint16_t refcount)
876184610Salfred{
877184610Salfred	struct termios t_copy;
878184610Salfred
879184610Salfred	sc = cc->cc_softc;
880184610Salfred
881184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
882184610Salfred		return;
883184610Salfred	}
884184610Salfred	if (sc->sc_callback->usb2_com_cfg_param == NULL) {
885184610Salfred		return;
886184610Salfred	}
887184610Salfred	t_copy = sc->sc_termios_copy;
888184610Salfred
889184610Salfred	(sc->sc_callback->usb2_com_cfg_param) (sc, &t_copy);
890184610Salfred
891184610Salfred	/* wait a little */
892184610Salfred	usb2_com_cfg_sleep(sc, hz / 10);
893184610Salfred
894184610Salfred	return;
895184610Salfred}
896184610Salfred
897184610Salfredstatic int
898184610Salfredusb2_com_param(struct tty *tp, struct termios *t)
899184610Salfred{
900184610Salfred	struct usb2_com_softc *sc = tty_softc(tp);
901184610Salfred	uint8_t opened;
902184610Salfred	int error;
903184610Salfred
904184610Salfred	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
905184610Salfred
906184610Salfred	opened = 0;
907184610Salfred	error = 0;
908184610Salfred
909184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
910184610Salfred
911184610Salfred		/* XXX the TTY layer should call "open()" first! */
912184610Salfred
913184610Salfred		error = usb2_com_open(tp);
914184610Salfred		if (error) {
915184610Salfred			goto done;
916184610Salfred		}
917184610Salfred		opened = 1;
918184610Salfred	}
919184610Salfred	DPRINTF("sc = %p\n", sc);
920184610Salfred
921184610Salfred	/* Check requested parameters. */
922184610Salfred	if (t->c_ospeed < 0) {
923184610Salfred		DPRINTF("negative ospeed\n");
924184610Salfred		error = EINVAL;
925184610Salfred		goto done;
926184610Salfred	}
927184610Salfred	if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
928184610Salfred		DPRINTF("mismatch ispeed and ospeed\n");
929184610Salfred		error = EINVAL;
930184610Salfred		goto done;
931184610Salfred	}
932184610Salfred	t->c_ispeed = t->c_ospeed;
933184610Salfred
934184610Salfred	if (sc->sc_callback->usb2_com_pre_param) {
935184610Salfred		/* Let the lower layer verify the parameters */
936184610Salfred		error = (sc->sc_callback->usb2_com_pre_param) (sc, t);
937184610Salfred		if (error) {
938184610Salfred			DPRINTF("callback error = %d\n", error);
939184610Salfred			goto done;
940184610Salfred		}
941184610Salfred	}
942184610Salfred	/* Make a copy of the termios parameters */
943184610Salfred	sc->sc_termios_copy = *t;
944184610Salfred
945184610Salfred	/* Disable transfers */
946184610Salfred	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
947184610Salfred
948184610Salfred	/* Queue baud rate programming command first */
949184610Salfred	usb2_com_queue_command(sc, &usb2_com_cfg_param, 0);
950184610Salfred
951184610Salfred	/* Queue transfer enable command last */
952184610Salfred	usb2_com_start_transfers(sc);
953184610Salfred
954184610Salfred	if (t->c_cflag & CRTS_IFLOW) {
955184610Salfred		sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
956184610Salfred	} else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
957184610Salfred		sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
958184610Salfred		usb2_com_modem(tp, SER_RTS, 0);
959184610Salfred	}
960184610Salfreddone:
961184610Salfred	if (error) {
962184610Salfred		if (opened) {
963184610Salfred			usb2_com_close(tp);
964184610Salfred		}
965184610Salfred	}
966184610Salfred	return (error);
967184610Salfred}
968184610Salfred
969184610Salfredstatic void
970184610Salfredusb2_com_start_write(struct tty *tp)
971184610Salfred{
972184610Salfred	struct usb2_com_softc *sc = tty_softc(tp);
973184610Salfred
974184610Salfred	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
975184610Salfred
976184610Salfred	DPRINTF("sc = %p\n", sc);
977184610Salfred
978184610Salfred	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
979184610Salfred		/* The higher layer is not ready */
980184610Salfred		return;
981184610Salfred	}
982184610Salfred	sc->sc_flag |= UCOM_FLAG_WR_START;
983184610Salfred
984184610Salfred	usb2_com_start_transfers(sc);
985184610Salfred
986184610Salfred	return;
987184610Salfred}
988184610Salfred
989184610Salfred/*------------------------------------------------------------------------*
990184610Salfred *	usb2_com_get_data
991184610Salfred *
992184610Salfred * Return values:
993184610Salfred * 0: No data is available.
994184610Salfred * Else: Data is available.
995184610Salfred *------------------------------------------------------------------------*/
996184610Salfreduint8_t
997184610Salfredusb2_com_get_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
998184610Salfred    uint32_t offset, uint32_t len, uint32_t *actlen)
999184610Salfred{
1000184610Salfred	struct usb2_page_search res;
1001184610Salfred	struct tty *tp = sc->sc_tty;
1002184610Salfred	uint32_t cnt;
1003184610Salfred	uint32_t offset_orig;
1004184610Salfred
1005184610Salfred	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
1006184610Salfred
1007184610Salfred	if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) ||
1008184610Salfred	    (!(sc->sc_flag & UCOM_FLAG_GP_DATA)) ||
1009184610Salfred	    (!(sc->sc_flag & UCOM_FLAG_WR_START))) {
1010184610Salfred		actlen[0] = 0;
1011184610Salfred		return (0);		/* multiport device polling */
1012184610Salfred	}
1013184610Salfred	offset_orig = offset;
1014184610Salfred
1015184610Salfred	while (len != 0) {
1016184610Salfred
1017184610Salfred		usb2_get_page(pc, offset, &res);
1018184610Salfred
1019184610Salfred		if (res.length > len) {
1020184610Salfred			res.length = len;
1021184610Salfred		}
1022184610Salfred		/* copy data directly into USB buffer */
1023184610Salfred		cnt = ttydisc_getc(tp, res.buffer, res.length);
1024184610Salfred
1025184610Salfred		offset += cnt;
1026184610Salfred		len -= cnt;
1027184610Salfred
1028184610Salfred		if (cnt < res.length) {
1029184610Salfred			/* end of buffer */
1030184610Salfred			break;
1031184610Salfred		}
1032184610Salfred	}
1033184610Salfred
1034184610Salfred	actlen[0] = offset - offset_orig;
1035184610Salfred
1036184610Salfred	DPRINTF("cnt=%d\n", actlen[0]);
1037184610Salfred
1038184610Salfred	if (actlen[0] == 0) {
1039184610Salfred		return (0);
1040184610Salfred	}
1041184610Salfred	return (1);
1042184610Salfred}
1043184610Salfred
1044184610Salfredvoid
1045184610Salfredusb2_com_put_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
1046184610Salfred    uint32_t offset, uint32_t len)
1047184610Salfred{
1048184610Salfred	struct usb2_page_search res;
1049184610Salfred	struct tty *tp = sc->sc_tty;
1050184610Salfred	char *buf;
1051184610Salfred	uint32_t cnt;
1052184610Salfred
1053184610Salfred	mtx_assert(sc->sc_parent_mtx, MA_OWNED);
1054184610Salfred
1055184610Salfred	if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) ||
1056184610Salfred	    (!(sc->sc_flag & UCOM_FLAG_GP_DATA))) {
1057184610Salfred		return;			/* multiport device polling */
1058184610Salfred	}
1059184610Salfred	if (len == 0)
1060184610Salfred		return;			/* no data */
1061184610Salfred
1062184610Salfred	/* set a flag to prevent recursation ? */
1063184610Salfred
1064184610Salfred	while (len > 0) {
1065184610Salfred
1066184610Salfred		usb2_get_page(pc, offset, &res);
1067184610Salfred
1068184610Salfred		if (res.length > len) {
1069184610Salfred			res.length = len;
1070184610Salfred		}
1071184610Salfred		len -= res.length;
1072184610Salfred		offset += res.length;
1073184610Salfred
1074184610Salfred		/* pass characters to tty layer */
1075184610Salfred
1076184610Salfred		buf = res.buffer;
1077184610Salfred		cnt = res.length;
1078184610Salfred
1079184610Salfred		/* first check if we can pass the buffer directly */
1080184610Salfred
1081184610Salfred		if (ttydisc_can_bypass(tp)) {
1082184610Salfred			if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1083184610Salfred				DPRINTF("tp=%p, data lost\n", tp);
1084184610Salfred			}
1085184610Salfred			continue;
1086184610Salfred		}
1087184610Salfred		/* need to loop */
1088184610Salfred
1089184610Salfred		for (cnt = 0; cnt != res.length; cnt++) {
1090184610Salfred			if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
1091184610Salfred				/* XXX what should we do? */
1092184610Salfred
1093184610Salfred				DPRINTF("tp=%p, lost %d "
1094184610Salfred				    "chars\n", tp, res.length - cnt);
1095184610Salfred				break;
1096184610Salfred			}
1097184610Salfred		}
1098184610Salfred	}
1099184610Salfred	ttydisc_rint_done(tp);
1100184610Salfred	return;
1101184610Salfred}
1102184610Salfred
1103184610Salfredstatic void
1104184610Salfredusb2_com_free(void *xsc)
1105184610Salfred{
1106184610Salfred	struct usb2_com_softc *sc = xsc;
1107184610Salfred
1108184610Salfred	mtx_lock(sc->sc_parent_mtx);
1109184610Salfred	sc->sc_ttyfreed = 1;
1110184610Salfred	usb2_cv_signal(&sc->sc_cv);
1111184610Salfred	mtx_unlock(sc->sc_parent_mtx);
1112184610Salfred}
1113