xref: /freebsd-src/sys/dev/usb/net/uhso.c (revision aa3860851b9f6a6002d135b1cac7736e0995eedc)
1941e2863SAndrew Thompson /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
421a9d6e7SAndrew Thompson  * Copyright (c) 2010 Fredrik Lindberg <fli@shapeshifter.se>
5941e2863SAndrew Thompson  * All rights reserved.
6941e2863SAndrew Thompson  *
7941e2863SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
8941e2863SAndrew Thompson  * modification, are permitted provided that the following conditions
9941e2863SAndrew Thompson  * are met:
10941e2863SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
11941e2863SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
12941e2863SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
13941e2863SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
14941e2863SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
15941e2863SAndrew Thompson  *
16941e2863SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17941e2863SAndrew Thompson  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18941e2863SAndrew Thompson  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19941e2863SAndrew Thompson  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20941e2863SAndrew Thompson  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21941e2863SAndrew Thompson  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22941e2863SAndrew Thompson  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23941e2863SAndrew Thompson  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24941e2863SAndrew Thompson  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25941e2863SAndrew Thompson  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26941e2863SAndrew Thompson  *
27941e2863SAndrew Thompson  */
28*fdafd315SWarner Losh 
29941e2863SAndrew Thompson #include <sys/param.h>
30e2e050c8SConrad Meyer #include <sys/eventhandler.h>
31941e2863SAndrew Thompson #include <sys/sockio.h>
32941e2863SAndrew Thompson #include <sys/mbuf.h>
33941e2863SAndrew Thompson #include <sys/malloc.h>
34941e2863SAndrew Thompson #include <sys/kernel.h>
35941e2863SAndrew Thompson #include <sys/module.h>
36941e2863SAndrew Thompson #include <sys/socket.h>
37941e2863SAndrew Thompson #include <sys/tty.h>
38941e2863SAndrew Thompson #include <sys/sysctl.h>
39941e2863SAndrew Thompson #include <sys/condvar.h>
40941e2863SAndrew Thompson #include <sys/sx.h>
41941e2863SAndrew Thompson #include <sys/proc.h>
42941e2863SAndrew Thompson #include <sys/conf.h>
43941e2863SAndrew Thompson #include <sys/bus.h>
44941e2863SAndrew Thompson #include <sys/systm.h>
4521a9d6e7SAndrew Thompson #include <sys/limits.h>
46941e2863SAndrew Thompson 
47941e2863SAndrew Thompson #include <machine/bus.h>
48941e2863SAndrew Thompson 
49941e2863SAndrew Thompson #include <net/if.h>
5076039bc8SGleb Smirnoff #include <net/if_var.h>
51941e2863SAndrew Thompson #include <net/if_types.h>
52941e2863SAndrew Thompson #include <net/netisr.h>
53941e2863SAndrew Thompson #include <net/bpf.h>
54941e2863SAndrew Thompson #include <netinet/in.h>
55941e2863SAndrew Thompson #include <netinet/ip.h>
56941e2863SAndrew Thompson #include <netinet/ip6.h>
57941e2863SAndrew Thompson 
58941e2863SAndrew Thompson #include <dev/usb/usb.h>
59941e2863SAndrew Thompson #include <dev/usb/usbdi.h>
60941e2863SAndrew Thompson #include <dev/usb/usbdi_util.h>
61941e2863SAndrew Thompson #include <dev/usb/usb_cdc.h>
62941e2863SAndrew Thompson #include "usbdevs.h"
63941e2863SAndrew Thompson #define USB_DEBUG_VAR uhso_debug
64941e2863SAndrew Thompson #include <dev/usb/usb_debug.h>
65941e2863SAndrew Thompson #include <dev/usb/usb_process.h>
66941e2863SAndrew Thompson #include <dev/usb/usb_busdma.h>
67941e2863SAndrew Thompson #include <dev/usb/usb_msctest.h>
68941e2863SAndrew Thompson 
69ae538d85SAndrew Thompson #include <dev/usb/serial/usb_serial.h>
70ae538d85SAndrew Thompson 
71941e2863SAndrew Thompson struct uhso_tty {
72941e2863SAndrew Thompson 	struct uhso_softc *ht_sc;
73941e2863SAndrew Thompson 	struct usb_xfer	*ht_xfer[3];
749b6ffc1fSAndrew Thompson 	int		ht_muxport; /* Mux. port no */
75941e2863SAndrew Thompson 	int		ht_open;
76941e2863SAndrew Thompson 	char		ht_name[32];
77941e2863SAndrew Thompson };
78941e2863SAndrew Thompson 
79941e2863SAndrew Thompson struct uhso_softc {
80941e2863SAndrew Thompson 	device_t		sc_dev;
81941e2863SAndrew Thompson 	struct usb_device	*sc_udev;
82941e2863SAndrew Thompson 	struct mtx		sc_mtx;
839b6ffc1fSAndrew Thompson 	uint32_t		sc_type;	/* Interface definition */
8421a9d6e7SAndrew Thompson 	int			sc_radio;
85941e2863SAndrew Thompson 
86941e2863SAndrew Thompson 	struct usb_xfer		*sc_xfer[3];
87941e2863SAndrew Thompson 	uint8_t			sc_iface_no;
88941e2863SAndrew Thompson 	uint8_t			sc_iface_index;
89941e2863SAndrew Thompson 
90941e2863SAndrew Thompson 	/* Control pipe */
91941e2863SAndrew Thompson 	struct usb_xfer	*	sc_ctrl_xfer[2];
92941e2863SAndrew Thompson 	uint8_t			sc_ctrl_iface_no;
93941e2863SAndrew Thompson 
94941e2863SAndrew Thompson 	/* Network */
95941e2863SAndrew Thompson 	struct usb_xfer		*sc_if_xfer[2];
96935b194dSJustin Hibbits 	if_t			sc_ifp;
979b6ffc1fSAndrew Thompson 	struct mbuf		*sc_mwait;	/* Partial packet */
989b6ffc1fSAndrew Thompson 	size_t			sc_waitlen;	/* No. of outstanding bytes */
9932c4f3bbSGleb Smirnoff 	struct mbufq		sc_rxq;
100941e2863SAndrew Thompson 	struct callout		sc_c;
101941e2863SAndrew Thompson 
102941e2863SAndrew Thompson 	/* TTY related structures */
103941e2863SAndrew Thompson 	struct ucom_super_softc sc_super_ucom;
104941e2863SAndrew Thompson 	int			sc_ttys;
105941e2863SAndrew Thompson 	struct uhso_tty		*sc_tty;
106941e2863SAndrew Thompson 	struct ucom_softc	*sc_ucom;
107941e2863SAndrew Thompson 	int			sc_msr;
108941e2863SAndrew Thompson 	int			sc_lsr;
109941e2863SAndrew Thompson 	int			sc_line;
110941e2863SAndrew Thompson };
111941e2863SAndrew Thompson 
112941e2863SAndrew Thompson #define UHSO_MAX_MTU		2048
113941e2863SAndrew Thompson 
114941e2863SAndrew Thompson /*
115941e2863SAndrew Thompson  * There are mainly two type of cards floating around.
116941e2863SAndrew Thompson  * The first one has 2,3 or 4 interfaces with a multiplexed serial port
117941e2863SAndrew Thompson  * and packet interface on the first interface and bulk serial ports
118941e2863SAndrew Thompson  * on the others.
119941e2863SAndrew Thompson  * The second type of card has several other interfaces, their purpose
120941e2863SAndrew Thompson  * can be detected during run-time.
121941e2863SAndrew Thompson  */
122941e2863SAndrew Thompson #define UHSO_IFACE_SPEC(usb_type, port, port_type) \
123941e2863SAndrew Thompson 	(((usb_type) << 24) | ((port) << 16) | (port_type))
124941e2863SAndrew Thompson 
125941e2863SAndrew Thompson #define UHSO_IFACE_USB_TYPE(x) ((x >> 24) & 0xff)
126941e2863SAndrew Thompson #define UHSO_IFACE_PORT(x) ((x >> 16) & 0xff)
127941e2863SAndrew Thompson #define UHSO_IFACE_PORT_TYPE(x) (x & 0xff)
128941e2863SAndrew Thompson 
129941e2863SAndrew Thompson /*
130941e2863SAndrew Thompson  * USB interface types
131941e2863SAndrew Thompson  */
132941e2863SAndrew Thompson #define UHSO_IF_NET		0x01	/* Network packet interface */
133941e2863SAndrew Thompson #define UHSO_IF_MUX		0x02	/* Multiplexed serial port */
134941e2863SAndrew Thompson #define UHSO_IF_BULK		0x04	/* Bulk interface */
135941e2863SAndrew Thompson 
136941e2863SAndrew Thompson /*
137941e2863SAndrew Thompson  * Port types
138941e2863SAndrew Thompson  */
139941e2863SAndrew Thompson #define UHSO_PORT_UNKNOWN	0x00
140941e2863SAndrew Thompson #define UHSO_PORT_SERIAL	0x01	/* Serial port */
141941e2863SAndrew Thompson #define UHSO_PORT_NETWORK	0x02	/* Network packet interface */
142941e2863SAndrew Thompson 
143941e2863SAndrew Thompson /*
144941e2863SAndrew Thompson  * Multiplexed serial port destination sub-port names
145941e2863SAndrew Thompson  */
146941e2863SAndrew Thompson #define UHSO_MPORT_TYPE_CTL	0x00	/* Control port */
147941e2863SAndrew Thompson #define UHSO_MPORT_TYPE_APP	0x01	/* Application */
148941e2863SAndrew Thompson #define UHSO_MPORT_TYPE_PCSC	0x02
149941e2863SAndrew Thompson #define UHSO_MPORT_TYPE_GPS	0x03
1509b6ffc1fSAndrew Thompson #define UHSO_MPORT_TYPE_APP2	0x04	/* Secondary application */
151941e2863SAndrew Thompson #define UHSO_MPORT_TYPE_MAX	UHSO_MPORT_TYPE_APP2
152941e2863SAndrew Thompson #define UHSO_MPORT_TYPE_NOMAX	8	/* Max number of mux ports */
153941e2863SAndrew Thompson 
154941e2863SAndrew Thompson /*
155941e2863SAndrew Thompson  * Port definitions
156f644abcfSAndrew Thompson  * Note that these definitions are arbitrary and do not match the values
157f644abcfSAndrew Thompson  * returned by the auto config descriptor.
158941e2863SAndrew Thompson  */
15921a9d6e7SAndrew Thompson #define UHSO_PORT_TYPE_UNKNOWN	0x00
160941e2863SAndrew Thompson #define UHSO_PORT_TYPE_CTL	0x01
161941e2863SAndrew Thompson #define UHSO_PORT_TYPE_APP	0x02
162941e2863SAndrew Thompson #define UHSO_PORT_TYPE_APP2	0x03
163941e2863SAndrew Thompson #define UHSO_PORT_TYPE_MODEM	0x04
164941e2863SAndrew Thompson #define UHSO_PORT_TYPE_NETWORK	0x05
165941e2863SAndrew Thompson #define UHSO_PORT_TYPE_DIAG	0x06
166941e2863SAndrew Thompson #define UHSO_PORT_TYPE_DIAG2	0x07
167941e2863SAndrew Thompson #define UHSO_PORT_TYPE_GPS	0x08
168941e2863SAndrew Thompson #define UHSO_PORT_TYPE_GPSCTL	0x09
169941e2863SAndrew Thompson #define UHSO_PORT_TYPE_PCSC	0x0a
170941e2863SAndrew Thompson #define UHSO_PORT_TYPE_MSD	0x0b
171941e2863SAndrew Thompson #define UHSO_PORT_TYPE_VOICE	0x0c
172941e2863SAndrew Thompson #define UHSO_PORT_TYPE_MAX	0x0c
173941e2863SAndrew Thompson 
174941e2863SAndrew Thompson static eventhandler_tag uhso_etag;
175941e2863SAndrew Thompson 
176941e2863SAndrew Thompson /* Overall port type */
177941e2863SAndrew Thompson static char *uhso_port[] = {
178941e2863SAndrew Thompson 	"Unknown",
179941e2863SAndrew Thompson 	"Serial",
180941e2863SAndrew Thompson 	"Network",
181941e2863SAndrew Thompson 	"Network/Serial"
182941e2863SAndrew Thompson };
183941e2863SAndrew Thompson 
1849b6ffc1fSAndrew Thompson /*
1859b6ffc1fSAndrew Thompson  * Map between interface port type read from device and description type.
1869b6ffc1fSAndrew Thompson  * The position in this array is a direct map to the auto config
1879b6ffc1fSAndrew Thompson  * descriptor values.
1889b6ffc1fSAndrew Thompson  */
1899b6ffc1fSAndrew Thompson static unsigned char uhso_port_map[] = {
19021a9d6e7SAndrew Thompson 	UHSO_PORT_TYPE_UNKNOWN,
191941e2863SAndrew Thompson 	UHSO_PORT_TYPE_DIAG,
192941e2863SAndrew Thompson 	UHSO_PORT_TYPE_GPS,
193941e2863SAndrew Thompson 	UHSO_PORT_TYPE_GPSCTL,
194941e2863SAndrew Thompson 	UHSO_PORT_TYPE_APP,
195941e2863SAndrew Thompson 	UHSO_PORT_TYPE_APP2,
196941e2863SAndrew Thompson 	UHSO_PORT_TYPE_CTL,
197941e2863SAndrew Thompson 	UHSO_PORT_TYPE_NETWORK,
198941e2863SAndrew Thompson 	UHSO_PORT_TYPE_MODEM,
199941e2863SAndrew Thompson 	UHSO_PORT_TYPE_MSD,
200941e2863SAndrew Thompson 	UHSO_PORT_TYPE_PCSC,
201941e2863SAndrew Thompson 	UHSO_PORT_TYPE_VOICE
202941e2863SAndrew Thompson };
203941e2863SAndrew Thompson static char uhso_port_map_max = sizeof(uhso_port_map) / sizeof(char);
204941e2863SAndrew Thompson 
2059b6ffc1fSAndrew Thompson static unsigned char uhso_mux_port_map[] = {
206941e2863SAndrew Thompson 	UHSO_PORT_TYPE_CTL,
207941e2863SAndrew Thompson 	UHSO_PORT_TYPE_APP,
208941e2863SAndrew Thompson 	UHSO_PORT_TYPE_PCSC,
209941e2863SAndrew Thompson 	UHSO_PORT_TYPE_GPS,
210941e2863SAndrew Thompson 	UHSO_PORT_TYPE_APP2
211941e2863SAndrew Thompson };
212941e2863SAndrew Thompson 
213941e2863SAndrew Thompson static char *uhso_port_type[] = {
2149b6ffc1fSAndrew Thompson 	"Unknown",  /* Not a valid port */
215941e2863SAndrew Thompson 	"Control",
216941e2863SAndrew Thompson 	"Application",
217941e2863SAndrew Thompson 	"Application (Secondary)",
218941e2863SAndrew Thompson 	"Modem",
219941e2863SAndrew Thompson 	"Network",
220941e2863SAndrew Thompson 	"Diagnostic",
221941e2863SAndrew Thompson 	"Diagnostic (Secondary)",
222941e2863SAndrew Thompson 	"GPS",
223941e2863SAndrew Thompson 	"GPS Control",
224941e2863SAndrew Thompson 	"PC Smartcard",
225941e2863SAndrew Thompson 	"MSD",
226941e2863SAndrew Thompson 	"Voice",
227941e2863SAndrew Thompson };
228941e2863SAndrew Thompson 
229941e2863SAndrew Thompson static char *uhso_port_type_sysctl[] = {
230941e2863SAndrew Thompson 	"unknown",
231941e2863SAndrew Thompson 	"control",
232941e2863SAndrew Thompson 	"application",
233941e2863SAndrew Thompson 	"application",
234941e2863SAndrew Thompson 	"modem",
235941e2863SAndrew Thompson 	"network",
236941e2863SAndrew Thompson 	"diagnostic",
237941e2863SAndrew Thompson 	"diagnostic",
238941e2863SAndrew Thompson 	"gps",
239941e2863SAndrew Thompson 	"gps_control",
240941e2863SAndrew Thompson 	"pcsc",
241941e2863SAndrew Thompson 	"msd",
242941e2863SAndrew Thompson 	"voice",
243941e2863SAndrew Thompson };
244941e2863SAndrew Thompson 
245941e2863SAndrew Thompson #define UHSO_STATIC_IFACE	0x01
246941e2863SAndrew Thompson #define UHSO_AUTO_IFACE		0x02
247941e2863SAndrew Thompson 
24821a9d6e7SAndrew Thompson /* ifnet device unit allocations */
24921a9d6e7SAndrew Thompson static struct unrhdr *uhso_ifnet_unit = NULL;
25021a9d6e7SAndrew Thompson 
251f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID uhso_devs[] = {
252941e2863SAndrew Thompson #define	UHSO_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) }
2530f92e529SHans Petter Selasky 	/* Option GlobeTrotter MAX 7.2 with upgraded firmware */
2540f92e529SHans Petter Selasky 	UHSO_DEV(OPTION, GTMAX72, UHSO_STATIC_IFACE),
255941e2863SAndrew Thompson 	/* Option GlobeSurfer iCON 7.2 */
256941e2863SAndrew Thompson 	UHSO_DEV(OPTION, GSICON72, UHSO_STATIC_IFACE),
257941e2863SAndrew Thompson 	/* Option iCON 225 */
258941e2863SAndrew Thompson 	UHSO_DEV(OPTION, GTHSDPA, UHSO_STATIC_IFACE),
259941e2863SAndrew Thompson 	/* Option GlobeSurfer iCON HSUPA */
260941e2863SAndrew Thompson 	UHSO_DEV(OPTION, GSICONHSUPA, UHSO_STATIC_IFACE),
261941e2863SAndrew Thompson 	/* Option GlobeTrotter HSUPA */
262941e2863SAndrew Thompson 	UHSO_DEV(OPTION, GTHSUPA, UHSO_STATIC_IFACE),
263941e2863SAndrew Thompson 	/* GE40x */
264941e2863SAndrew Thompson 	UHSO_DEV(OPTION, GE40X, UHSO_AUTO_IFACE),
265941e2863SAndrew Thompson 	UHSO_DEV(OPTION, GE40X_1, UHSO_AUTO_IFACE),
266941e2863SAndrew Thompson 	UHSO_DEV(OPTION, GE40X_2, UHSO_AUTO_IFACE),
267941e2863SAndrew Thompson 	UHSO_DEV(OPTION, GE40X_3, UHSO_AUTO_IFACE),
268941e2863SAndrew Thompson 	/* Option GlobeSurfer iCON 401 */
269941e2863SAndrew Thompson 	UHSO_DEV(OPTION, ICON401, UHSO_AUTO_IFACE),
270941e2863SAndrew Thompson 	/* Option GlobeTrotter Module 382 */
271941e2863SAndrew Thompson 	UHSO_DEV(OPTION, GMT382, UHSO_AUTO_IFACE),
2725199761bSHans Petter Selasky 	/* Option GTM661W */
2735199761bSHans Petter Selasky 	UHSO_DEV(OPTION, GTM661W, UHSO_AUTO_IFACE),
274941e2863SAndrew Thompson 	/* Option iCON EDGE */
275941e2863SAndrew Thompson 	UHSO_DEV(OPTION, ICONEDGE, UHSO_STATIC_IFACE),
276941e2863SAndrew Thompson 	/* Option Module HSxPA */
277941e2863SAndrew Thompson 	UHSO_DEV(OPTION, MODHSXPA, UHSO_STATIC_IFACE),
278941e2863SAndrew Thompson 	/* Option iCON 321 */
279941e2863SAndrew Thompson 	UHSO_DEV(OPTION, ICON321, UHSO_STATIC_IFACE),
280941e2863SAndrew Thompson 	/* Option iCON 322 */
2819b6ffc1fSAndrew Thompson 	UHSO_DEV(OPTION, GTICON322, UHSO_STATIC_IFACE),
2829b6ffc1fSAndrew Thompson 	/* Option iCON 505 */
2839b6ffc1fSAndrew Thompson 	UHSO_DEV(OPTION, ICON505, UHSO_AUTO_IFACE),
28421a9d6e7SAndrew Thompson 	/* Option iCON 452 */
28521a9d6e7SAndrew Thompson 	UHSO_DEV(OPTION, ICON505, UHSO_AUTO_IFACE),
286941e2863SAndrew Thompson #undef UHSO_DEV
287941e2863SAndrew Thompson };
288941e2863SAndrew Thompson 
289f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, uhso, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
290f8d2b1f3SPawel Biernacki     "USB uhso");
2919b6ffc1fSAndrew Thompson static int uhso_autoswitch = 1;
292ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_uhso, OID_AUTO, auto_switch, CTLFLAG_RWTUN,
2939b6ffc1fSAndrew Thompson     &uhso_autoswitch, 0, "Automatically switch to modem mode");
294941e2863SAndrew Thompson 
295941e2863SAndrew Thompson #ifdef USB_DEBUG
296941e2863SAndrew Thompson #ifdef UHSO_DEBUG
297941e2863SAndrew Thompson static int uhso_debug = UHSO_DEBUG;
298941e2863SAndrew Thompson #else
299941e2863SAndrew Thompson static int uhso_debug = -1;
300941e2863SAndrew Thompson #endif
301941e2863SAndrew Thompson 
302ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_uhso, OID_AUTO, debug, CTLFLAG_RWTUN,
303941e2863SAndrew Thompson     &uhso_debug, 0, "Debug level");
304941e2863SAndrew Thompson 
305941e2863SAndrew Thompson #define UHSO_DPRINTF(n, x, ...) {\
306941e2863SAndrew Thompson 	if (uhso_debug >= n) {\
307941e2863SAndrew Thompson 		printf("%s: " x, __func__, ##__VA_ARGS__);\
308941e2863SAndrew Thompson 	}\
309941e2863SAndrew Thompson }
310941e2863SAndrew Thompson #else
311941e2863SAndrew Thompson #define UHSO_DPRINTF(n, x, ...)
312941e2863SAndrew Thompson #endif
313941e2863SAndrew Thompson 
314941e2863SAndrew Thompson #ifdef UHSO_DEBUG_HEXDUMP
315941e2863SAndrew Thompson # define UHSO_HEXDUMP(_buf, _len) do { \
316941e2863SAndrew Thompson   { \
317941e2863SAndrew Thompson         size_t __tmp; \
318941e2863SAndrew Thompson         const char *__buf = (const char *)_buf; \
319941e2863SAndrew Thompson         for (__tmp = 0; __tmp < _len; __tmp++) \
320941e2863SAndrew Thompson                 printf("%02hhx ", *__buf++); \
321941e2863SAndrew Thompson     printf("\n"); \
322941e2863SAndrew Thompson   } \
323941e2863SAndrew Thompson } while(0)
324941e2863SAndrew Thompson #else
325941e2863SAndrew Thompson # define UHSO_HEXDUMP(_buf, _len)
326941e2863SAndrew Thompson #endif
327941e2863SAndrew Thompson 
328941e2863SAndrew Thompson enum {
329941e2863SAndrew Thompson 	UHSO_MUX_ENDPT_INTR = 0,
330941e2863SAndrew Thompson 	UHSO_MUX_ENDPT_MAX
331941e2863SAndrew Thompson };
332941e2863SAndrew Thompson 
333941e2863SAndrew Thompson enum {
334941e2863SAndrew Thompson 	UHSO_CTRL_READ = 0,
335941e2863SAndrew Thompson 	UHSO_CTRL_WRITE,
336941e2863SAndrew Thompson 	UHSO_CTRL_MAX
337941e2863SAndrew Thompson };
338941e2863SAndrew Thompson 
339941e2863SAndrew Thompson enum {
340941e2863SAndrew Thompson 	UHSO_IFNET_READ = 0,
341941e2863SAndrew Thompson 	UHSO_IFNET_WRITE,
342941e2863SAndrew Thompson 	UHSO_IFNET_MAX
343941e2863SAndrew Thompson };
344941e2863SAndrew Thompson 
345941e2863SAndrew Thompson enum {
346941e2863SAndrew Thompson 	UHSO_BULK_ENDPT_READ = 0,
347941e2863SAndrew Thompson 	UHSO_BULK_ENDPT_WRITE,
348941e2863SAndrew Thompson 	UHSO_BULK_ENDPT_INTR,
349941e2863SAndrew Thompson 	UHSO_BULK_ENDPT_MAX
350941e2863SAndrew Thompson };
351941e2863SAndrew Thompson 
352941e2863SAndrew Thompson static usb_callback_t uhso_mux_intr_callback;
353941e2863SAndrew Thompson static usb_callback_t uhso_mux_read_callback;
354941e2863SAndrew Thompson static usb_callback_t uhso_mux_write_callback;
355941e2863SAndrew Thompson static usb_callback_t uhso_bs_read_callback;
356941e2863SAndrew Thompson static usb_callback_t uhso_bs_write_callback;
357941e2863SAndrew Thompson static usb_callback_t uhso_bs_intr_callback;
358941e2863SAndrew Thompson static usb_callback_t uhso_ifnet_read_callback;
359941e2863SAndrew Thompson static usb_callback_t uhso_ifnet_write_callback;
360941e2863SAndrew Thompson 
3619b6ffc1fSAndrew Thompson /* Config used for the default control pipes */
362941e2863SAndrew Thompson static const struct usb_config uhso_ctrl_config[UHSO_CTRL_MAX] = {
363941e2863SAndrew Thompson 	[UHSO_CTRL_READ] = {
364941e2863SAndrew Thompson 		.type = UE_CONTROL,
365941e2863SAndrew Thompson 		.endpoint = 0x00,
366941e2863SAndrew Thompson 		.direction = UE_DIR_ANY,
367941e2863SAndrew Thompson 		.flags = { .pipe_bof = 1, .short_xfer_ok = 1 },
368941e2863SAndrew Thompson 		.bufsize = sizeof(struct usb_device_request) + 1024,
369941e2863SAndrew Thompson 		.callback = &uhso_mux_read_callback
370941e2863SAndrew Thompson 	},
371941e2863SAndrew Thompson 
372941e2863SAndrew Thompson 	[UHSO_CTRL_WRITE] = {
373941e2863SAndrew Thompson 		.type = UE_CONTROL,
374941e2863SAndrew Thompson 		.endpoint = 0x00,
375941e2863SAndrew Thompson 		.direction = UE_DIR_ANY,
376941e2863SAndrew Thompson 		.flags = { .pipe_bof = 1, .force_short_xfer = 1 },
377941e2863SAndrew Thompson 		.bufsize = sizeof(struct usb_device_request) + 1024,
378941e2863SAndrew Thompson 		.timeout = 1000,
379941e2863SAndrew Thompson 		.callback = &uhso_mux_write_callback
380941e2863SAndrew Thompson 	}
381941e2863SAndrew Thompson };
382941e2863SAndrew Thompson 
3839b6ffc1fSAndrew Thompson /* Config for the multiplexed serial ports */
384941e2863SAndrew Thompson static const struct usb_config uhso_mux_config[UHSO_MUX_ENDPT_MAX] = {
385941e2863SAndrew Thompson 	[UHSO_MUX_ENDPT_INTR] = {
386941e2863SAndrew Thompson 		.type = UE_INTERRUPT,
387941e2863SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
388941e2863SAndrew Thompson 		.direction = UE_DIR_IN,
389941e2863SAndrew Thompson 		.flags = { .short_xfer_ok = 1 },
390941e2863SAndrew Thompson 		.bufsize = 0,
391941e2863SAndrew Thompson 		.callback = &uhso_mux_intr_callback,
392941e2863SAndrew Thompson 	}
393941e2863SAndrew Thompson };
394941e2863SAndrew Thompson 
3959b6ffc1fSAndrew Thompson /* Config for the raw IP-packet interface */
396941e2863SAndrew Thompson static const struct usb_config uhso_ifnet_config[UHSO_IFNET_MAX] = {
397941e2863SAndrew Thompson 	[UHSO_IFNET_READ] = {
398941e2863SAndrew Thompson 		.type = UE_BULK,
399941e2863SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
400941e2863SAndrew Thompson 		.direction = UE_DIR_IN,
401941e2863SAndrew Thompson 		.flags = { .pipe_bof = 1, .short_xfer_ok = 1 },
402941e2863SAndrew Thompson 		.bufsize = MCLBYTES,
403941e2863SAndrew Thompson 		.callback = &uhso_ifnet_read_callback
404941e2863SAndrew Thompson 	},
405941e2863SAndrew Thompson 	[UHSO_IFNET_WRITE] = {
406941e2863SAndrew Thompson 		.type = UE_BULK,
407941e2863SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
408941e2863SAndrew Thompson 		.direction = UE_DIR_OUT,
409941e2863SAndrew Thompson 		.flags = { .pipe_bof = 1, .force_short_xfer = 1 },
410941e2863SAndrew Thompson 		.bufsize = MCLBYTES,
411941e2863SAndrew Thompson 		.timeout = 5 * USB_MS_HZ,
412941e2863SAndrew Thompson 		.callback = &uhso_ifnet_write_callback
413941e2863SAndrew Thompson 	}
414941e2863SAndrew Thompson };
415941e2863SAndrew Thompson 
4169b6ffc1fSAndrew Thompson /* Config for interfaces with normal bulk serial ports */
417941e2863SAndrew Thompson static const struct usb_config uhso_bs_config[UHSO_BULK_ENDPT_MAX] = {
418941e2863SAndrew Thompson 	[UHSO_BULK_ENDPT_READ] = {
419941e2863SAndrew Thompson 		.type = UE_BULK,
420941e2863SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
421941e2863SAndrew Thompson 		.direction = UE_DIR_IN,
422941e2863SAndrew Thompson 		.flags = { .pipe_bof = 1, .short_xfer_ok = 1 },
423941e2863SAndrew Thompson 		.bufsize = 4096,
424941e2863SAndrew Thompson 		.callback = &uhso_bs_read_callback
425941e2863SAndrew Thompson 	},
426941e2863SAndrew Thompson 
427941e2863SAndrew Thompson 	[UHSO_BULK_ENDPT_WRITE] = {
428941e2863SAndrew Thompson 		.type = UE_BULK,
429941e2863SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
430941e2863SAndrew Thompson 		.direction = UE_DIR_OUT,
431941e2863SAndrew Thompson 		.flags = { .pipe_bof = 1, .force_short_xfer = 1 },
432941e2863SAndrew Thompson 		.bufsize = 8192,
433941e2863SAndrew Thompson 		.callback = &uhso_bs_write_callback
434941e2863SAndrew Thompson 	},
435941e2863SAndrew Thompson 
436941e2863SAndrew Thompson 	[UHSO_BULK_ENDPT_INTR] = {
437941e2863SAndrew Thompson 		.type = UE_INTERRUPT,
438941e2863SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
439941e2863SAndrew Thompson 		.direction = UE_DIR_IN,
440941e2863SAndrew Thompson 		.flags = { .short_xfer_ok = 1 },
441941e2863SAndrew Thompson 		.bufsize = 0,
442941e2863SAndrew Thompson 		.callback = &uhso_bs_intr_callback,
443941e2863SAndrew Thompson 	}
444941e2863SAndrew Thompson };
445941e2863SAndrew Thompson 
446941e2863SAndrew Thompson static int  uhso_probe_iface(struct uhso_softc *, int,
44721a9d6e7SAndrew Thompson     int (*probe)(struct usb_device *, int));
44821a9d6e7SAndrew Thompson static int  uhso_probe_iface_auto(struct usb_device *, int);
44921a9d6e7SAndrew Thompson static int  uhso_probe_iface_static(struct usb_device *, int);
450941e2863SAndrew Thompson static int  uhso_attach_muxserial(struct uhso_softc *, struct usb_interface *,
451941e2863SAndrew Thompson     int type);
452941e2863SAndrew Thompson static int  uhso_attach_bulkserial(struct uhso_softc *, struct usb_interface *,
453941e2863SAndrew Thompson     int type);
454941e2863SAndrew Thompson static int  uhso_attach_ifnet(struct uhso_softc *, struct usb_interface *,
455941e2863SAndrew Thompson     int type);
456941e2863SAndrew Thompson static void uhso_test_autoinst(void *, struct usb_device *,
457941e2863SAndrew Thompson 		struct usb_attach_arg *);
458941e2863SAndrew Thompson static int  uhso_driver_loaded(struct module *, int, void *);
45921a9d6e7SAndrew Thompson static int uhso_radio_sysctl(SYSCTL_HANDLER_ARGS);
46021a9d6e7SAndrew Thompson static int uhso_radio_ctrl(struct uhso_softc *, int);
461941e2863SAndrew Thompson 
4625805d178SHans Petter Selasky static void uhso_free(struct ucom_softc *);
463941e2863SAndrew Thompson static void uhso_ucom_start_read(struct ucom_softc *);
464941e2863SAndrew Thompson static void uhso_ucom_stop_read(struct ucom_softc *);
465941e2863SAndrew Thompson static void uhso_ucom_start_write(struct ucom_softc *);
466941e2863SAndrew Thompson static void uhso_ucom_stop_write(struct ucom_softc *);
467941e2863SAndrew Thompson static void uhso_ucom_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *);
468941e2863SAndrew Thompson static void uhso_ucom_cfg_set_dtr(struct ucom_softc *, uint8_t);
469941e2863SAndrew Thompson static void uhso_ucom_cfg_set_rts(struct ucom_softc *, uint8_t);
470941e2863SAndrew Thompson static void uhso_if_init(void *);
471935b194dSJustin Hibbits static void uhso_if_start(if_t);
472941e2863SAndrew Thompson static void uhso_if_stop(struct uhso_softc *);
473935b194dSJustin Hibbits static int  uhso_if_ioctl(if_t, u_long, caddr_t);
474935b194dSJustin Hibbits static int  uhso_if_output(if_t, struct mbuf *,
47547e8d432SGleb Smirnoff     const struct sockaddr *, struct route *);
476941e2863SAndrew Thompson static void uhso_if_rxflush(void *);
477941e2863SAndrew Thompson 
478941e2863SAndrew Thompson static device_probe_t uhso_probe;
479941e2863SAndrew Thompson static device_attach_t uhso_attach;
480941e2863SAndrew Thompson static device_detach_t uhso_detach;
481c01fc06eSHans Petter Selasky static void uhso_free_softc(struct uhso_softc *);
482941e2863SAndrew Thompson 
483941e2863SAndrew Thompson static device_method_t uhso_methods[] = {
484941e2863SAndrew Thompson 	DEVMETHOD(device_probe,		uhso_probe),
485941e2863SAndrew Thompson 	DEVMETHOD(device_attach,	uhso_attach),
486941e2863SAndrew Thompson 	DEVMETHOD(device_detach,	uhso_detach),
487941e2863SAndrew Thompson 	{ 0, 0 }
488941e2863SAndrew Thompson };
489941e2863SAndrew Thompson 
490941e2863SAndrew Thompson static driver_t uhso_driver = {
4916d917491SHans Petter Selasky 	.name = "uhso",
4926d917491SHans Petter Selasky 	.methods = uhso_methods,
4936d917491SHans Petter Selasky 	.size = sizeof(struct uhso_softc)
494941e2863SAndrew Thompson };
495941e2863SAndrew Thompson 
496bc9372d7SJohn Baldwin DRIVER_MODULE(uhso, uhub, uhso_driver, uhso_driver_loaded, NULL);
497941e2863SAndrew Thompson MODULE_DEPEND(uhso, ucom, 1, 1, 1);
498941e2863SAndrew Thompson MODULE_DEPEND(uhso, usb, 1, 1, 1);
499941e2863SAndrew Thompson MODULE_VERSION(uhso, 1);
500f809f280SWarner Losh USB_PNP_HOST_INFO(uhso_devs);
501941e2863SAndrew Thompson 
502941e2863SAndrew Thompson static struct ucom_callback uhso_ucom_callback = {
503941e2863SAndrew Thompson 	.ucom_cfg_get_status = &uhso_ucom_cfg_get_status,
504941e2863SAndrew Thompson 	.ucom_cfg_set_dtr = &uhso_ucom_cfg_set_dtr,
505941e2863SAndrew Thompson 	.ucom_cfg_set_rts = &uhso_ucom_cfg_set_rts,
506941e2863SAndrew Thompson 	.ucom_start_read = uhso_ucom_start_read,
507941e2863SAndrew Thompson 	.ucom_stop_read = uhso_ucom_stop_read,
508941e2863SAndrew Thompson 	.ucom_start_write = uhso_ucom_start_write,
5095805d178SHans Petter Selasky 	.ucom_stop_write = uhso_ucom_stop_write,
5105805d178SHans Petter Selasky 	.ucom_free = &uhso_free,
511941e2863SAndrew Thompson };
512941e2863SAndrew Thompson 
513941e2863SAndrew Thompson static int
uhso_probe(device_t self)514941e2863SAndrew Thompson uhso_probe(device_t self)
515941e2863SAndrew Thompson {
516941e2863SAndrew Thompson 	struct usb_attach_arg *uaa = device_get_ivars(self);
51721a9d6e7SAndrew Thompson 	int error;
518941e2863SAndrew Thompson 
519941e2863SAndrew Thompson 	if (uaa->usb_mode != USB_MODE_HOST)
520941e2863SAndrew Thompson 		return (ENXIO);
521941e2863SAndrew Thompson 	if (uaa->info.bConfigIndex != 0)
522941e2863SAndrew Thompson 		return (ENXIO);
523ae538d85SAndrew Thompson 	if (uaa->info.bDeviceClass != 0xff)
524941e2863SAndrew Thompson 		return (ENXIO);
525941e2863SAndrew Thompson 
52621a9d6e7SAndrew Thompson 	error = usbd_lookup_id_by_uaa(uhso_devs, sizeof(uhso_devs), uaa);
52721a9d6e7SAndrew Thompson 	if (error != 0)
52821a9d6e7SAndrew Thompson 		return (error);
52921a9d6e7SAndrew Thompson 
53021a9d6e7SAndrew Thompson 	/*
53121a9d6e7SAndrew Thompson 	 * Probe device to see if we are able to attach
53221a9d6e7SAndrew Thompson 	 * to this interface or not.
53321a9d6e7SAndrew Thompson 	 */
53421a9d6e7SAndrew Thompson 	if (USB_GET_DRIVER_INFO(uaa) == UHSO_AUTO_IFACE) {
53521a9d6e7SAndrew Thompson 		if (uhso_probe_iface_auto(uaa->device,
53621a9d6e7SAndrew Thompson 		    uaa->info.bIfaceNum) == 0)
53721a9d6e7SAndrew Thompson 			return (ENXIO);
53821a9d6e7SAndrew Thompson 	}
53921a9d6e7SAndrew Thompson 	return (error);
540941e2863SAndrew Thompson }
541941e2863SAndrew Thompson 
542941e2863SAndrew Thompson static int
uhso_attach(device_t self)543941e2863SAndrew Thompson uhso_attach(device_t self)
544941e2863SAndrew Thompson {
545941e2863SAndrew Thompson 	struct uhso_softc *sc = device_get_softc(self);
546941e2863SAndrew Thompson 	struct usb_attach_arg *uaa = device_get_ivars(self);
547941e2863SAndrew Thompson 	struct usb_interface_descriptor *id;
548941e2863SAndrew Thompson 	struct sysctl_ctx_list *sctx;
549941e2863SAndrew Thompson 	struct sysctl_oid *soid;
55021a9d6e7SAndrew Thompson 	struct sysctl_oid *tree = NULL, *tty_node;
551941e2863SAndrew Thompson 	struct ucom_softc *ucom;
552941e2863SAndrew Thompson 	struct uhso_tty *ht;
553941e2863SAndrew Thompson 	int i, error, port;
554941e2863SAndrew Thompson 	void *probe_f;
555941e2863SAndrew Thompson 	usb_error_t uerr;
556941e2863SAndrew Thompson 	char *desc;
557941e2863SAndrew Thompson 
558941e2863SAndrew Thompson 	sc->sc_dev = self;
559941e2863SAndrew Thompson 	sc->sc_udev = uaa->device;
560941e2863SAndrew Thompson 	mtx_init(&sc->sc_mtx, "uhso", NULL, MTX_DEF);
56132c4f3bbSGleb Smirnoff 	mbufq_init(&sc->sc_rxq, INT_MAX);	/* XXXGL: sane maximum */
5625805d178SHans Petter Selasky 	ucom_ref(&sc->sc_super_ucom);
563941e2863SAndrew Thompson 
56421a9d6e7SAndrew Thompson 	sc->sc_radio = 1;
565941e2863SAndrew Thompson 
566941e2863SAndrew Thompson 	id = usbd_get_interface_descriptor(uaa->iface);
567941e2863SAndrew Thompson 	sc->sc_ctrl_iface_no = id->bInterfaceNumber;
568941e2863SAndrew Thompson 
569941e2863SAndrew Thompson 	sc->sc_iface_no = uaa->info.bIfaceNum;
570941e2863SAndrew Thompson 	sc->sc_iface_index = uaa->info.bIfaceIndex;
571941e2863SAndrew Thompson 
572941e2863SAndrew Thompson 	/* Setup control pipe */
573941e2863SAndrew Thompson 	uerr = usbd_transfer_setup(uaa->device,
574941e2863SAndrew Thompson 	    &sc->sc_iface_index, sc->sc_ctrl_xfer,
575941e2863SAndrew Thompson 	    uhso_ctrl_config, UHSO_CTRL_MAX, sc, &sc->sc_mtx);
576941e2863SAndrew Thompson 	if (uerr) {
577941e2863SAndrew Thompson 		device_printf(self, "Failed to setup control pipe: %s\n",
578941e2863SAndrew Thompson 		    usbd_errstr(uerr));
579941e2863SAndrew Thompson 		goto out;
580941e2863SAndrew Thompson 	}
581941e2863SAndrew Thompson 
582941e2863SAndrew Thompson 	if (USB_GET_DRIVER_INFO(uaa) == UHSO_STATIC_IFACE)
583941e2863SAndrew Thompson 		probe_f = uhso_probe_iface_static;
584941e2863SAndrew Thompson 	else if (USB_GET_DRIVER_INFO(uaa) == UHSO_AUTO_IFACE)
585941e2863SAndrew Thompson 		probe_f = uhso_probe_iface_auto;
586941e2863SAndrew Thompson 	else
587941e2863SAndrew Thompson 		goto out;
588941e2863SAndrew Thompson 
589941e2863SAndrew Thompson 	error = uhso_probe_iface(sc, uaa->info.bIfaceNum, probe_f);
590941e2863SAndrew Thompson 	if (error != 0)
591941e2863SAndrew Thompson 		goto out;
592941e2863SAndrew Thompson 
593941e2863SAndrew Thompson 	sctx = device_get_sysctl_ctx(sc->sc_dev);
594941e2863SAndrew Thompson 	soid = device_get_sysctl_tree(sc->sc_dev);
595941e2863SAndrew Thompson 
596941e2863SAndrew Thompson 	SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "type",
597941e2863SAndrew Thompson 	    CTLFLAG_RD, uhso_port[UHSO_IFACE_PORT(sc->sc_type)], 0,
598941e2863SAndrew Thompson 	    "Port available at this interface");
59921a9d6e7SAndrew Thompson 	SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "radio",
600f8d2b1f3SPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, sc, 0,
601f8d2b1f3SPawel Biernacki 	    uhso_radio_sysctl, "I", "Enable radio");
602941e2863SAndrew Thompson 
6039b6ffc1fSAndrew Thompson 	/*
604f644abcfSAndrew Thompson 	 * The default interface description on most Option devices isn't
6059b6ffc1fSAndrew Thompson 	 * very helpful. So we skip device_set_usb_desc and set the
6069b6ffc1fSAndrew Thompson 	 * device description manually.
6079b6ffc1fSAndrew Thompson 	 */
6089b6ffc1fSAndrew Thompson 	device_set_desc_copy(self, uhso_port_type[UHSO_IFACE_PORT_TYPE(sc->sc_type)]);
6099b6ffc1fSAndrew Thompson 	/* Announce device */
6109b6ffc1fSAndrew Thompson 	device_printf(self, "<%s port> at <%s %s> on %s\n",
6119b6ffc1fSAndrew Thompson 	    uhso_port_type[UHSO_IFACE_PORT_TYPE(sc->sc_type)],
612ae538d85SAndrew Thompson 	    usb_get_manufacturer(uaa->device),
613ae538d85SAndrew Thompson 	    usb_get_product(uaa->device),
614ae538d85SAndrew Thompson 	    device_get_nameunit(device_get_parent(self)));
6159b6ffc1fSAndrew Thompson 
616941e2863SAndrew Thompson 	if (sc->sc_ttys > 0) {
617941e2863SAndrew Thompson 		SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "ports",
618941e2863SAndrew Thompson 		    CTLFLAG_RD, &sc->sc_ttys, 0, "Number of attached serial ports");
619941e2863SAndrew Thompson 
620941e2863SAndrew Thompson 		tree = SYSCTL_ADD_NODE(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
621f8d2b1f3SPawel Biernacki 		    "port", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Serial ports");
622941e2863SAndrew Thompson 	}
623941e2863SAndrew Thompson 
6249b6ffc1fSAndrew Thompson 	/*
6259b6ffc1fSAndrew Thompson 	 * Loop through the number of found TTYs and create sysctl
6269b6ffc1fSAndrew Thompson 	 * nodes for them.
6279b6ffc1fSAndrew Thompson 	 */
628941e2863SAndrew Thompson 	for (i = 0; i < sc->sc_ttys; i++) {
629941e2863SAndrew Thompson 		ht = &sc->sc_tty[i];
630941e2863SAndrew Thompson 		ucom = &sc->sc_ucom[i];
631941e2863SAndrew Thompson 
632941e2863SAndrew Thompson 		if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_MUX)
633941e2863SAndrew Thompson 			port = uhso_mux_port_map[ht->ht_muxport];
634941e2863SAndrew Thompson 		else
635941e2863SAndrew Thompson 			port = UHSO_IFACE_PORT_TYPE(sc->sc_type);
636941e2863SAndrew Thompson 
637941e2863SAndrew Thompson 		desc = uhso_port_type_sysctl[port];
638941e2863SAndrew Thompson 
639941e2863SAndrew Thompson 		tty_node = SYSCTL_ADD_NODE(sctx, SYSCTL_CHILDREN(tree), OID_AUTO,
640f8d2b1f3SPawel Biernacki 		    desc, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "");
641941e2863SAndrew Thompson 
642941e2863SAndrew Thompson 		ht->ht_name[0] = 0;
643941e2863SAndrew Thompson 		if (sc->sc_ttys == 1)
644015bb88fSNick Hibma 			snprintf(ht->ht_name, 32, "cuaU%d", ucom->sc_super->sc_unit);
645941e2863SAndrew Thompson 		else {
646941e2863SAndrew Thompson 			snprintf(ht->ht_name, 32, "cuaU%d.%d",
647015bb88fSNick Hibma 			    ucom->sc_super->sc_unit, ucom->sc_subunit);
648941e2863SAndrew Thompson 		}
649941e2863SAndrew Thompson 
650941e2863SAndrew Thompson 		desc = uhso_port_type[port];
651941e2863SAndrew Thompson 		SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(tty_node), OID_AUTO,
652941e2863SAndrew Thompson 		    "tty", CTLFLAG_RD, ht->ht_name, 0, "");
653941e2863SAndrew Thompson 		SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(tty_node), OID_AUTO,
654941e2863SAndrew Thompson 		    "desc", CTLFLAG_RD, desc, 0, "");
655941e2863SAndrew Thompson 
656941e2863SAndrew Thompson 		if (bootverbose)
657941e2863SAndrew Thompson 			device_printf(sc->sc_dev,
658941e2863SAndrew Thompson 			    "\"%s\" port at %s\n", desc, ht->ht_name);
659941e2863SAndrew Thompson 	}
660941e2863SAndrew Thompson 
661941e2863SAndrew Thompson 	return (0);
662941e2863SAndrew Thompson out:
663941e2863SAndrew Thompson 	uhso_detach(sc->sc_dev);
664941e2863SAndrew Thompson 	return (ENXIO);
665941e2863SAndrew Thompson }
666941e2863SAndrew Thompson 
667941e2863SAndrew Thompson static int
uhso_detach(device_t self)668941e2863SAndrew Thompson uhso_detach(device_t self)
669941e2863SAndrew Thompson {
670941e2863SAndrew Thompson 	struct uhso_softc *sc = device_get_softc(self);
671941e2863SAndrew Thompson 	int i;
672941e2863SAndrew Thompson 
673941e2863SAndrew Thompson 	usbd_transfer_unsetup(sc->sc_xfer, 3);
674941e2863SAndrew Thompson 	usbd_transfer_unsetup(sc->sc_ctrl_xfer, UHSO_CTRL_MAX);
675941e2863SAndrew Thompson 	if (sc->sc_ttys > 0) {
676015bb88fSNick Hibma 		ucom_detach(&sc->sc_super_ucom, sc->sc_ucom);
677941e2863SAndrew Thompson 
678941e2863SAndrew Thompson 		for (i = 0; i < sc->sc_ttys; i++) {
679941e2863SAndrew Thompson 			if (sc->sc_tty[i].ht_muxport != -1) {
680941e2863SAndrew Thompson 				usbd_transfer_unsetup(sc->sc_tty[i].ht_xfer,
681941e2863SAndrew Thompson 				    UHSO_CTRL_MAX);
682941e2863SAndrew Thompson 			}
683941e2863SAndrew Thompson 		}
684941e2863SAndrew Thompson 	}
685941e2863SAndrew Thompson 
686941e2863SAndrew Thompson 	if (sc->sc_ifp != NULL) {
687941e2863SAndrew Thompson 		callout_drain(&sc->sc_c);
688935b194dSJustin Hibbits 		free_unr(uhso_ifnet_unit, if_getdunit(sc->sc_ifp));
689941e2863SAndrew Thompson 		mtx_lock(&sc->sc_mtx);
690941e2863SAndrew Thompson 		uhso_if_stop(sc);
6914eac63afSHans Petter Selasky 		mtx_unlock(&sc->sc_mtx);
692941e2863SAndrew Thompson 		bpfdetach(sc->sc_ifp);
693941e2863SAndrew Thompson 		if_detach(sc->sc_ifp);
694941e2863SAndrew Thompson 		if_free(sc->sc_ifp);
695941e2863SAndrew Thompson 		usbd_transfer_unsetup(sc->sc_if_xfer, UHSO_IFNET_MAX);
696941e2863SAndrew Thompson 	}
697941e2863SAndrew Thompson 
698c01fc06eSHans Petter Selasky 	device_claim_softc(self);
699c01fc06eSHans Petter Selasky 
700c01fc06eSHans Petter Selasky 	uhso_free_softc(sc);
701c01fc06eSHans Petter Selasky 
702941e2863SAndrew Thompson 	return (0);
703941e2863SAndrew Thompson }
704941e2863SAndrew Thompson 
7055805d178SHans Petter Selasky UCOM_UNLOAD_DRAIN(uhso);
7065805d178SHans Petter Selasky 
7075805d178SHans Petter Selasky static void
uhso_free_softc(struct uhso_softc * sc)708c01fc06eSHans Petter Selasky uhso_free_softc(struct uhso_softc *sc)
7095805d178SHans Petter Selasky {
7105805d178SHans Petter Selasky 	if (ucom_unref(&sc->sc_super_ucom)) {
71131136808SHans Petter Selasky 		free(sc->sc_tty, M_USBDEV);
71231136808SHans Petter Selasky 		free(sc->sc_ucom, M_USBDEV);
7135805d178SHans Petter Selasky 		mtx_destroy(&sc->sc_mtx);
714c01fc06eSHans Petter Selasky 		device_free_softc(sc);
7155805d178SHans Petter Selasky 	}
7165805d178SHans Petter Selasky }
7175805d178SHans Petter Selasky 
7185805d178SHans Petter Selasky static void
uhso_free(struct ucom_softc * ucom)7195805d178SHans Petter Selasky uhso_free(struct ucom_softc *ucom)
7205805d178SHans Petter Selasky {
721c01fc06eSHans Petter Selasky 	uhso_free_softc(ucom->sc_parent);
7225805d178SHans Petter Selasky }
7235805d178SHans Petter Selasky 
724941e2863SAndrew Thompson static void
uhso_test_autoinst(void * arg,struct usb_device * udev,struct usb_attach_arg * uaa)725941e2863SAndrew Thompson uhso_test_autoinst(void *arg, struct usb_device *udev,
726941e2863SAndrew Thompson     struct usb_attach_arg *uaa)
727941e2863SAndrew Thompson {
728941e2863SAndrew Thompson 	struct usb_interface *iface;
729941e2863SAndrew Thompson 	struct usb_interface_descriptor *id;
730941e2863SAndrew Thompson 
7319b6ffc1fSAndrew Thompson 	if (uaa->dev_state != UAA_DEV_READY || !uhso_autoswitch)
732941e2863SAndrew Thompson 		return;
733941e2863SAndrew Thompson 
734941e2863SAndrew Thompson 	iface = usbd_get_iface(udev, 0);
735941e2863SAndrew Thompson 	if (iface == NULL)
736941e2863SAndrew Thompson 		return;
737941e2863SAndrew Thompson 	id = iface->idesc;
738941e2863SAndrew Thompson 	if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
739941e2863SAndrew Thompson 		return;
740941e2863SAndrew Thompson 	if (usbd_lookup_id_by_uaa(uhso_devs, sizeof(uhso_devs), uaa))
741941e2863SAndrew Thompson 		return;		/* no device match */
742941e2863SAndrew Thompson 
743941e2863SAndrew Thompson 	if (usb_msc_eject(udev, 0, MSC_EJECT_REZERO) == 0) {
744941e2863SAndrew Thompson 		/* success, mark the udev as disappearing */
745941e2863SAndrew Thompson 		uaa->dev_state = UAA_DEV_EJECTING;
746941e2863SAndrew Thompson 	}
747941e2863SAndrew Thompson }
748941e2863SAndrew Thompson 
749941e2863SAndrew Thompson static int
uhso_driver_loaded(struct module * mod,int what,void * arg)750941e2863SAndrew Thompson uhso_driver_loaded(struct module *mod, int what, void *arg)
751941e2863SAndrew Thompson {
752941e2863SAndrew Thompson 	switch (what) {
753941e2863SAndrew Thompson 	case MOD_LOAD:
754941e2863SAndrew Thompson 		/* register our autoinstall handler */
755941e2863SAndrew Thompson 		uhso_etag = EVENTHANDLER_REGISTER(usb_dev_configured,
756941e2863SAndrew Thompson 		    uhso_test_autoinst, NULL, EVENTHANDLER_PRI_ANY);
75721a9d6e7SAndrew Thompson 		/* create our unit allocator for inet devs */
75821a9d6e7SAndrew Thompson 		uhso_ifnet_unit = new_unrhdr(0, INT_MAX, NULL);
759941e2863SAndrew Thompson 		break;
760941e2863SAndrew Thompson 	case MOD_UNLOAD:
761941e2863SAndrew Thompson 		EVENTHANDLER_DEREGISTER(usb_dev_configured, uhso_etag);
76221a9d6e7SAndrew Thompson 		delete_unrhdr(uhso_ifnet_unit);
763941e2863SAndrew Thompson 		break;
764941e2863SAndrew Thompson 	default:
765941e2863SAndrew Thompson 		return (EOPNOTSUPP);
766941e2863SAndrew Thompson 	}
767941e2863SAndrew Thompson 	return (0);
768941e2863SAndrew Thompson }
769941e2863SAndrew Thompson 
7709b6ffc1fSAndrew Thompson /*
7719b6ffc1fSAndrew Thompson  * Probe the interface type by querying the device. The elements
7729b6ffc1fSAndrew Thompson  * of an array indicates the capabilities of a particular interface.
7739b6ffc1fSAndrew Thompson  * Returns a bit mask with the interface capabilities.
7749b6ffc1fSAndrew Thompson  */
7759b6ffc1fSAndrew Thompson static int
uhso_probe_iface_auto(struct usb_device * udev,int index)77621a9d6e7SAndrew Thompson uhso_probe_iface_auto(struct usb_device *udev, int index)
777941e2863SAndrew Thompson {
778941e2863SAndrew Thompson 	struct usb_device_request req;
779941e2863SAndrew Thompson 	usb_error_t uerr;
780941e2863SAndrew Thompson 	uint16_t actlen = 0;
781941e2863SAndrew Thompson 	char port;
782941e2863SAndrew Thompson 	char buf[17] = {0};
783941e2863SAndrew Thompson 
784941e2863SAndrew Thompson 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
785941e2863SAndrew Thompson 	req.bRequest = 0x86;
786941e2863SAndrew Thompson 	USETW(req.wValue, 0);
787941e2863SAndrew Thompson 	USETW(req.wIndex, 0);
788941e2863SAndrew Thompson 	USETW(req.wLength, 17);
789941e2863SAndrew Thompson 
79021a9d6e7SAndrew Thompson 	uerr = usbd_do_request_flags(udev, NULL, &req, buf,
791941e2863SAndrew Thompson 	    0, &actlen, USB_MS_HZ);
792941e2863SAndrew Thompson 	if (uerr != 0) {
79321a9d6e7SAndrew Thompson 		printf("%s: usbd_do_request_flags failed, %s\n",
79421a9d6e7SAndrew Thompson 		    __func__, usbd_errstr(uerr));
795941e2863SAndrew Thompson 		return (0);
796941e2863SAndrew Thompson 	}
797941e2863SAndrew Thompson 
7989b6ffc1fSAndrew Thompson 	UHSO_DPRINTF(1, "actlen=%d\n", actlen);
799941e2863SAndrew Thompson 	UHSO_HEXDUMP(buf, 17);
800941e2863SAndrew Thompson 
801941e2863SAndrew Thompson 	if (index < 0 || index > 16) {
802941e2863SAndrew Thompson 		UHSO_DPRINTF(0, "Index %d out of range\n", index);
803941e2863SAndrew Thompson 		return (0);
804941e2863SAndrew Thompson 	}
805941e2863SAndrew Thompson 
8069b6ffc1fSAndrew Thompson 	UHSO_DPRINTF(1, "index=%d, type=%x[%s]\n", index, buf[index],
8079b6ffc1fSAndrew Thompson 	    uhso_port_type[(int)uhso_port_map[(int)buf[index]]]);
808941e2863SAndrew Thompson 
809941e2863SAndrew Thompson 	if (buf[index] >= uhso_port_map_max)
810941e2863SAndrew Thompson 		port = 0;
811941e2863SAndrew Thompson 	else
812941e2863SAndrew Thompson 		port = uhso_port_map[(int)buf[index]];
813941e2863SAndrew Thompson 
8149b6ffc1fSAndrew Thompson 	switch (port) {
8159b6ffc1fSAndrew Thompson 	case UHSO_PORT_TYPE_NETWORK:
8169b6ffc1fSAndrew Thompson 		return (UHSO_IFACE_SPEC(UHSO_IF_NET | UHSO_IF_MUX,
8179b6ffc1fSAndrew Thompson 		    UHSO_PORT_SERIAL | UHSO_PORT_NETWORK, port));
81821a9d6e7SAndrew Thompson 	case UHSO_PORT_TYPE_DIAG:
81921a9d6e7SAndrew Thompson 	case UHSO_PORT_TYPE_DIAG2:
82062b97a9aSHans Petter Selasky 	case UHSO_PORT_TYPE_GPS:
82162b97a9aSHans Petter Selasky 	case UHSO_PORT_TYPE_GPSCTL:
82221a9d6e7SAndrew Thompson 	case UHSO_PORT_TYPE_CTL:
82321a9d6e7SAndrew Thompson 	case UHSO_PORT_TYPE_APP:
82421a9d6e7SAndrew Thompson 	case UHSO_PORT_TYPE_APP2:
82521a9d6e7SAndrew Thompson 	case UHSO_PORT_TYPE_MODEM:
826941e2863SAndrew Thompson 		return (UHSO_IFACE_SPEC(UHSO_IF_BULK,
827941e2863SAndrew Thompson 		    UHSO_PORT_SERIAL, port));
82821a9d6e7SAndrew Thompson 	case UHSO_PORT_TYPE_MSD:
82921a9d6e7SAndrew Thompson 		return (0);
83021a9d6e7SAndrew Thompson 	case UHSO_PORT_TYPE_UNKNOWN:
83121a9d6e7SAndrew Thompson 	default:
83221a9d6e7SAndrew Thompson 		return (0);
8339b6ffc1fSAndrew Thompson 	}
834941e2863SAndrew Thompson 
835941e2863SAndrew Thompson 	return (0);
836941e2863SAndrew Thompson }
837941e2863SAndrew Thompson 
83821a9d6e7SAndrew Thompson /*
83921a9d6e7SAndrew Thompson  * Returns the capabilities of interfaces for devices that don't
84021a9d6e7SAndrew Thompson  * support the automatic query.
84121a9d6e7SAndrew Thompson  * Returns a bit mask with the interface capabilities.
84221a9d6e7SAndrew Thompson  */
843941e2863SAndrew Thompson static int
uhso_probe_iface_static(struct usb_device * udev,int index)84421a9d6e7SAndrew Thompson uhso_probe_iface_static(struct usb_device *udev, int index)
845941e2863SAndrew Thompson {
846941e2863SAndrew Thompson 	struct usb_config_descriptor *cd;
847941e2863SAndrew Thompson 
84821a9d6e7SAndrew Thompson 	cd = usbd_get_config_descriptor(udev);
849941e2863SAndrew Thompson 	if (cd->bNumInterface <= 3) {
8509b6ffc1fSAndrew Thompson 		/* Cards with 3 or less interfaces */
851941e2863SAndrew Thompson 		switch (index) {
852941e2863SAndrew Thompson 		case 0:
853941e2863SAndrew Thompson 			return UHSO_IFACE_SPEC(UHSO_IF_NET | UHSO_IF_MUX,
8549b6ffc1fSAndrew Thompson 			    UHSO_PORT_SERIAL | UHSO_PORT_NETWORK,
8559b6ffc1fSAndrew Thompson 			    UHSO_PORT_TYPE_NETWORK);
856941e2863SAndrew Thompson 		case 1:
857941e2863SAndrew Thompson 			return UHSO_IFACE_SPEC(UHSO_IF_BULK,
858941e2863SAndrew Thompson 			    UHSO_PORT_SERIAL, UHSO_PORT_TYPE_DIAG);
859941e2863SAndrew Thompson 		case 2:
860941e2863SAndrew Thompson 			return UHSO_IFACE_SPEC(UHSO_IF_BULK,
861941e2863SAndrew Thompson 			    UHSO_PORT_SERIAL, UHSO_PORT_TYPE_MODEM);
862941e2863SAndrew Thompson 		}
8639b6ffc1fSAndrew Thompson 	} else {
8649b6ffc1fSAndrew Thompson 		/* Cards with 4 interfaces */
865941e2863SAndrew Thompson 		switch (index) {
866941e2863SAndrew Thompson 		case 0:
867941e2863SAndrew Thompson 			return UHSO_IFACE_SPEC(UHSO_IF_NET | UHSO_IF_MUX,
8689b6ffc1fSAndrew Thompson 			    UHSO_PORT_SERIAL | UHSO_PORT_NETWORK,
8699b6ffc1fSAndrew Thompson 			    UHSO_PORT_TYPE_NETWORK);
870941e2863SAndrew Thompson 		case 1:
871941e2863SAndrew Thompson 			return UHSO_IFACE_SPEC(UHSO_IF_BULK,
872941e2863SAndrew Thompson 			    UHSO_PORT_SERIAL, UHSO_PORT_TYPE_DIAG2);
873941e2863SAndrew Thompson 		case 2:
874941e2863SAndrew Thompson 			return UHSO_IFACE_SPEC(UHSO_IF_BULK,
875941e2863SAndrew Thompson 			    UHSO_PORT_SERIAL, UHSO_PORT_TYPE_MODEM);
876941e2863SAndrew Thompson 		case 3:
877941e2863SAndrew Thompson 			return UHSO_IFACE_SPEC(UHSO_IF_BULK,
878941e2863SAndrew Thompson 			    UHSO_PORT_SERIAL, UHSO_PORT_TYPE_DIAG);
879941e2863SAndrew Thompson 		}
880941e2863SAndrew Thompson 	}
881941e2863SAndrew Thompson 	return (0);
882941e2863SAndrew Thompson }
883941e2863SAndrew Thompson 
8849b6ffc1fSAndrew Thompson /*
8859b6ffc1fSAndrew Thompson  * Probes an interface for its particular capabilities and attaches if
8869b6ffc1fSAndrew Thompson  * it's a supported interface.
8879b6ffc1fSAndrew Thompson  */
888941e2863SAndrew Thompson static int
uhso_probe_iface(struct uhso_softc * sc,int index,int (* probe)(struct usb_device *,int))889941e2863SAndrew Thompson uhso_probe_iface(struct uhso_softc *sc, int index,
89021a9d6e7SAndrew Thompson     int (*probe)(struct usb_device *, int))
891941e2863SAndrew Thompson {
892941e2863SAndrew Thompson 	struct usb_interface *iface;
8939b6ffc1fSAndrew Thompson 	int type, error;
894941e2863SAndrew Thompson 
8959b6ffc1fSAndrew Thompson 	UHSO_DPRINTF(1, "Probing for interface %d, probe_func=%p\n", index, probe);
896941e2863SAndrew Thompson 
89721a9d6e7SAndrew Thompson 	type = probe(sc->sc_udev, index);
898941e2863SAndrew Thompson 	UHSO_DPRINTF(1, "Probe result %x\n", type);
899941e2863SAndrew Thompson 	if (type <= 0)
900941e2863SAndrew Thompson 		return (ENXIO);
901941e2863SAndrew Thompson 
902941e2863SAndrew Thompson 	sc->sc_type = type;
903941e2863SAndrew Thompson 	iface = usbd_get_iface(sc->sc_udev, index);
904941e2863SAndrew Thompson 
9059b6ffc1fSAndrew Thompson 	if (UHSO_IFACE_PORT_TYPE(type) == UHSO_PORT_TYPE_NETWORK) {
906941e2863SAndrew Thompson 		error = uhso_attach_ifnet(sc, iface, type);
9079b6ffc1fSAndrew Thompson 		if (error) {
9089b6ffc1fSAndrew Thompson 			UHSO_DPRINTF(1, "uhso_attach_ifnet failed");
909941e2863SAndrew Thompson 			return (ENXIO);
9109b6ffc1fSAndrew Thompson 		}
911941e2863SAndrew Thompson 
9129b6ffc1fSAndrew Thompson 		/*
9139b6ffc1fSAndrew Thompson 		 * If there is an additional interrupt endpoint on this
914f644abcfSAndrew Thompson 		 * interface then we most likely have a multiplexed serial port
9159b6ffc1fSAndrew Thompson 		 * available.
9169b6ffc1fSAndrew Thompson 		 */
9179b6ffc1fSAndrew Thompson 		if (iface->idesc->bNumEndpoints < 3) {
9189b6ffc1fSAndrew Thompson 			sc->sc_type = UHSO_IFACE_SPEC(
9199b6ffc1fSAndrew Thompson 			    UHSO_IFACE_USB_TYPE(type) & ~UHSO_IF_MUX,
9209b6ffc1fSAndrew Thompson 			    UHSO_IFACE_PORT(type) & ~UHSO_PORT_SERIAL,
9219b6ffc1fSAndrew Thompson 			    UHSO_IFACE_PORT_TYPE(type));
9229b6ffc1fSAndrew Thompson 			return (0);
9239b6ffc1fSAndrew Thompson 		}
9249b6ffc1fSAndrew Thompson 
9259b6ffc1fSAndrew Thompson 		UHSO_DPRINTF(1, "Trying to attach mux. serial\n");
9269b6ffc1fSAndrew Thompson 		error = uhso_attach_muxserial(sc, iface, type);
9279b6ffc1fSAndrew Thompson 		if (error == 0 && sc->sc_ttys > 0) {
928941e2863SAndrew Thompson 			error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom,
929941e2863SAndrew Thompson 			    sc->sc_ttys, sc, &uhso_ucom_callback, &sc->sc_mtx);
930941e2863SAndrew Thompson 			if (error) {
931941e2863SAndrew Thompson 				device_printf(sc->sc_dev, "ucom_attach failed\n");
932941e2863SAndrew Thompson 				return (ENXIO);
933941e2863SAndrew Thompson 			}
9346416c259SNick Hibma 			ucom_set_pnpinfo_usb(&sc->sc_super_ucom, sc->sc_dev);
935941e2863SAndrew Thompson 
936941e2863SAndrew Thompson 			mtx_lock(&sc->sc_mtx);
937941e2863SAndrew Thompson 			usbd_transfer_start(sc->sc_xfer[UHSO_MUX_ENDPT_INTR]);
938941e2863SAndrew Thompson 			mtx_unlock(&sc->sc_mtx);
939941e2863SAndrew Thompson 		}
9409b6ffc1fSAndrew Thompson 	} else if ((UHSO_IFACE_USB_TYPE(type) & UHSO_IF_BULK) &&
941941e2863SAndrew Thompson 	    UHSO_IFACE_PORT(type) & UHSO_PORT_SERIAL) {
942941e2863SAndrew Thompson 		error = uhso_attach_bulkserial(sc, iface, type);
943941e2863SAndrew Thompson 		if (error)
944941e2863SAndrew Thompson 			return (ENXIO);
945941e2863SAndrew Thompson 
946941e2863SAndrew Thompson 		error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom,
947941e2863SAndrew Thompson 		    sc->sc_ttys, sc, &uhso_ucom_callback, &sc->sc_mtx);
948941e2863SAndrew Thompson 		if (error) {
949941e2863SAndrew Thompson 			device_printf(sc->sc_dev, "ucom_attach failed\n");
950941e2863SAndrew Thompson 			return (ENXIO);
951941e2863SAndrew Thompson 		}
9526416c259SNick Hibma 		ucom_set_pnpinfo_usb(&sc->sc_super_ucom, sc->sc_dev);
953941e2863SAndrew Thompson 	}
954941e2863SAndrew Thompson 	else {
9559b6ffc1fSAndrew Thompson 		UHSO_DPRINTF(0, "Unknown type %x\n", type);
956941e2863SAndrew Thompson 		return (ENXIO);
957941e2863SAndrew Thompson 	}
958941e2863SAndrew Thompson 
959941e2863SAndrew Thompson 	return (0);
960941e2863SAndrew Thompson }
961941e2863SAndrew Thompson 
96221a9d6e7SAndrew Thompson static int
uhso_radio_ctrl(struct uhso_softc * sc,int onoff)96321a9d6e7SAndrew Thompson uhso_radio_ctrl(struct uhso_softc *sc, int onoff)
96421a9d6e7SAndrew Thompson {
96521a9d6e7SAndrew Thompson 	struct usb_device_request req;
96621a9d6e7SAndrew Thompson 	usb_error_t uerr;
96721a9d6e7SAndrew Thompson 
96821a9d6e7SAndrew Thompson 	req.bmRequestType = UT_VENDOR;
96921a9d6e7SAndrew Thompson 	req.bRequest = onoff ? 0x82 : 0x81;
97021a9d6e7SAndrew Thompson 	USETW(req.wValue, 0);
97121a9d6e7SAndrew Thompson 	USETW(req.wIndex, 0);
97221a9d6e7SAndrew Thompson 	USETW(req.wLength, 0);
97321a9d6e7SAndrew Thompson 
97421a9d6e7SAndrew Thompson 	uerr = usbd_do_request(sc->sc_udev, NULL, &req, NULL);
97521a9d6e7SAndrew Thompson 	if (uerr != 0) {
97621a9d6e7SAndrew Thompson 		device_printf(sc->sc_dev, "usbd_do_request_flags failed: %s\n",
97721a9d6e7SAndrew Thompson 		    usbd_errstr(uerr));
97821a9d6e7SAndrew Thompson 		return (-1);
97921a9d6e7SAndrew Thompson 	}
98021a9d6e7SAndrew Thompson 	return (onoff);
98121a9d6e7SAndrew Thompson }
98221a9d6e7SAndrew Thompson 
98321a9d6e7SAndrew Thompson static int
uhso_radio_sysctl(SYSCTL_HANDLER_ARGS)98421a9d6e7SAndrew Thompson uhso_radio_sysctl(SYSCTL_HANDLER_ARGS)
98521a9d6e7SAndrew Thompson {
98621a9d6e7SAndrew Thompson 	struct uhso_softc *sc = arg1;
98721a9d6e7SAndrew Thompson 	int error, radio;
98821a9d6e7SAndrew Thompson 
98921a9d6e7SAndrew Thompson 	radio = sc->sc_radio;
99021a9d6e7SAndrew Thompson 	error = sysctl_handle_int(oidp, &radio, 0, req);
99121a9d6e7SAndrew Thompson 	if (error)
99221a9d6e7SAndrew Thompson 		return (error);
99321a9d6e7SAndrew Thompson 	if (radio != sc->sc_radio) {
99421a9d6e7SAndrew Thompson 		radio = radio != 0 ? 1 : 0;
99521a9d6e7SAndrew Thompson 		error = uhso_radio_ctrl(sc, radio);
99621a9d6e7SAndrew Thompson 		if (error != -1)
99721a9d6e7SAndrew Thompson 			sc->sc_radio = radio;
99821a9d6e7SAndrew Thompson 
99921a9d6e7SAndrew Thompson 	}
100021a9d6e7SAndrew Thompson 	return (0);
100121a9d6e7SAndrew Thompson }
100221a9d6e7SAndrew Thompson 
10039b6ffc1fSAndrew Thompson /*
10049b6ffc1fSAndrew Thompson  * Expands allocated memory to fit an additional TTY.
10059b6ffc1fSAndrew Thompson  * Two arrays are kept with matching indexes, one for ucom and one
10069b6ffc1fSAndrew Thompson  * for our private data.
10079b6ffc1fSAndrew Thompson  */
1008941e2863SAndrew Thompson static int
uhso_alloc_tty(struct uhso_softc * sc)1009941e2863SAndrew Thompson uhso_alloc_tty(struct uhso_softc *sc)
1010941e2863SAndrew Thompson {
1011941e2863SAndrew Thompson 
1012941e2863SAndrew Thompson 	sc->sc_ttys++;
1013941e2863SAndrew Thompson 	sc->sc_tty = reallocf(sc->sc_tty, sizeof(struct uhso_tty) * sc->sc_ttys,
1014941e2863SAndrew Thompson 	    M_USBDEV, M_WAITOK | M_ZERO);
1015941e2863SAndrew Thompson 	if (sc->sc_tty == NULL)
1016941e2863SAndrew Thompson 		return (-1);
1017941e2863SAndrew Thompson 
1018941e2863SAndrew Thompson 	sc->sc_ucom = reallocf(sc->sc_ucom,
1019941e2863SAndrew Thompson 	    sizeof(struct ucom_softc) * sc->sc_ttys, M_USBDEV, M_WAITOK | M_ZERO);
1020941e2863SAndrew Thompson 	if (sc->sc_ucom == NULL)
1021941e2863SAndrew Thompson 		return (-1);
1022941e2863SAndrew Thompson 
1023941e2863SAndrew Thompson 	sc->sc_tty[sc->sc_ttys - 1].ht_sc = sc;
1024941e2863SAndrew Thompson 
10259b6ffc1fSAndrew Thompson 	UHSO_DPRINTF(1, "Allocated TTY %d\n", sc->sc_ttys - 1);
1026941e2863SAndrew Thompson 	return (sc->sc_ttys - 1);
1027941e2863SAndrew Thompson }
1028941e2863SAndrew Thompson 
10299b6ffc1fSAndrew Thompson /*
10309b6ffc1fSAndrew Thompson  * Attach a multiplexed serial port
10319b6ffc1fSAndrew Thompson  * Data is read/written with requests on the default control pipe. An interrupt
10329b6ffc1fSAndrew Thompson  * endpoint returns when there is new data to be read.
10339b6ffc1fSAndrew Thompson  */
1034941e2863SAndrew Thompson static int
uhso_attach_muxserial(struct uhso_softc * sc,struct usb_interface * iface,int type)1035941e2863SAndrew Thompson uhso_attach_muxserial(struct uhso_softc *sc, struct usb_interface *iface,
1036941e2863SAndrew Thompson     int type)
1037941e2863SAndrew Thompson {
1038941e2863SAndrew Thompson 	struct usb_descriptor *desc;
1039941e2863SAndrew Thompson 	int i, port, tty;
1040941e2863SAndrew Thompson 	usb_error_t uerr;
1041941e2863SAndrew Thompson 
1042941e2863SAndrew Thompson 	/*
1043941e2863SAndrew Thompson 	 * The class specific interface (type 0x24) descriptor subtype field
1044941e2863SAndrew Thompson 	 * contains a bitmask that specifies which (and how many) ports that
1045941e2863SAndrew Thompson 	 * are available through this multiplexed serial port.
1046941e2863SAndrew Thompson  	 */
1047941e2863SAndrew Thompson 	desc = usbd_find_descriptor(sc->sc_udev, NULL,
1048941e2863SAndrew Thompson 	    iface->idesc->bInterfaceNumber, UDESC_CS_INTERFACE, 0xff, 0, 0);
1049941e2863SAndrew Thompson 	if (desc == NULL) {
1050941e2863SAndrew Thompson 		UHSO_DPRINTF(0, "Failed to find UDESC_CS_INTERFACE\n");
1051941e2863SAndrew Thompson 		return (ENXIO);
1052941e2863SAndrew Thompson 	}
1053941e2863SAndrew Thompson 
1054941e2863SAndrew Thompson 	UHSO_DPRINTF(1, "Mux port mask %x\n", desc->bDescriptorSubtype);
1055941e2863SAndrew Thompson 	if (desc->bDescriptorSubtype == 0)
1056941e2863SAndrew Thompson 		return (ENXIO);
1057941e2863SAndrew Thompson 
10589b6ffc1fSAndrew Thompson 	/*
10599b6ffc1fSAndrew Thompson 	 * The bitmask is one octet, loop through the number of
10609b6ffc1fSAndrew Thompson 	 * bits that are set and create a TTY for each.
10619b6ffc1fSAndrew Thompson 	 */
1062941e2863SAndrew Thompson 	for (i = 0; i < 8; i++) {
1063941e2863SAndrew Thompson 		port = (1 << i);
1064941e2863SAndrew Thompson 		if ((port & desc->bDescriptorSubtype) == port) {
1065941e2863SAndrew Thompson 			UHSO_DPRINTF(2, "Found mux port %x (%d)\n", port, i);
1066941e2863SAndrew Thompson 			tty = uhso_alloc_tty(sc);
1067941e2863SAndrew Thompson 			if (tty < 0)
1068941e2863SAndrew Thompson 				return (ENOMEM);
1069941e2863SAndrew Thompson 			sc->sc_tty[tty].ht_muxport = i;
1070941e2863SAndrew Thompson 			uerr = usbd_transfer_setup(sc->sc_udev,
1071941e2863SAndrew Thompson 			    &sc->sc_iface_index, sc->sc_tty[tty].ht_xfer,
1072941e2863SAndrew Thompson 			    uhso_ctrl_config, UHSO_CTRL_MAX, sc, &sc->sc_mtx);
1073941e2863SAndrew Thompson 			if (uerr) {
1074941e2863SAndrew Thompson 				device_printf(sc->sc_dev,
1075941e2863SAndrew Thompson 				    "Failed to setup control pipe: %s\n",
1076941e2863SAndrew Thompson 				    usbd_errstr(uerr));
1077941e2863SAndrew Thompson 				return (ENXIO);
1078941e2863SAndrew Thompson 			}
1079941e2863SAndrew Thompson 		}
1080941e2863SAndrew Thompson 	}
1081941e2863SAndrew Thompson 
10829b6ffc1fSAndrew Thompson 	/* Setup the intr. endpoint */
1083941e2863SAndrew Thompson 	uerr = usbd_transfer_setup(sc->sc_udev,
1084941e2863SAndrew Thompson 	    &iface->idesc->bInterfaceNumber, sc->sc_xfer,
1085941e2863SAndrew Thompson 	    uhso_mux_config, 1, sc, &sc->sc_mtx);
1086941e2863SAndrew Thompson 	if (uerr)
1087941e2863SAndrew Thompson 		return (ENXIO);
1088941e2863SAndrew Thompson 
1089941e2863SAndrew Thompson 	return (0);
1090941e2863SAndrew Thompson }
1091941e2863SAndrew Thompson 
10929b6ffc1fSAndrew Thompson /*
10939b6ffc1fSAndrew Thompson  * Interrupt callback for the multiplexed serial port. Indicates
1094f644abcfSAndrew Thompson  * which serial port has data waiting.
10959b6ffc1fSAndrew Thompson  */
1096941e2863SAndrew Thompson static void
uhso_mux_intr_callback(struct usb_xfer * xfer,usb_error_t error)1097941e2863SAndrew Thompson uhso_mux_intr_callback(struct usb_xfer *xfer, usb_error_t error)
1098941e2863SAndrew Thompson {
1099941e2863SAndrew Thompson 	struct usb_page_cache *pc;
1100941e2863SAndrew Thompson 	struct usb_page_search res;
1101941e2863SAndrew Thompson 	struct uhso_softc *sc = usbd_xfer_softc(xfer);
110262d42655SHans Petter Selasky 	unsigned i, mux;
1103941e2863SAndrew Thompson 
1104941e2863SAndrew Thompson 	UHSO_DPRINTF(3, "status %d\n", USB_GET_STATE(xfer));
1105941e2863SAndrew Thompson 
1106941e2863SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
1107941e2863SAndrew Thompson 	case USB_ST_TRANSFERRED:
1108941e2863SAndrew Thompson 		/*
1109941e2863SAndrew Thompson 		 * The multiplexed port number can be found at the first byte.
1110941e2863SAndrew Thompson 		 * It contains a bit mask, we transform this in to an integer.
1111941e2863SAndrew Thompson 		 */
1112941e2863SAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
1113941e2863SAndrew Thompson 		usbd_get_page(pc, 0, &res);
1114941e2863SAndrew Thompson 
1115941e2863SAndrew Thompson 		i = *((unsigned char *)res.buffer);
1116941e2863SAndrew Thompson 		mux = 0;
1117941e2863SAndrew Thompson 		while (i >>= 1) {
1118941e2863SAndrew Thompson 			mux++;
1119941e2863SAndrew Thompson 		}
1120941e2863SAndrew Thompson 
1121941e2863SAndrew Thompson 		UHSO_DPRINTF(3, "mux port %d (%d)\n", mux, i);
1122941e2863SAndrew Thompson 		if (mux > UHSO_MPORT_TYPE_NOMAX)
1123941e2863SAndrew Thompson 			break;
1124941e2863SAndrew Thompson 
11259b6ffc1fSAndrew Thompson 		/* Issue a read for this serial port */
1126941e2863SAndrew Thompson 		usbd_xfer_set_priv(
1127941e2863SAndrew Thompson 		    sc->sc_tty[mux].ht_xfer[UHSO_CTRL_READ],
1128941e2863SAndrew Thompson 		    &sc->sc_tty[mux]);
1129941e2863SAndrew Thompson 		usbd_transfer_start(sc->sc_tty[mux].ht_xfer[UHSO_CTRL_READ]);
1130941e2863SAndrew Thompson 
1131941e2863SAndrew Thompson 		break;
1132941e2863SAndrew Thompson 	case USB_ST_SETUP:
1133941e2863SAndrew Thompson tr_setup:
1134941e2863SAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
1135941e2863SAndrew Thompson 		usbd_transfer_submit(xfer);
1136941e2863SAndrew Thompson 		break;
1137941e2863SAndrew Thompson 	default:
1138941e2863SAndrew Thompson 		UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error));
1139941e2863SAndrew Thompson 		if (error == USB_ERR_CANCELLED)
1140941e2863SAndrew Thompson 			break;
1141941e2863SAndrew Thompson 
1142941e2863SAndrew Thompson 		usbd_xfer_set_stall(xfer);
1143941e2863SAndrew Thompson 		goto tr_setup;
1144941e2863SAndrew Thompson 	}
1145941e2863SAndrew Thompson }
1146941e2863SAndrew Thompson 
1147941e2863SAndrew Thompson static void
uhso_mux_read_callback(struct usb_xfer * xfer,usb_error_t error)1148941e2863SAndrew Thompson uhso_mux_read_callback(struct usb_xfer *xfer, usb_error_t error)
1149941e2863SAndrew Thompson {
1150941e2863SAndrew Thompson 	struct uhso_softc *sc = usbd_xfer_softc(xfer);
1151941e2863SAndrew Thompson 	struct usb_page_cache *pc;
1152941e2863SAndrew Thompson 	struct usb_device_request req;
1153941e2863SAndrew Thompson 	struct uhso_tty *ht;
1154941e2863SAndrew Thompson 	int actlen, len;
1155941e2863SAndrew Thompson 
1156941e2863SAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1157941e2863SAndrew Thompson 
1158941e2863SAndrew Thompson 	UHSO_DPRINTF(3, "status %d\n", USB_GET_STATE(xfer));
1159941e2863SAndrew Thompson 
1160941e2863SAndrew Thompson 	ht = usbd_xfer_get_priv(xfer);
1161941e2863SAndrew Thompson 	UHSO_DPRINTF(3, "ht=%p open=%d\n", ht, ht->ht_open);
1162941e2863SAndrew Thompson 
1163941e2863SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
1164941e2863SAndrew Thompson 	case USB_ST_TRANSFERRED:
1165941e2863SAndrew Thompson 		/* Got data, send to ucom */
1166941e2863SAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 1);
1167941e2863SAndrew Thompson 		len = usbd_xfer_frame_len(xfer, 1);
1168941e2863SAndrew Thompson 
1169941e2863SAndrew Thompson 		UHSO_DPRINTF(3, "got %d bytes on mux port %d\n", len,
1170941e2863SAndrew Thompson 		    ht->ht_muxport);
1171941e2863SAndrew Thompson 		if (len <= 0) {
1172941e2863SAndrew Thompson 			usbd_transfer_start(sc->sc_xfer[UHSO_MUX_ENDPT_INTR]);
1173941e2863SAndrew Thompson 			break;
1174941e2863SAndrew Thompson 		}
1175941e2863SAndrew Thompson 
1176941e2863SAndrew Thompson 		/* Deliver data if the TTY is open, discard otherwise */
1177941e2863SAndrew Thompson 		if (ht->ht_open)
1178941e2863SAndrew Thompson 			ucom_put_data(&sc->sc_ucom[ht->ht_muxport], pc, 0, len);
1179941e2863SAndrew Thompson 		/* FALLTHROUGH */
1180941e2863SAndrew Thompson 	case USB_ST_SETUP:
1181941e2863SAndrew Thompson tr_setup:
1182271ae033SHans Petter Selasky 		memset(&req, 0, sizeof(struct usb_device_request));
1183941e2863SAndrew Thompson 		req.bmRequestType = UT_READ_CLASS_INTERFACE;
1184941e2863SAndrew Thompson 		req.bRequest = UCDC_GET_ENCAPSULATED_RESPONSE;
1185941e2863SAndrew Thompson 		USETW(req.wValue, 0);
1186941e2863SAndrew Thompson 		USETW(req.wIndex, ht->ht_muxport);
1187941e2863SAndrew Thompson 		USETW(req.wLength, 1024);
1188941e2863SAndrew Thompson 
1189941e2863SAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
1190941e2863SAndrew Thompson 		usbd_copy_in(pc, 0, &req, sizeof(req));
1191941e2863SAndrew Thompson 
1192941e2863SAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
1193941e2863SAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 1, 1024);
1194941e2863SAndrew Thompson 		usbd_xfer_set_frames(xfer, 2);
1195941e2863SAndrew Thompson 		usbd_transfer_submit(xfer);
1196941e2863SAndrew Thompson 		break;
1197941e2863SAndrew Thompson 	default:
1198941e2863SAndrew Thompson 		UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error));
1199941e2863SAndrew Thompson 		if (error == USB_ERR_CANCELLED)
1200941e2863SAndrew Thompson 			break;
1201941e2863SAndrew Thompson 		usbd_xfer_set_stall(xfer);
1202941e2863SAndrew Thompson 		goto tr_setup;
1203941e2863SAndrew Thompson 	}
1204941e2863SAndrew Thompson }
1205941e2863SAndrew Thompson 
1206941e2863SAndrew Thompson static void
uhso_mux_write_callback(struct usb_xfer * xfer,usb_error_t error)1207941e2863SAndrew Thompson uhso_mux_write_callback(struct usb_xfer *xfer, usb_error_t error)
1208941e2863SAndrew Thompson {
1209941e2863SAndrew Thompson 	struct uhso_softc *sc = usbd_xfer_softc(xfer);
1210941e2863SAndrew Thompson 	struct uhso_tty *ht;
1211941e2863SAndrew Thompson 	struct usb_page_cache *pc;
1212941e2863SAndrew Thompson 	struct usb_device_request req;
1213941e2863SAndrew Thompson 	int actlen;
1214941e2863SAndrew Thompson 	struct usb_page_search res;
1215941e2863SAndrew Thompson 
1216941e2863SAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1217941e2863SAndrew Thompson 
1218941e2863SAndrew Thompson 	ht = usbd_xfer_get_priv(xfer);
1219941e2863SAndrew Thompson 	UHSO_DPRINTF(3, "status=%d, using mux port %d\n",
1220941e2863SAndrew Thompson 	    USB_GET_STATE(xfer), ht->ht_muxport);
1221941e2863SAndrew Thompson 
1222941e2863SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
1223941e2863SAndrew Thompson 	case USB_ST_TRANSFERRED:
1224941e2863SAndrew Thompson 		UHSO_DPRINTF(3, "wrote %zd data bytes to muxport %d\n",
1225941e2863SAndrew Thompson 		    actlen - sizeof(struct usb_device_request) ,
1226941e2863SAndrew Thompson 		    ht->ht_muxport);
1227941e2863SAndrew Thompson 		/* FALLTHROUGH */
1228941e2863SAndrew Thompson 	case USB_ST_SETUP:
1229b16de7bfSPedro F. Giffuni tr_setup:
1230941e2863SAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 1);
1231941e2863SAndrew Thompson 		if (ucom_get_data(&sc->sc_ucom[ht->ht_muxport], pc,
1232941e2863SAndrew Thompson 		    0, 32, &actlen)) {
1233941e2863SAndrew Thompson 			usbd_get_page(pc, 0, &res);
1234941e2863SAndrew Thompson 
1235271ae033SHans Petter Selasky 			memset(&req, 0, sizeof(struct usb_device_request));
1236941e2863SAndrew Thompson 			req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
1237941e2863SAndrew Thompson 			req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND;
1238941e2863SAndrew Thompson 			USETW(req.wValue, 0);
1239941e2863SAndrew Thompson 			USETW(req.wIndex, ht->ht_muxport);
1240941e2863SAndrew Thompson 			USETW(req.wLength, actlen);
1241941e2863SAndrew Thompson 
1242941e2863SAndrew Thompson 			pc = usbd_xfer_get_frame(xfer, 0);
1243941e2863SAndrew Thompson 			usbd_copy_in(pc, 0, &req, sizeof(req));
1244941e2863SAndrew Thompson 
1245941e2863SAndrew Thompson 			usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
1246941e2863SAndrew Thompson 			usbd_xfer_set_frame_len(xfer, 1, actlen);
1247941e2863SAndrew Thompson 			usbd_xfer_set_frames(xfer, 2);
1248941e2863SAndrew Thompson 
1249941e2863SAndrew Thompson 			UHSO_DPRINTF(3, "Prepared %d bytes for transmit "
1250941e2863SAndrew Thompson 			    "on muxport %d\n", actlen, ht->ht_muxport);
1251941e2863SAndrew Thompson 
1252941e2863SAndrew Thompson 			usbd_transfer_submit(xfer);
1253941e2863SAndrew Thompson 		}
1254941e2863SAndrew Thompson 		break;
1255941e2863SAndrew Thompson 	default:
1256941e2863SAndrew Thompson 		UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error));
1257941e2863SAndrew Thompson 		if (error == USB_ERR_CANCELLED)
1258941e2863SAndrew Thompson 			break;
1259b16de7bfSPedro F. Giffuni 		usbd_xfer_set_stall(xfer);
1260b16de7bfSPedro F. Giffuni 		goto tr_setup;
1261941e2863SAndrew Thompson 	}
1262941e2863SAndrew Thompson }
1263941e2863SAndrew Thompson 
1264941e2863SAndrew Thompson static int
uhso_attach_bulkserial(struct uhso_softc * sc,struct usb_interface * iface,int type)1265941e2863SAndrew Thompson uhso_attach_bulkserial(struct uhso_softc *sc, struct usb_interface *iface,
1266941e2863SAndrew Thompson     int type)
1267941e2863SAndrew Thompson {
1268941e2863SAndrew Thompson 	usb_error_t uerr;
1269941e2863SAndrew Thompson 	int tty;
1270941e2863SAndrew Thompson 
12719b6ffc1fSAndrew Thompson 	/* Try attaching RD/WR/INTR first */
1272941e2863SAndrew Thompson 	uerr = usbd_transfer_setup(sc->sc_udev,
1273941e2863SAndrew Thompson 	    &iface->idesc->bInterfaceNumber, sc->sc_xfer,
1274941e2863SAndrew Thompson 	    uhso_bs_config, UHSO_BULK_ENDPT_MAX, sc, &sc->sc_mtx);
1275941e2863SAndrew Thompson 	if (uerr) {
1276941e2863SAndrew Thompson 		/* Try only RD/WR */
1277941e2863SAndrew Thompson 		uerr = usbd_transfer_setup(sc->sc_udev,
1278941e2863SAndrew Thompson 		    &iface->idesc->bInterfaceNumber, sc->sc_xfer,
1279941e2863SAndrew Thompson 		    uhso_bs_config, UHSO_BULK_ENDPT_MAX - 1, sc, &sc->sc_mtx);
1280941e2863SAndrew Thompson 	}
1281941e2863SAndrew Thompson 	if (uerr) {
1282941e2863SAndrew Thompson 		UHSO_DPRINTF(0, "usbd_transfer_setup failed");
1283941e2863SAndrew Thompson 		return (-1);
1284941e2863SAndrew Thompson 	}
1285941e2863SAndrew Thompson 
1286941e2863SAndrew Thompson 	tty = uhso_alloc_tty(sc);
1287941e2863SAndrew Thompson 	if (tty < 0) {
1288941e2863SAndrew Thompson 		usbd_transfer_unsetup(sc->sc_xfer, UHSO_BULK_ENDPT_MAX);
1289941e2863SAndrew Thompson 		return (ENOMEM);
1290941e2863SAndrew Thompson 	}
1291941e2863SAndrew Thompson 
1292941e2863SAndrew Thompson 	sc->sc_tty[tty].ht_muxport = -1;
1293941e2863SAndrew Thompson 	return (0);
1294941e2863SAndrew Thompson }
1295941e2863SAndrew Thompson 
1296941e2863SAndrew Thompson static void
uhso_bs_read_callback(struct usb_xfer * xfer,usb_error_t error)1297941e2863SAndrew Thompson uhso_bs_read_callback(struct usb_xfer *xfer, usb_error_t error)
1298941e2863SAndrew Thompson {
1299941e2863SAndrew Thompson 	struct uhso_softc *sc = usbd_xfer_softc(xfer);
1300941e2863SAndrew Thompson 	struct usb_page_cache *pc;
1301941e2863SAndrew Thompson 	int actlen;
1302941e2863SAndrew Thompson 
1303941e2863SAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1304941e2863SAndrew Thompson 
1305941e2863SAndrew Thompson 	UHSO_DPRINTF(3, "status %d, actlen=%d\n", USB_GET_STATE(xfer), actlen);
1306941e2863SAndrew Thompson 
1307941e2863SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
1308941e2863SAndrew Thompson 	case USB_ST_TRANSFERRED:
1309941e2863SAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
1310941e2863SAndrew Thompson 		ucom_put_data(&sc->sc_ucom[0], pc, 0, actlen);
1311941e2863SAndrew Thompson 		/* FALLTHROUGH */
1312941e2863SAndrew Thompson 	case USB_ST_SETUP:
1313941e2863SAndrew Thompson tr_setup:
1314941e2863SAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
1315941e2863SAndrew Thompson 		usbd_transfer_submit(xfer);
1316941e2863SAndrew Thompson 	break;
1317941e2863SAndrew Thompson 	default:
1318941e2863SAndrew Thompson 		UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error));
1319941e2863SAndrew Thompson 		if (error == USB_ERR_CANCELLED)
1320941e2863SAndrew Thompson 			break;
1321941e2863SAndrew Thompson 		usbd_xfer_set_stall(xfer);
1322941e2863SAndrew Thompson 		goto tr_setup;
1323941e2863SAndrew Thompson 	}
1324941e2863SAndrew Thompson }
1325941e2863SAndrew Thompson 
1326941e2863SAndrew Thompson static void
uhso_bs_write_callback(struct usb_xfer * xfer,usb_error_t error)1327941e2863SAndrew Thompson uhso_bs_write_callback(struct usb_xfer *xfer, usb_error_t error)
1328941e2863SAndrew Thompson {
1329941e2863SAndrew Thompson 	struct uhso_softc *sc = usbd_xfer_softc(xfer);
1330941e2863SAndrew Thompson 	struct usb_page_cache *pc;
1331941e2863SAndrew Thompson 	int actlen;
1332941e2863SAndrew Thompson 
1333941e2863SAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1334941e2863SAndrew Thompson 
1335941e2863SAndrew Thompson 	UHSO_DPRINTF(3, "status %d, actlen=%d\n", USB_GET_STATE(xfer), actlen);
1336941e2863SAndrew Thompson 
1337941e2863SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
1338941e2863SAndrew Thompson 	case USB_ST_TRANSFERRED:
1339941e2863SAndrew Thompson 	case USB_ST_SETUP:
1340941e2863SAndrew Thompson tr_setup:
1341941e2863SAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
1342941e2863SAndrew Thompson 		if (ucom_get_data(&sc->sc_ucom[0], pc, 0, 8192, &actlen)) {
1343941e2863SAndrew Thompson 			usbd_xfer_set_frame_len(xfer, 0, actlen);
1344941e2863SAndrew Thompson 			usbd_transfer_submit(xfer);
1345941e2863SAndrew Thompson 		}
1346941e2863SAndrew Thompson 		break;
1347941e2863SAndrew Thompson 	break;
1348941e2863SAndrew Thompson 	default:
1349941e2863SAndrew Thompson 		UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error));
1350941e2863SAndrew Thompson 		if (error == USB_ERR_CANCELLED)
1351941e2863SAndrew Thompson 			break;
1352941e2863SAndrew Thompson 		usbd_xfer_set_stall(xfer);
1353941e2863SAndrew Thompson 		goto tr_setup;
1354941e2863SAndrew Thompson 	}
1355941e2863SAndrew Thompson }
1356941e2863SAndrew Thompson 
1357941e2863SAndrew Thompson static void
uhso_bs_cfg(struct uhso_softc * sc)1358941e2863SAndrew Thompson uhso_bs_cfg(struct uhso_softc *sc)
1359941e2863SAndrew Thompson {
1360941e2863SAndrew Thompson 	struct usb_device_request req;
1361941e2863SAndrew Thompson 	usb_error_t uerr;
1362941e2863SAndrew Thompson 
1363941e2863SAndrew Thompson 	if (!(UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_BULK))
1364941e2863SAndrew Thompson 		return;
1365941e2863SAndrew Thompson 
1366941e2863SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
1367941e2863SAndrew Thompson 	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
1368941e2863SAndrew Thompson 	USETW(req.wValue, sc->sc_line);
1369941e2863SAndrew Thompson 	USETW(req.wIndex, sc->sc_iface_no);
1370941e2863SAndrew Thompson 	USETW(req.wLength, 0);
1371941e2863SAndrew Thompson 
1372941e2863SAndrew Thompson 	uerr = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom[0], &req, NULL, 0, 1000);
1373941e2863SAndrew Thompson 	if (uerr != 0) {
1374941e2863SAndrew Thompson 		device_printf(sc->sc_dev, "failed to set ctrl line state to "
1375941e2863SAndrew Thompson 		    "0x%02x: %s\n", sc->sc_line, usbd_errstr(uerr));
1376941e2863SAndrew Thompson 	}
1377941e2863SAndrew Thompson }
1378941e2863SAndrew Thompson 
1379941e2863SAndrew Thompson static void
uhso_bs_intr_callback(struct usb_xfer * xfer,usb_error_t error)1380941e2863SAndrew Thompson uhso_bs_intr_callback(struct usb_xfer *xfer, usb_error_t error)
1381941e2863SAndrew Thompson {
1382941e2863SAndrew Thompson 	struct uhso_softc *sc = usbd_xfer_softc(xfer);
1383941e2863SAndrew Thompson 	struct usb_page_cache *pc;
1384941e2863SAndrew Thompson 	int actlen;
1385941e2863SAndrew Thompson 	struct usb_cdc_notification cdc;
1386941e2863SAndrew Thompson 
1387941e2863SAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1388941e2863SAndrew Thompson 	UHSO_DPRINTF(3, "status %d, actlen=%d\n", USB_GET_STATE(xfer), actlen);
1389941e2863SAndrew Thompson 
1390941e2863SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
1391941e2863SAndrew Thompson 	case USB_ST_TRANSFERRED:
1392941e2863SAndrew Thompson 		if (actlen < UCDC_NOTIFICATION_LENGTH) {
1393941e2863SAndrew Thompson 			UHSO_DPRINTF(0, "UCDC notification too short: %d\n", actlen);
1394941e2863SAndrew Thompson 			goto tr_setup;
1395941e2863SAndrew Thompson 		}
13966d917491SHans Petter Selasky 		else if (actlen > (int)sizeof(struct usb_cdc_notification)) {
1397941e2863SAndrew Thompson 			UHSO_DPRINTF(0, "UCDC notification too large: %d\n", actlen);
1398941e2863SAndrew Thompson 			actlen = sizeof(struct usb_cdc_notification);
1399941e2863SAndrew Thompson 		}
1400941e2863SAndrew Thompson 
1401941e2863SAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
1402941e2863SAndrew Thompson 		usbd_copy_out(pc, 0, &cdc, actlen);
1403941e2863SAndrew Thompson 
1404941e2863SAndrew Thompson 		if (UGETW(cdc.wIndex) != sc->sc_iface_no) {
14059b6ffc1fSAndrew Thompson 			UHSO_DPRINTF(0, "Interface mismatch, got %d expected %d\n",
1406941e2863SAndrew Thompson 			    UGETW(cdc.wIndex), sc->sc_iface_no);
1407941e2863SAndrew Thompson 			goto tr_setup;
1408941e2863SAndrew Thompson 		}
1409941e2863SAndrew Thompson 
1410941e2863SAndrew Thompson 		if (cdc.bmRequestType == UCDC_NOTIFICATION &&
1411941e2863SAndrew Thompson 		    cdc.bNotification == UCDC_N_SERIAL_STATE) {
14129b6ffc1fSAndrew Thompson 			UHSO_DPRINTF(2, "notify = 0x%02x\n", cdc.data[0]);
1413941e2863SAndrew Thompson 
1414941e2863SAndrew Thompson 			sc->sc_msr = 0;
1415941e2863SAndrew Thompson 			sc->sc_lsr = 0;
1416941e2863SAndrew Thompson 			if (cdc.data[0] & UCDC_N_SERIAL_RI)
1417941e2863SAndrew Thompson 				sc->sc_msr |= SER_RI;
1418941e2863SAndrew Thompson 			if (cdc.data[0] & UCDC_N_SERIAL_DSR)
1419941e2863SAndrew Thompson 				sc->sc_msr |= SER_DSR;
1420941e2863SAndrew Thompson 			if (cdc.data[0] & UCDC_N_SERIAL_DCD)
1421941e2863SAndrew Thompson 				sc->sc_msr |= SER_DCD;
1422941e2863SAndrew Thompson 
1423941e2863SAndrew Thompson 			ucom_status_change(&sc->sc_ucom[0]);
1424941e2863SAndrew Thompson 		}
1425941e2863SAndrew Thompson 	case USB_ST_SETUP:
1426941e2863SAndrew Thompson tr_setup:
1427941e2863SAndrew Thompson 	default:
1428941e2863SAndrew Thompson 		if (error == USB_ERR_CANCELLED)
1429941e2863SAndrew Thompson 			break;
1430941e2863SAndrew Thompson 		usbd_xfer_set_stall(xfer);
1431941e2863SAndrew Thompson 		goto tr_setup;
1432941e2863SAndrew Thompson 	}
1433941e2863SAndrew Thompson }
1434941e2863SAndrew Thompson 
1435941e2863SAndrew Thompson static void
uhso_ucom_cfg_get_status(struct ucom_softc * ucom,uint8_t * lsr,uint8_t * msr)1436941e2863SAndrew Thompson uhso_ucom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
1437941e2863SAndrew Thompson {
1438941e2863SAndrew Thompson 	struct uhso_softc *sc = ucom->sc_parent;
1439941e2863SAndrew Thompson 
1440941e2863SAndrew Thompson 	*lsr = sc->sc_lsr;
1441941e2863SAndrew Thompson 	*msr = sc->sc_msr;
1442941e2863SAndrew Thompson }
1443941e2863SAndrew Thompson 
1444941e2863SAndrew Thompson static void
uhso_ucom_cfg_set_dtr(struct ucom_softc * ucom,uint8_t onoff)1445941e2863SAndrew Thompson uhso_ucom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
1446941e2863SAndrew Thompson {
1447941e2863SAndrew Thompson 	struct uhso_softc *sc = ucom->sc_parent;
1448941e2863SAndrew Thompson 
1449941e2863SAndrew Thompson 	if (!(UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_BULK))
1450941e2863SAndrew Thompson 		return;
1451941e2863SAndrew Thompson 
1452941e2863SAndrew Thompson 	if (onoff)
1453941e2863SAndrew Thompson 		sc->sc_line |= UCDC_LINE_DTR;
1454941e2863SAndrew Thompson 	else
14559429d9dbSAndrew Thompson 		sc->sc_line &= ~UCDC_LINE_DTR;
1456941e2863SAndrew Thompson 
1457941e2863SAndrew Thompson 	uhso_bs_cfg(sc);
1458941e2863SAndrew Thompson }
1459941e2863SAndrew Thompson 
1460941e2863SAndrew Thompson static void
uhso_ucom_cfg_set_rts(struct ucom_softc * ucom,uint8_t onoff)1461941e2863SAndrew Thompson uhso_ucom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
1462941e2863SAndrew Thompson {
1463941e2863SAndrew Thompson 	struct uhso_softc *sc = ucom->sc_parent;
1464941e2863SAndrew Thompson 
1465941e2863SAndrew Thompson 	if (!(UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_BULK))
1466941e2863SAndrew Thompson 		return;
1467941e2863SAndrew Thompson 
1468941e2863SAndrew Thompson 	if (onoff)
1469941e2863SAndrew Thompson 		sc->sc_line |= UCDC_LINE_RTS;
1470941e2863SAndrew Thompson 	else
14719429d9dbSAndrew Thompson 		sc->sc_line &= ~UCDC_LINE_RTS;
1472941e2863SAndrew Thompson 
1473941e2863SAndrew Thompson 	uhso_bs_cfg(sc);
1474941e2863SAndrew Thompson }
1475941e2863SAndrew Thompson 
1476941e2863SAndrew Thompson static void
uhso_ucom_start_read(struct ucom_softc * ucom)1477941e2863SAndrew Thompson uhso_ucom_start_read(struct ucom_softc *ucom)
1478941e2863SAndrew Thompson {
1479941e2863SAndrew Thompson 	struct uhso_softc *sc = ucom->sc_parent;
1480941e2863SAndrew Thompson 
1481015bb88fSNick Hibma 	UHSO_DPRINTF(3, "unit=%d, subunit=%d\n",
1482015bb88fSNick Hibma 	    ucom->sc_super->sc_unit, ucom->sc_subunit);
1483941e2863SAndrew Thompson 
1484941e2863SAndrew Thompson 	if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_MUX) {
1485015bb88fSNick Hibma 		sc->sc_tty[ucom->sc_subunit].ht_open = 1;
1486941e2863SAndrew Thompson 		usbd_transfer_start(sc->sc_xfer[UHSO_MUX_ENDPT_INTR]);
1487941e2863SAndrew Thompson 	}
1488941e2863SAndrew Thompson 	else if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_BULK) {
1489941e2863SAndrew Thompson 		sc->sc_tty[0].ht_open = 1;
1490941e2863SAndrew Thompson 		usbd_transfer_start(sc->sc_xfer[UHSO_BULK_ENDPT_READ]);
1491941e2863SAndrew Thompson 		if (sc->sc_xfer[UHSO_BULK_ENDPT_INTR] != NULL)
1492941e2863SAndrew Thompson 			usbd_transfer_start(sc->sc_xfer[UHSO_BULK_ENDPT_INTR]);
1493941e2863SAndrew Thompson 	}
1494941e2863SAndrew Thompson }
1495941e2863SAndrew Thompson 
1496941e2863SAndrew Thompson static void
uhso_ucom_stop_read(struct ucom_softc * ucom)1497941e2863SAndrew Thompson uhso_ucom_stop_read(struct ucom_softc *ucom)
1498941e2863SAndrew Thompson {
1499941e2863SAndrew Thompson 
1500941e2863SAndrew Thompson 	struct uhso_softc *sc = ucom->sc_parent;
1501941e2863SAndrew Thompson 
1502941e2863SAndrew Thompson 	if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_MUX) {
1503015bb88fSNick Hibma 		sc->sc_tty[ucom->sc_subunit].ht_open = 0;
1504941e2863SAndrew Thompson 		usbd_transfer_stop(
1505015bb88fSNick Hibma 		    sc->sc_tty[ucom->sc_subunit].ht_xfer[UHSO_CTRL_READ]);
1506941e2863SAndrew Thompson 	}
1507941e2863SAndrew Thompson 	else if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_BULK) {
1508941e2863SAndrew Thompson 		sc->sc_tty[0].ht_open = 0;
1509941e2863SAndrew Thompson 		usbd_transfer_start(sc->sc_xfer[UHSO_BULK_ENDPT_READ]);
1510941e2863SAndrew Thompson 		if (sc->sc_xfer[UHSO_BULK_ENDPT_INTR] != NULL)
1511941e2863SAndrew Thompson 			usbd_transfer_stop(sc->sc_xfer[UHSO_BULK_ENDPT_INTR]);
1512941e2863SAndrew Thompson 	}
1513941e2863SAndrew Thompson }
1514941e2863SAndrew Thompson 
1515941e2863SAndrew Thompson static void
uhso_ucom_start_write(struct ucom_softc * ucom)1516941e2863SAndrew Thompson uhso_ucom_start_write(struct ucom_softc *ucom)
1517941e2863SAndrew Thompson {
1518941e2863SAndrew Thompson 	struct uhso_softc *sc = ucom->sc_parent;
1519941e2863SAndrew Thompson 
1520941e2863SAndrew Thompson 	if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_MUX) {
1521015bb88fSNick Hibma 		UHSO_DPRINTF(3, "local unit %d\n", ucom->sc_subunit);
1522941e2863SAndrew Thompson 
1523941e2863SAndrew Thompson 		usbd_transfer_start(sc->sc_xfer[UHSO_MUX_ENDPT_INTR]);
1524941e2863SAndrew Thompson 
1525941e2863SAndrew Thompson 		usbd_xfer_set_priv(
1526015bb88fSNick Hibma 		    sc->sc_tty[ucom->sc_subunit].ht_xfer[UHSO_CTRL_WRITE],
1527015bb88fSNick Hibma 		    &sc->sc_tty[ucom->sc_subunit]);
1528941e2863SAndrew Thompson 		usbd_transfer_start(
1529015bb88fSNick Hibma 		    sc->sc_tty[ucom->sc_subunit].ht_xfer[UHSO_CTRL_WRITE]);
1530941e2863SAndrew Thompson 	}
1531941e2863SAndrew Thompson 	else if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_BULK) {
1532941e2863SAndrew Thompson 		usbd_transfer_start(sc->sc_xfer[UHSO_BULK_ENDPT_WRITE]);
1533941e2863SAndrew Thompson 	}
1534941e2863SAndrew Thompson }
1535941e2863SAndrew Thompson 
1536941e2863SAndrew Thompson static void
uhso_ucom_stop_write(struct ucom_softc * ucom)1537941e2863SAndrew Thompson uhso_ucom_stop_write(struct ucom_softc *ucom)
1538941e2863SAndrew Thompson {
1539941e2863SAndrew Thompson 	struct uhso_softc *sc = ucom->sc_parent;
1540941e2863SAndrew Thompson 
1541941e2863SAndrew Thompson 	if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_MUX) {
1542941e2863SAndrew Thompson 		usbd_transfer_stop(
1543015bb88fSNick Hibma 		    sc->sc_tty[ucom->sc_subunit].ht_xfer[UHSO_CTRL_WRITE]);
1544941e2863SAndrew Thompson 	}
1545941e2863SAndrew Thompson 	else if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_BULK) {
1546941e2863SAndrew Thompson 		usbd_transfer_stop(sc->sc_xfer[UHSO_BULK_ENDPT_WRITE]);
1547941e2863SAndrew Thompson 	}
1548941e2863SAndrew Thompson }
1549941e2863SAndrew Thompson 
155021a9d6e7SAndrew Thompson static int
uhso_attach_ifnet(struct uhso_softc * sc,struct usb_interface * iface,int type)155121a9d6e7SAndrew Thompson uhso_attach_ifnet(struct uhso_softc *sc, struct usb_interface *iface, int type)
1552941e2863SAndrew Thompson {
1553935b194dSJustin Hibbits 	if_t ifp;
1554941e2863SAndrew Thompson 	usb_error_t uerr;
1555941e2863SAndrew Thompson 	struct sysctl_ctx_list *sctx;
1556941e2863SAndrew Thompson 	struct sysctl_oid *soid;
155762d42655SHans Petter Selasky 	unsigned devunit;
1558941e2863SAndrew Thompson 
1559941e2863SAndrew Thompson 	uerr = usbd_transfer_setup(sc->sc_udev,
1560941e2863SAndrew Thompson 	    &iface->idesc->bInterfaceNumber, sc->sc_if_xfer,
1561941e2863SAndrew Thompson 	    uhso_ifnet_config, UHSO_IFNET_MAX, sc, &sc->sc_mtx);
1562941e2863SAndrew Thompson 	if (uerr) {
1563941e2863SAndrew Thompson 		UHSO_DPRINTF(0, "usbd_transfer_setup failed: %s\n",
1564941e2863SAndrew Thompson 		    usbd_errstr(uerr));
1565941e2863SAndrew Thompson 		return (-1);
1566941e2863SAndrew Thompson 	}
1567941e2863SAndrew Thompson 
15689b6ffc1fSAndrew Thompson 	sc->sc_ifp = ifp = if_alloc(IFT_OTHER);
1569941e2863SAndrew Thompson 
1570941e2863SAndrew Thompson 	callout_init_mtx(&sc->sc_c, &sc->sc_mtx, 0);
1571941e2863SAndrew Thompson 	mtx_lock(&sc->sc_mtx);
1572941e2863SAndrew Thompson 	callout_reset(&sc->sc_c, 1, uhso_if_rxflush, sc);
1573941e2863SAndrew Thompson 	mtx_unlock(&sc->sc_mtx);
1574941e2863SAndrew Thompson 
157521a9d6e7SAndrew Thompson 	/*
157621a9d6e7SAndrew Thompson 	 * We create our own unit numbers for ifnet devices because the
157721a9d6e7SAndrew Thompson 	 * USB interface unit numbers can be at arbitrary positions yielding
157821a9d6e7SAndrew Thompson 	 * odd looking device names.
157921a9d6e7SAndrew Thompson 	 */
158021a9d6e7SAndrew Thompson 	devunit = alloc_unr(uhso_ifnet_unit);
158121a9d6e7SAndrew Thompson 
158221a9d6e7SAndrew Thompson 	if_initname(ifp, device_get_name(sc->sc_dev), devunit);
1583935b194dSJustin Hibbits 	if_setmtu(ifp, UHSO_MAX_MTU);
1584935b194dSJustin Hibbits 	if_setioctlfn(ifp, uhso_if_ioctl);
1585935b194dSJustin Hibbits 	if_setinitfn(ifp, uhso_if_init);
1586935b194dSJustin Hibbits 	if_setstartfn(ifp, uhso_if_start);
1587935b194dSJustin Hibbits 	if_setoutputfn(ifp, uhso_if_output);
1588935b194dSJustin Hibbits 	if_setflags(ifp, IFF_BROADCAST | IFF_MULTICAST | IFF_NOARP);
1589935b194dSJustin Hibbits 	if_setsoftc(ifp, sc);
1590935b194dSJustin Hibbits 	if_setsendqlen(ifp, ifqmaxlen);
1591935b194dSJustin Hibbits 	if_setsendqready(ifp);
1592941e2863SAndrew Thompson 
1593941e2863SAndrew Thompson 	if_attach(ifp);
1594941e2863SAndrew Thompson 	bpfattach(ifp, DLT_RAW, 0);
1595941e2863SAndrew Thompson 
1596941e2863SAndrew Thompson 	sctx = device_get_sysctl_ctx(sc->sc_dev);
1597941e2863SAndrew Thompson 	soid = device_get_sysctl_tree(sc->sc_dev);
1598941e2863SAndrew Thompson 	/* Unlocked read... */
1599935b194dSJustin Hibbits 	SYSCTL_ADD_CONST_STRING(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "netif",
1600935b194dSJustin Hibbits 	    CTLFLAG_RD, if_name(ifp), "Attached network interface");
1601941e2863SAndrew Thompson 
1602941e2863SAndrew Thompson 	return (0);
1603941e2863SAndrew Thompson }
1604941e2863SAndrew Thompson 
1605941e2863SAndrew Thompson static void
uhso_ifnet_read_callback(struct usb_xfer * xfer,usb_error_t error)1606941e2863SAndrew Thompson uhso_ifnet_read_callback(struct usb_xfer *xfer, usb_error_t error)
1607941e2863SAndrew Thompson {
1608941e2863SAndrew Thompson 	struct uhso_softc *sc = usbd_xfer_softc(xfer);
1609941e2863SAndrew Thompson 	struct mbuf *m;
1610941e2863SAndrew Thompson 	struct usb_page_cache *pc;
1611941e2863SAndrew Thompson 	int actlen;
1612941e2863SAndrew Thompson 
1613941e2863SAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1614941e2863SAndrew Thompson 
1615941e2863SAndrew Thompson 	UHSO_DPRINTF(3, "status=%d, actlen=%d\n", USB_GET_STATE(xfer), actlen);
1616941e2863SAndrew Thompson 
1617941e2863SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
1618941e2863SAndrew Thompson 	case USB_ST_TRANSFERRED:
1619935b194dSJustin Hibbits 		if (actlen > 0 && (if_getdrvflags(sc->sc_ifp) & IFF_DRV_RUNNING)) {
1620941e2863SAndrew Thompson 			pc = usbd_xfer_get_frame(xfer, 0);
162132c4f3bbSGleb Smirnoff 			if (mbufq_full(&sc->sc_rxq))
162232c4f3bbSGleb Smirnoff 				break;
1623c6499eccSGleb Smirnoff 			m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
1624941e2863SAndrew Thompson 			usbd_copy_out(pc, 0, mtod(m, uint8_t *), actlen);
1625941e2863SAndrew Thompson 			m->m_pkthdr.len = m->m_len = actlen;
16269b6ffc1fSAndrew Thompson 			/* Enqueue frame for further processing */
162732c4f3bbSGleb Smirnoff 			mbufq_enqueue(&sc->sc_rxq, m);
1628941e2863SAndrew Thompson 			if (!callout_pending(&sc->sc_c) ||
1629941e2863SAndrew Thompson 			    !callout_active(&sc->sc_c)) {
1630941e2863SAndrew Thompson 				callout_schedule(&sc->sc_c, 1);
1631941e2863SAndrew Thompson 			}
1632941e2863SAndrew Thompson 		}
1633941e2863SAndrew Thompson 	/* FALLTHROUGH */
1634941e2863SAndrew Thompson 	case USB_ST_SETUP:
1635941e2863SAndrew Thompson tr_setup:
1636941e2863SAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
1637941e2863SAndrew Thompson 		usbd_transfer_submit(xfer);
1638941e2863SAndrew Thompson 		break;
1639941e2863SAndrew Thompson 	default:
1640941e2863SAndrew Thompson 		UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error));
1641941e2863SAndrew Thompson 		if (error == USB_ERR_CANCELLED)
1642941e2863SAndrew Thompson 			break;
1643941e2863SAndrew Thompson 		usbd_xfer_set_stall(xfer);
1644941e2863SAndrew Thompson 		goto tr_setup;
1645941e2863SAndrew Thompson 	}
1646941e2863SAndrew Thompson }
1647941e2863SAndrew Thompson 
1648941e2863SAndrew Thompson /*
16499b6ffc1fSAndrew Thompson  * Deferred RX processing, called with mutex locked.
16509b6ffc1fSAndrew Thompson  *
16519b6ffc1fSAndrew Thompson  * Each frame we receive might contain several small ip-packets as well
16529b6ffc1fSAndrew Thompson  * as partial ip-packets. We need to separate/assemble them into individual
16539b6ffc1fSAndrew Thompson  * packets before sending them to the ip-layer.
1654941e2863SAndrew Thompson  */
1655941e2863SAndrew Thompson static void
uhso_if_rxflush(void * arg)1656941e2863SAndrew Thompson uhso_if_rxflush(void *arg)
1657941e2863SAndrew Thompson {
1658b8a6e03fSGleb Smirnoff 	struct epoch_tracker et;
1659941e2863SAndrew Thompson 	struct uhso_softc *sc = arg;
1660935b194dSJustin Hibbits 	if_t ifp = sc->sc_ifp;
1661941e2863SAndrew Thompson 	uint8_t *cp;
1662941e2863SAndrew Thompson 	struct mbuf *m, *m0, *mwait;
1663941e2863SAndrew Thompson 	struct ip *ip;
1664941e2863SAndrew Thompson #ifdef INET6
1665941e2863SAndrew Thompson 	struct ip6_hdr *ip6;
1666941e2863SAndrew Thompson #endif
1667941e2863SAndrew Thompson 	uint16_t iplen;
1668925bcbd6SMark Johnston 	int isr;
1669941e2863SAndrew Thompson 
1670941e2863SAndrew Thompson 	m = NULL;
1671941e2863SAndrew Thompson 	mwait = sc->sc_mwait;
1672b8a6e03fSGleb Smirnoff 	NET_EPOCH_ENTER(et);
1673941e2863SAndrew Thompson 	for (;;) {
1674941e2863SAndrew Thompson 		if (m == NULL) {
167532c4f3bbSGleb Smirnoff 			if ((m = mbufq_dequeue(&sc->sc_rxq)) == NULL)
1676941e2863SAndrew Thompson 				break;
16779b6ffc1fSAndrew Thompson 			UHSO_DPRINTF(3, "dequeue m=%p, len=%d\n", m, m->m_len);
1678941e2863SAndrew Thompson 		}
1679941e2863SAndrew Thompson 		mtx_unlock(&sc->sc_mtx);
1680941e2863SAndrew Thompson 
1681941e2863SAndrew Thompson 		/* Do we have a partial packet waiting? */
1682941e2863SAndrew Thompson 		if (mwait != NULL) {
1683941e2863SAndrew Thompson 			m0 = mwait;
1684941e2863SAndrew Thompson 			mwait = NULL;
1685941e2863SAndrew Thompson 
16869b6ffc1fSAndrew Thompson 			UHSO_DPRINTF(3, "partial m0=%p(%d), concat w/ m=%p(%d)\n",
1687941e2863SAndrew Thompson 			    m0, m0->m_len, m, m->m_len);
1688941e2863SAndrew Thompson 
1689925bcbd6SMark Johnston 			m_catpkt(m0, m);
1690941e2863SAndrew Thompson 			m = m_pullup(m0, sizeof(struct ip));
1691941e2863SAndrew Thompson 			if (m == NULL) {
1692ecc70d3fSGleb Smirnoff 				if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
1693941e2863SAndrew Thompson 				UHSO_DPRINTF(0, "m_pullup failed\n");
1694941e2863SAndrew Thompson 				mtx_lock(&sc->sc_mtx);
1695941e2863SAndrew Thompson 				continue;
1696941e2863SAndrew Thompson 			}
16979b6ffc1fSAndrew Thompson 			UHSO_DPRINTF(3, "Constructed mbuf=%p, len=%d\n",
1698941e2863SAndrew Thompson 			    m, m->m_pkthdr.len);
1699941e2863SAndrew Thompson 		}
1700941e2863SAndrew Thompson 
1701941e2863SAndrew Thompson 		cp = mtod(m, uint8_t *);
1702941e2863SAndrew Thompson 		ip = (struct ip *)cp;
1703941e2863SAndrew Thompson #ifdef INET6
1704941e2863SAndrew Thompson 		ip6 = (struct ip6_hdr *)cp;
1705941e2863SAndrew Thompson #endif
1706941e2863SAndrew Thompson 
1707941e2863SAndrew Thompson 		/* Check for IPv4 */
1708941e2863SAndrew Thompson 		if (ip->ip_v == IPVERSION) {
1709941e2863SAndrew Thompson 			iplen = htons(ip->ip_len);
1710941e2863SAndrew Thompson 			isr = NETISR_IP;
1711941e2863SAndrew Thompson 		}
1712941e2863SAndrew Thompson #ifdef INET6
1713941e2863SAndrew Thompson 		/* Check for IPv6 */
1714941e2863SAndrew Thompson 		else if ((ip6->ip6_vfc & IPV6_VERSION_MASK) == IPV6_VERSION) {
1715941e2863SAndrew Thompson 			iplen = htons(ip6->ip6_plen);
1716941e2863SAndrew Thompson 			isr = NETISR_IPV6;
1717941e2863SAndrew Thompson 		}
1718941e2863SAndrew Thompson #endif
1719941e2863SAndrew Thompson 		else {
1720941e2863SAndrew Thompson 			UHSO_DPRINTF(0, "got unexpected ip version %d, "
1721941e2863SAndrew Thompson 			    "m=%p, len=%d\n", (*cp & 0xf0) >> 4, m, m->m_len);
1722ecc70d3fSGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
1723941e2863SAndrew Thompson 			UHSO_HEXDUMP(cp, 4);
1724941e2863SAndrew Thompson 			m_freem(m);
1725941e2863SAndrew Thompson 			m = NULL;
1726941e2863SAndrew Thompson 			mtx_lock(&sc->sc_mtx);
1727941e2863SAndrew Thompson 			continue;
1728941e2863SAndrew Thompson 		}
1729941e2863SAndrew Thompson 
1730941e2863SAndrew Thompson 		if (iplen == 0) {
1731941e2863SAndrew Thompson 			UHSO_DPRINTF(0, "Zero IP length\n");
1732ecc70d3fSGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
1733941e2863SAndrew Thompson 			m_freem(m);
1734941e2863SAndrew Thompson 			m = NULL;
1735941e2863SAndrew Thompson 			mtx_lock(&sc->sc_mtx);
1736941e2863SAndrew Thompson 			continue;
1737941e2863SAndrew Thompson 		}
1738941e2863SAndrew Thompson 
17399b6ffc1fSAndrew Thompson 		UHSO_DPRINTF(3, "m=%p, len=%d, cp=%p, iplen=%d\n",
1740941e2863SAndrew Thompson 		    m, m->m_pkthdr.len, cp, iplen);
1741941e2863SAndrew Thompson 
1742941e2863SAndrew Thompson 		m0 = NULL;
1743941e2863SAndrew Thompson 
1744941e2863SAndrew Thompson 		/* More IP packets in this mbuf */
1745941e2863SAndrew Thompson 		if (iplen < m->m_pkthdr.len) {
1746941e2863SAndrew Thompson 			m0 = m;
1747941e2863SAndrew Thompson 
1748941e2863SAndrew Thompson 			/*
1749941e2863SAndrew Thompson 			 * Allocate a new mbuf for this IP packet and
1750941e2863SAndrew Thompson 			 * copy the IP-packet into it.
1751941e2863SAndrew Thompson 			 */
1752b8acc2d6SConrad Meyer 			m = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR);
1753271ae033SHans Petter Selasky 			memcpy(mtod(m, uint8_t *), mtod(m0, uint8_t *), iplen);
1754941e2863SAndrew Thompson 			m->m_pkthdr.len = m->m_len = iplen;
1755941e2863SAndrew Thompson 
1756941e2863SAndrew Thompson 			/* Adjust the size of the original mbuf */
1757941e2863SAndrew Thompson 			m_adj(m0, iplen);
1758c6499eccSGleb Smirnoff 			m0 = m_defrag(m0, M_WAITOK);
1759941e2863SAndrew Thompson 
17609b6ffc1fSAndrew Thompson 			UHSO_DPRINTF(3, "New mbuf=%p, len=%d/%d, m0=%p, "
1761941e2863SAndrew Thompson 			    "m0_len=%d/%d\n", m, m->m_pkthdr.len, m->m_len,
1762941e2863SAndrew Thompson 			    m0, m0->m_pkthdr.len, m0->m_len);
1763941e2863SAndrew Thompson 		}
1764941e2863SAndrew Thompson 		else if (iplen > m->m_pkthdr.len) {
17659b6ffc1fSAndrew Thompson 			UHSO_DPRINTF(3, "Deferred mbuf=%p, len=%d\n",
1766941e2863SAndrew Thompson 			    m, m->m_pkthdr.len);
1767941e2863SAndrew Thompson 			mwait = m;
1768941e2863SAndrew Thompson 			m = NULL;
1769941e2863SAndrew Thompson 			mtx_lock(&sc->sc_mtx);
1770941e2863SAndrew Thompson 			continue;
1771941e2863SAndrew Thompson 		}
1772941e2863SAndrew Thompson 
1773ecc70d3fSGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
1774941e2863SAndrew Thompson 		m->m_pkthdr.rcvif = ifp;
1775941e2863SAndrew Thompson 
1776941e2863SAndrew Thompson 		/* Dispatch to IP layer */
1777941e2863SAndrew Thompson 		BPF_MTAP(sc->sc_ifp, m);
1778935b194dSJustin Hibbits 		M_SETFIB(m, if_getfib(ifp));
1779941e2863SAndrew Thompson 		netisr_dispatch(isr, m);
1780941e2863SAndrew Thompson 		m = m0 != NULL ? m0 : NULL;
1781941e2863SAndrew Thompson 		mtx_lock(&sc->sc_mtx);
1782941e2863SAndrew Thompson 	}
1783b8a6e03fSGleb Smirnoff 	NET_EPOCH_EXIT(et);
1784941e2863SAndrew Thompson 	sc->sc_mwait = mwait;
1785941e2863SAndrew Thompson }
1786941e2863SAndrew Thompson 
1787941e2863SAndrew Thompson static void
uhso_ifnet_write_callback(struct usb_xfer * xfer,usb_error_t error)1788941e2863SAndrew Thompson uhso_ifnet_write_callback(struct usb_xfer *xfer, usb_error_t error)
1789941e2863SAndrew Thompson {
1790941e2863SAndrew Thompson 	struct uhso_softc *sc = usbd_xfer_softc(xfer);
1791935b194dSJustin Hibbits 	if_t ifp = sc->sc_ifp;
1792941e2863SAndrew Thompson 	struct usb_page_cache *pc;
1793941e2863SAndrew Thompson 	struct mbuf *m;
1794941e2863SAndrew Thompson 	int actlen;
1795941e2863SAndrew Thompson 
1796941e2863SAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1797941e2863SAndrew Thompson 
1798941e2863SAndrew Thompson 	UHSO_DPRINTF(3, "status %d, actlen=%d\n", USB_GET_STATE(xfer), actlen);
1799941e2863SAndrew Thompson 
1800941e2863SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
1801941e2863SAndrew Thompson 	case USB_ST_TRANSFERRED:
1802ecc70d3fSGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
1803935b194dSJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
1804941e2863SAndrew Thompson 	case USB_ST_SETUP:
1805941e2863SAndrew Thompson tr_setup:
1806935b194dSJustin Hibbits 		m = if_dequeue(ifp);
1807941e2863SAndrew Thompson 		if (m == NULL)
1808941e2863SAndrew Thompson 			break;
1809941e2863SAndrew Thompson 
1810935b194dSJustin Hibbits 		if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
1811941e2863SAndrew Thompson 
1812941e2863SAndrew Thompson 		if (m->m_pkthdr.len > MCLBYTES)
1813941e2863SAndrew Thompson 			m->m_pkthdr.len = MCLBYTES;
1814941e2863SAndrew Thompson 
1815941e2863SAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);
1816941e2863SAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
1817941e2863SAndrew Thompson 		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
1818941e2863SAndrew Thompson 		usbd_transfer_submit(xfer);
1819941e2863SAndrew Thompson 
1820941e2863SAndrew Thompson 		BPF_MTAP(ifp, m);
1821941e2863SAndrew Thompson 		m_freem(m);
1822941e2863SAndrew Thompson 		break;
1823941e2863SAndrew Thompson 	default:
1824941e2863SAndrew Thompson 		UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error));
1825941e2863SAndrew Thompson 		if (error == USB_ERR_CANCELLED)
1826941e2863SAndrew Thompson 			break;
1827941e2863SAndrew Thompson 		usbd_xfer_set_stall(xfer);
1828941e2863SAndrew Thompson 		goto tr_setup;
1829941e2863SAndrew Thompson 	}
1830941e2863SAndrew Thompson }
1831941e2863SAndrew Thompson 
1832941e2863SAndrew Thompson static int
uhso_if_ioctl(if_t ifp,u_long cmd,caddr_t data)1833935b194dSJustin Hibbits uhso_if_ioctl(if_t ifp, u_long cmd, caddr_t data)
1834941e2863SAndrew Thompson {
1835941e2863SAndrew Thompson 	struct uhso_softc *sc;
1836941e2863SAndrew Thompson 
1837935b194dSJustin Hibbits 	sc = if_getsoftc(ifp);
1838941e2863SAndrew Thompson 
1839941e2863SAndrew Thompson 	switch (cmd) {
1840941e2863SAndrew Thompson 	case SIOCSIFFLAGS:
1841935b194dSJustin Hibbits 		if (if_getflags(ifp) & IFF_UP) {
1842935b194dSJustin Hibbits 			if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING)) {
1843941e2863SAndrew Thompson 				uhso_if_init(sc);
1844941e2863SAndrew Thompson 			}
1845941e2863SAndrew Thompson 		}
1846941e2863SAndrew Thompson 		else {
1847935b194dSJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
1848941e2863SAndrew Thompson 				mtx_lock(&sc->sc_mtx);
1849941e2863SAndrew Thompson 				uhso_if_stop(sc);
1850941e2863SAndrew Thompson 				mtx_unlock(&sc->sc_mtx);
1851941e2863SAndrew Thompson 			}
1852941e2863SAndrew Thompson 		}
1853941e2863SAndrew Thompson 		break;
1854941e2863SAndrew Thompson 	case SIOCSIFADDR:
1855941e2863SAndrew Thompson 	case SIOCADDMULTI:
1856941e2863SAndrew Thompson 	case SIOCDELMULTI:
1857941e2863SAndrew Thompson 		break;
1858941e2863SAndrew Thompson 	default:
1859941e2863SAndrew Thompson 		return (EINVAL);
1860941e2863SAndrew Thompson 	}
1861941e2863SAndrew Thompson 	return (0);
1862941e2863SAndrew Thompson }
1863941e2863SAndrew Thompson 
1864941e2863SAndrew Thompson static void
uhso_if_init(void * priv)1865941e2863SAndrew Thompson uhso_if_init(void *priv)
1866941e2863SAndrew Thompson {
1867941e2863SAndrew Thompson 	struct uhso_softc *sc = priv;
1868935b194dSJustin Hibbits 	if_t ifp = sc->sc_ifp;
1869941e2863SAndrew Thompson 
1870941e2863SAndrew Thompson 	mtx_lock(&sc->sc_mtx);
1871941e2863SAndrew Thompson 	uhso_if_stop(sc);
1872941e2863SAndrew Thompson 	ifp = sc->sc_ifp;
1873935b194dSJustin Hibbits 	if_setflagbits(ifp, IFF_UP, 0);
1874935b194dSJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
1875941e2863SAndrew Thompson 	mtx_unlock(&sc->sc_mtx);
1876941e2863SAndrew Thompson 
18779b6ffc1fSAndrew Thompson 	UHSO_DPRINTF(2, "ifnet initialized\n");
1878941e2863SAndrew Thompson }
1879941e2863SAndrew Thompson 
1880941e2863SAndrew Thompson static int
uhso_if_output(if_t ifp,struct mbuf * m0,const struct sockaddr * dst,struct route * ro)1881935b194dSJustin Hibbits uhso_if_output(if_t ifp, struct mbuf *m0, const struct sockaddr *dst,
1882941e2863SAndrew Thompson     struct route *ro)
1883941e2863SAndrew Thompson {
1884941e2863SAndrew Thompson 	int error;
1885941e2863SAndrew Thompson 
1886941e2863SAndrew Thompson 	/* Only IPv4/6 support */
1887941e2863SAndrew Thompson 	if (dst->sa_family != AF_INET
1888941e2863SAndrew Thompson #ifdef INET6
1889941e2863SAndrew Thompson 	   && dst->sa_family != AF_INET6
1890941e2863SAndrew Thompson #endif
1891941e2863SAndrew Thompson 	 ) {
1892941e2863SAndrew Thompson 		return (EAFNOSUPPORT);
1893941e2863SAndrew Thompson 	}
1894941e2863SAndrew Thompson 
1895935b194dSJustin Hibbits 	error = if_transmit(ifp, m0);
1896941e2863SAndrew Thompson 	if (error) {
1897ecc70d3fSGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
1898941e2863SAndrew Thompson 		return (ENOBUFS);
1899941e2863SAndrew Thompson 	}
1900ecc70d3fSGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
1901941e2863SAndrew Thompson 	return (0);
1902941e2863SAndrew Thompson }
1903941e2863SAndrew Thompson 
1904941e2863SAndrew Thompson static void
uhso_if_start(if_t ifp)1905935b194dSJustin Hibbits uhso_if_start(if_t ifp)
1906941e2863SAndrew Thompson {
1907935b194dSJustin Hibbits 	struct uhso_softc *sc = if_getsoftc(ifp);
1908941e2863SAndrew Thompson 
1909935b194dSJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) {
1910941e2863SAndrew Thompson 		UHSO_DPRINTF(1, "Not running\n");
1911941e2863SAndrew Thompson 		return;
1912941e2863SAndrew Thompson 	}
1913941e2863SAndrew Thompson 
1914941e2863SAndrew Thompson 	mtx_lock(&sc->sc_mtx);
1915941e2863SAndrew Thompson 	usbd_transfer_start(sc->sc_if_xfer[UHSO_IFNET_READ]);
1916941e2863SAndrew Thompson 	usbd_transfer_start(sc->sc_if_xfer[UHSO_IFNET_WRITE]);
1917941e2863SAndrew Thompson 	mtx_unlock(&sc->sc_mtx);
1918941e2863SAndrew Thompson 	UHSO_DPRINTF(3, "interface started\n");
1919941e2863SAndrew Thompson }
1920941e2863SAndrew Thompson 
1921941e2863SAndrew Thompson static void
uhso_if_stop(struct uhso_softc * sc)1922941e2863SAndrew Thompson uhso_if_stop(struct uhso_softc *sc)
1923941e2863SAndrew Thompson {
1924941e2863SAndrew Thompson 
1925941e2863SAndrew Thompson 	usbd_transfer_stop(sc->sc_if_xfer[UHSO_IFNET_READ]);
1926941e2863SAndrew Thompson 	usbd_transfer_stop(sc->sc_if_xfer[UHSO_IFNET_WRITE]);
1927935b194dSJustin Hibbits 	if_setdrvflagbits(sc->sc_ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE));
1928941e2863SAndrew Thompson }
1929