uhso.c revision 202181
1202181Sthompsa/*-
2202181Sthompsa * Copyright (c) 2009 Fredrik Lindberg
3202181Sthompsa * All rights reserved.
4202181Sthompsa *
5202181Sthompsa * Redistribution and use in source and binary forms, with or without
6202181Sthompsa * modification, are permitted provided that the following conditions
7202181Sthompsa * are met:
8202181Sthompsa * 1. Redistributions of source code must retain the above copyright
9202181Sthompsa *    notice, this list of conditions and the following disclaimer.
10202181Sthompsa * 2. Redistributions in binary form must reproduce the above copyright
11202181Sthompsa *    notice, this list of conditions and the following disclaimer in the
12202181Sthompsa *    documentation and/or other materials provided with the distribution.
13202181Sthompsa *
14202181Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15202181Sthompsa * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16202181Sthompsa * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17202181Sthompsa * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18202181Sthompsa * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19202181Sthompsa * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20202181Sthompsa * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21202181Sthompsa * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22202181Sthompsa * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23202181Sthompsa * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24202181Sthompsa *
25202181Sthompsa */
26202181Sthompsa#include <sys/cdefs.h>
27202181Sthompsa__FBSDID("$FreeBSD: head/sys/dev/usb/net/uhso.c 202181 2010-01-13 03:16:31Z thompsa $");
28202181Sthompsa
29202181Sthompsa#include <sys/param.h>
30202181Sthompsa#include <sys/types.h>
31202181Sthompsa#include <sys/sockio.h>
32202181Sthompsa#include <sys/mbuf.h>
33202181Sthompsa#include <sys/malloc.h>
34202181Sthompsa#include <sys/kernel.h>
35202181Sthompsa#include <sys/module.h>
36202181Sthompsa#include <sys/socket.h>
37202181Sthompsa#include <sys/tty.h>
38202181Sthompsa#include <sys/sysctl.h>
39202181Sthompsa#include <sys/condvar.h>
40202181Sthompsa#include <sys/sx.h>
41202181Sthompsa#include <sys/proc.h>
42202181Sthompsa#include <sys/conf.h>
43202181Sthompsa#include <sys/bus.h>
44202181Sthompsa#include <sys/systm.h>
45202181Sthompsa
46202181Sthompsa#include <machine/bus.h>
47202181Sthompsa
48202181Sthompsa#include <net/if.h>
49202181Sthompsa#include <net/if_types.h>
50202181Sthompsa#include <net/netisr.h>
51202181Sthompsa#include <net/bpf.h>
52202181Sthompsa#include <netinet/in.h>
53202181Sthompsa#include <netinet/ip.h>
54202181Sthompsa#include <netinet/ip6.h>
55202181Sthompsa
56202181Sthompsa#include <dev/usb/usb.h>
57202181Sthompsa#include <dev/usb/usbdi.h>
58202181Sthompsa#include <dev/usb/usbdi_util.h>
59202181Sthompsa#include <dev/usb/usb_cdc.h>
60202181Sthompsa#include "usbdevs.h"
61202181Sthompsa#define USB_DEBUG_VAR uhso_debug
62202181Sthompsa#include <dev/usb/usb_debug.h>
63202181Sthompsa#include <dev/usb/usb_process.h>
64202181Sthompsa#include <dev/usb/usb_device.h>
65202181Sthompsa#include <dev/usb/usb_busdma.h>
66202181Sthompsa#include <dev/usb/serial/usb_serial.h>
67202181Sthompsa#include <dev/usb/usb_msctest.h>
68202181Sthompsa
69202181Sthompsastruct uhso_tty {
70202181Sthompsa	struct uhso_softc *ht_sc;
71202181Sthompsa	struct usb_xfer	*ht_xfer[3];
72202181Sthompsa	int		ht_muxport;
73202181Sthompsa	int		ht_open;
74202181Sthompsa	char		ht_name[32];
75202181Sthompsa};
76202181Sthompsa
77202181Sthompsastruct uhso_softc {
78202181Sthompsa	device_t		sc_dev;
79202181Sthompsa	struct usb_device	*sc_udev;
80202181Sthompsa	struct mtx		sc_mtx;
81202181Sthompsa	uint32_t		sc_type;
82202181Sthompsa
83202181Sthompsa	struct usb_xfer		*sc_xfer[3];
84202181Sthompsa	uint8_t			sc_iface_no;
85202181Sthompsa	uint8_t			sc_iface_index;
86202181Sthompsa
87202181Sthompsa	/* Control pipe */
88202181Sthompsa	struct usb_xfer	*	sc_ctrl_xfer[2];
89202181Sthompsa	uint8_t			sc_ctrl_iface_no;
90202181Sthompsa
91202181Sthompsa	/* Network */
92202181Sthompsa	struct usb_xfer		*sc_if_xfer[2];
93202181Sthompsa	struct ifnet		*sc_ifp;
94202181Sthompsa	struct mbuf		*sc_mwait;	/* partial packet */
95202181Sthompsa	size_t			sc_waitlen;	/* no. of outstanding bytes */
96202181Sthompsa	struct ifqueue		sc_rxq;
97202181Sthompsa	struct callout		sc_c;
98202181Sthompsa
99202181Sthompsa	/* TTY related structures */
100202181Sthompsa	struct ucom_super_softc sc_super_ucom;
101202181Sthompsa	int 			sc_ttys;
102202181Sthompsa	struct uhso_tty		*sc_tty;
103202181Sthompsa	struct ucom_softc	*sc_ucom;
104202181Sthompsa	int			sc_msr;
105202181Sthompsa	int			sc_lsr;
106202181Sthompsa	int			sc_line;
107202181Sthompsa};
108202181Sthompsa
109202181Sthompsa
110202181Sthompsa#define UHSO_MAX_MTU		2048
111202181Sthompsa
112202181Sthompsa/*
113202181Sthompsa * There are mainly two type of cards floating around.
114202181Sthompsa * The first one has 2,3 or 4 interfaces with a multiplexed serial port
115202181Sthompsa * and packet interface on the first interface and bulk serial ports
116202181Sthompsa * on the others.
117202181Sthompsa * The second type of card has several other interfaces, their purpose
118202181Sthompsa * can be detected during run-time.
119202181Sthompsa */
120202181Sthompsa#define UHSO_IFACE_SPEC(usb_type, port, port_type) \
121202181Sthompsa	(((usb_type) << 24) | ((port) << 16) | (port_type))
122202181Sthompsa
123202181Sthompsa#define UHSO_IFACE_USB_TYPE(x) ((x >> 24) & 0xff)
124202181Sthompsa#define UHSO_IFACE_PORT(x) ((x >> 16) & 0xff)
125202181Sthompsa#define UHSO_IFACE_PORT_TYPE(x) (x & 0xff)
126202181Sthompsa
127202181Sthompsa/*
128202181Sthompsa * USB interface types
129202181Sthompsa */
130202181Sthompsa#define UHSO_IF_NET		0x01	/* Network packet interface */
131202181Sthompsa#define UHSO_IF_MUX		0x02	/* Multiplexed serial port */
132202181Sthompsa#define UHSO_IF_BULK		0x04	/* Bulk interface */
133202181Sthompsa
134202181Sthompsa/*
135202181Sthompsa * Port types
136202181Sthompsa */
137202181Sthompsa#define UHSO_PORT_UNKNOWN	0x00
138202181Sthompsa#define UHSO_PORT_SERIAL		0x01	/* Serial port */
139202181Sthompsa#define UHSO_PORT_NETWORK	0x02	/* Network packet interface */
140202181Sthompsa
141202181Sthompsa/*
142202181Sthompsa * Multiplexed serial port destination sub-port names
143202181Sthompsa */
144202181Sthompsa#define UHSO_MPORT_TYPE_CTL	0x00	/* Control port */
145202181Sthompsa#define UHSO_MPORT_TYPE_APP	0x01	/* Application */
146202181Sthompsa#define UHSO_MPORT_TYPE_PCSC	0x02
147202181Sthompsa#define UHSO_MPORT_TYPE_GPS	0x03
148202181Sthompsa#define UHSO_MPORT_TYPE_APP2	0x04
149202181Sthompsa#define UHSO_MPORT_TYPE_MAX	UHSO_MPORT_TYPE_APP2
150202181Sthompsa#define UHSO_MPORT_TYPE_NOMAX	8	/* Max number of mux ports */
151202181Sthompsa
152202181Sthompsa/*
153202181Sthompsa * Port definitions
154202181Sthompsa */
155202181Sthompsa#define UHSO_PORT_TYPE_CTL	0x01
156202181Sthompsa#define UHSO_PORT_TYPE_APP	0x02
157202181Sthompsa#define UHSO_PORT_TYPE_APP2	0x03
158202181Sthompsa#define UHSO_PORT_TYPE_MODEM	0x04
159202181Sthompsa#define UHSO_PORT_TYPE_NETWORK	0x05
160202181Sthompsa#define UHSO_PORT_TYPE_DIAG	0x06
161202181Sthompsa#define UHSO_PORT_TYPE_DIAG2	0x07
162202181Sthompsa#define UHSO_PORT_TYPE_GPS	0x08
163202181Sthompsa#define UHSO_PORT_TYPE_GPSCTL	0x09
164202181Sthompsa#define UHSO_PORT_TYPE_PCSC	0x0a
165202181Sthompsa#define UHSO_PORT_TYPE_MSD	0x0b
166202181Sthompsa#define UHSO_PORT_TYPE_VOICE	0x0c
167202181Sthompsa#define UHSO_PORT_TYPE_MAX	0x0c
168202181Sthompsa
169202181Sthompsastatic eventhandler_tag uhso_etag;
170202181Sthompsa
171202181Sthompsa/* Overall port type */
172202181Sthompsastatic char *uhso_port[] = {
173202181Sthompsa	"Unknown",
174202181Sthompsa	"Serial",
175202181Sthompsa	"Network",
176202181Sthompsa	"Network/Serial"
177202181Sthompsa};
178202181Sthompsa
179202181Sthompsa/* Map between interface port type read from device and description type */
180202181Sthompsastatic char uhso_port_map[] = {
181202181Sthompsa	0,
182202181Sthompsa	UHSO_PORT_TYPE_DIAG,
183202181Sthompsa	UHSO_PORT_TYPE_GPS,
184202181Sthompsa	UHSO_PORT_TYPE_GPSCTL,
185202181Sthompsa	UHSO_PORT_TYPE_APP,
186202181Sthompsa	UHSO_PORT_TYPE_APP2,
187202181Sthompsa	UHSO_PORT_TYPE_CTL,
188202181Sthompsa	UHSO_PORT_TYPE_NETWORK,
189202181Sthompsa	UHSO_PORT_TYPE_MODEM,
190202181Sthompsa	UHSO_PORT_TYPE_MSD,
191202181Sthompsa	UHSO_PORT_TYPE_PCSC,
192202181Sthompsa	UHSO_PORT_TYPE_VOICE
193202181Sthompsa};
194202181Sthompsastatic char uhso_port_map_max = sizeof(uhso_port_map) / sizeof(char);
195202181Sthompsa
196202181Sthompsastatic char uhso_mux_port_map[] = {
197202181Sthompsa	UHSO_PORT_TYPE_CTL,
198202181Sthompsa	UHSO_PORT_TYPE_APP,
199202181Sthompsa	UHSO_PORT_TYPE_PCSC,
200202181Sthompsa	UHSO_PORT_TYPE_GPS,
201202181Sthompsa	UHSO_PORT_TYPE_APP2
202202181Sthompsa};
203202181Sthompsa
204202181Sthompsastatic char *uhso_port_type[] = {
205202181Sthompsa	"Unknown",
206202181Sthompsa	"Control",
207202181Sthompsa	"Application",
208202181Sthompsa	"Application (Secondary)",
209202181Sthompsa	"Modem",
210202181Sthompsa	"Network",
211202181Sthompsa	"Diagnostic",
212202181Sthompsa	"Diagnostic (Secondary)",
213202181Sthompsa	"GPS",
214202181Sthompsa	"GPS Control",
215202181Sthompsa	"PC Smartcard",
216202181Sthompsa	"MSD",
217202181Sthompsa	"Voice",
218202181Sthompsa};
219202181Sthompsa
220202181Sthompsastatic char *uhso_port_type_sysctl[] = {
221202181Sthompsa	"unknown",
222202181Sthompsa	"control",
223202181Sthompsa	"application",
224202181Sthompsa	"application",
225202181Sthompsa	"modem",
226202181Sthompsa	"network",
227202181Sthompsa	"diagnostic",
228202181Sthompsa	"diagnostic",
229202181Sthompsa	"gps",
230202181Sthompsa	"gps_control",
231202181Sthompsa	"pcsc",
232202181Sthompsa	"msd",
233202181Sthompsa	"voice",
234202181Sthompsa};
235202181Sthompsa
236202181Sthompsa
237202181Sthompsa#define UHSO_STATIC_IFACE	0x01
238202181Sthompsa#define UHSO_AUTO_IFACE		0x02
239202181Sthompsa
240202181Sthompsastatic const struct usb_device_id uhso_devs[] = {
241202181Sthompsa#define	UHSO_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) }
242202181Sthompsa	/* Option GlobeSurfer iCON 7.2 */
243202181Sthompsa	UHSO_DEV(OPTION, GSICON72, UHSO_STATIC_IFACE),
244202181Sthompsa	/* Option iCON 225 */
245202181Sthompsa	UHSO_DEV(OPTION, GTHSDPA, UHSO_STATIC_IFACE),
246202181Sthompsa	/* Option GlobeSurfer iCON HSUPA */
247202181Sthompsa	UHSO_DEV(OPTION, GSICONHSUPA, UHSO_STATIC_IFACE),
248202181Sthompsa	/* Option GlobeTrotter HSUPA */
249202181Sthompsa	UHSO_DEV(OPTION, GTHSUPA, UHSO_STATIC_IFACE),
250202181Sthompsa	/* GE40x */
251202181Sthompsa	UHSO_DEV(OPTION, GE40X, UHSO_AUTO_IFACE),
252202181Sthompsa	UHSO_DEV(OPTION, GE40X_1, UHSO_AUTO_IFACE),
253202181Sthompsa	UHSO_DEV(OPTION, GE40X_2, UHSO_AUTO_IFACE),
254202181Sthompsa	UHSO_DEV(OPTION, GE40X_3, UHSO_AUTO_IFACE),
255202181Sthompsa	/* Option GlobeSurfer iCON 401 */
256202181Sthompsa	UHSO_DEV(OPTION, ICON401, UHSO_AUTO_IFACE),
257202181Sthompsa	/* Option GlobeTrotter Module 382 */
258202181Sthompsa	UHSO_DEV(OPTION, GMT382, UHSO_AUTO_IFACE),
259202181Sthompsa	/* Option iCON EDGE */
260202181Sthompsa	UHSO_DEV(OPTION, ICONEDGE, UHSO_STATIC_IFACE),
261202181Sthompsa	/* Option Module HSxPA */
262202181Sthompsa	UHSO_DEV(OPTION, MODHSXPA, UHSO_STATIC_IFACE),
263202181Sthompsa	/* Option iCON 321 */
264202181Sthompsa	UHSO_DEV(OPTION, ICON321, UHSO_STATIC_IFACE),
265202181Sthompsa	/* Option iCON 322 */
266202181Sthompsa	UHSO_DEV(OPTION, GTICON322, UHSO_STATIC_IFACE)
267202181Sthompsa#undef UHSO_DEV
268202181Sthompsa};
269202181Sthompsa
270202181SthompsaSYSCTL_NODE(_hw_usb, OID_AUTO, uhso, CTLFLAG_RW, 0, "USB uhso");
271202181Sthompsa
272202181Sthompsa#ifdef USB_DEBUG
273202181Sthompsa#ifdef UHSO_DEBUG
274202181Sthompsastatic int uhso_debug = UHSO_DEBUG;
275202181Sthompsa#else
276202181Sthompsastatic int uhso_debug = -1;
277202181Sthompsa#endif
278202181Sthompsa
279202181SthompsaSYSCTL_INT(_hw_usb_uhso, OID_AUTO, debug, CTLFLAG_RW,
280202181Sthompsa    &uhso_debug, 0, "Debug level");
281202181Sthompsa
282202181Sthompsa#define UHSO_DPRINTF(n, x, ...) {\
283202181Sthompsa	if (uhso_debug >= n) {\
284202181Sthompsa		printf("%s: " x, __func__, ##__VA_ARGS__);\
285202181Sthompsa	}\
286202181Sthompsa}
287202181Sthompsa#else
288202181Sthompsa#define UHSO_DPRINTF(n, x, ...)
289202181Sthompsa#endif
290202181Sthompsa
291202181Sthompsa#ifdef UHSO_DEBUG_HEXDUMP
292202181Sthompsa# define UHSO_HEXDUMP(_buf, _len) do { \
293202181Sthompsa  { \
294202181Sthompsa        size_t __tmp; \
295202181Sthompsa        const char *__buf = (const char *)_buf; \
296202181Sthompsa        for (__tmp = 0; __tmp < _len; __tmp++) \
297202181Sthompsa                printf("%02hhx ", *__buf++); \
298202181Sthompsa    printf("\n"); \
299202181Sthompsa  } \
300202181Sthompsa} while(0)
301202181Sthompsa#else
302202181Sthompsa# define UHSO_HEXDUMP(_buf, _len)
303202181Sthompsa#endif
304202181Sthompsa
305202181Sthompsaenum {
306202181Sthompsa	UHSO_MUX_ENDPT_INTR = 0,
307202181Sthompsa	UHSO_MUX_ENDPT_MAX
308202181Sthompsa};
309202181Sthompsa
310202181Sthompsaenum {
311202181Sthompsa	UHSO_CTRL_READ = 0,
312202181Sthompsa	UHSO_CTRL_WRITE,
313202181Sthompsa	UHSO_CTRL_MAX
314202181Sthompsa};
315202181Sthompsa
316202181Sthompsaenum {
317202181Sthompsa	UHSO_IFNET_READ = 0,
318202181Sthompsa	UHSO_IFNET_WRITE,
319202181Sthompsa	UHSO_IFNET_MAX
320202181Sthompsa};
321202181Sthompsa
322202181Sthompsaenum {
323202181Sthompsa	UHSO_BULK_ENDPT_READ = 0,
324202181Sthompsa	UHSO_BULK_ENDPT_WRITE,
325202181Sthompsa	UHSO_BULK_ENDPT_INTR,
326202181Sthompsa	UHSO_BULK_ENDPT_MAX
327202181Sthompsa};
328202181Sthompsa
329202181Sthompsastatic usb_callback_t uhso_mux_intr_callback;
330202181Sthompsastatic usb_callback_t uhso_mux_read_callback;
331202181Sthompsastatic usb_callback_t uhso_mux_write_callback;
332202181Sthompsastatic usb_callback_t uhso_bs_read_callback;
333202181Sthompsastatic usb_callback_t uhso_bs_write_callback;
334202181Sthompsastatic usb_callback_t uhso_bs_intr_callback;
335202181Sthompsastatic usb_callback_t uhso_ifnet_read_callback;
336202181Sthompsastatic usb_callback_t uhso_ifnet_write_callback;
337202181Sthompsa
338202181Sthompsastatic const struct usb_config uhso_ctrl_config[UHSO_CTRL_MAX] = {
339202181Sthompsa	[UHSO_CTRL_READ] = {
340202181Sthompsa		.type = UE_CONTROL,
341202181Sthompsa		.endpoint = 0x00,
342202181Sthompsa		.direction = UE_DIR_ANY,
343202181Sthompsa		.flags = { .pipe_bof = 1, .short_xfer_ok = 1 },
344202181Sthompsa		.bufsize = sizeof(struct usb_device_request) + 1024,
345202181Sthompsa		.callback = &uhso_mux_read_callback
346202181Sthompsa	},
347202181Sthompsa
348202181Sthompsa	[UHSO_CTRL_WRITE] = {
349202181Sthompsa		.type = UE_CONTROL,
350202181Sthompsa		.endpoint = 0x00,
351202181Sthompsa		.direction = UE_DIR_ANY,
352202181Sthompsa		.flags = { .pipe_bof = 1, .force_short_xfer = 1 },
353202181Sthompsa		.bufsize = sizeof(struct usb_device_request) + 1024,
354202181Sthompsa		.timeout = 1000,
355202181Sthompsa		.callback = &uhso_mux_write_callback
356202181Sthompsa	}
357202181Sthompsa};
358202181Sthompsa
359202181Sthompsastatic const struct usb_config uhso_mux_config[UHSO_MUX_ENDPT_MAX] = {
360202181Sthompsa	[UHSO_MUX_ENDPT_INTR] = {
361202181Sthompsa		.type = UE_INTERRUPT,
362202181Sthompsa		.endpoint = UE_ADDR_ANY,
363202181Sthompsa		.direction = UE_DIR_IN,
364202181Sthompsa		.flags = { .short_xfer_ok = 1 },
365202181Sthompsa		.bufsize = 0,
366202181Sthompsa		.callback = &uhso_mux_intr_callback,
367202181Sthompsa	}
368202181Sthompsa};
369202181Sthompsa
370202181Sthompsastatic const struct usb_config uhso_ifnet_config[UHSO_IFNET_MAX] = {
371202181Sthompsa	[UHSO_IFNET_READ] = {
372202181Sthompsa		.type = UE_BULK,
373202181Sthompsa		.endpoint = UE_ADDR_ANY,
374202181Sthompsa		.direction = UE_DIR_IN,
375202181Sthompsa		.flags = { .pipe_bof = 1, .short_xfer_ok = 1 },
376202181Sthompsa		.bufsize = MCLBYTES,
377202181Sthompsa		.callback = &uhso_ifnet_read_callback
378202181Sthompsa	},
379202181Sthompsa	[UHSO_IFNET_WRITE] = {
380202181Sthompsa		.type = UE_BULK,
381202181Sthompsa		.endpoint = UE_ADDR_ANY,
382202181Sthompsa		.direction = UE_DIR_OUT,
383202181Sthompsa		.flags = { .pipe_bof = 1, .force_short_xfer = 1 },
384202181Sthompsa		.bufsize = MCLBYTES,
385202181Sthompsa		.timeout = 5 * USB_MS_HZ,
386202181Sthompsa		.callback = &uhso_ifnet_write_callback
387202181Sthompsa	}
388202181Sthompsa};
389202181Sthompsa
390202181Sthompsastatic const struct usb_config uhso_bs_config[UHSO_BULK_ENDPT_MAX] = {
391202181Sthompsa	[UHSO_BULK_ENDPT_READ] = {
392202181Sthompsa		.type = UE_BULK,
393202181Sthompsa		.endpoint = UE_ADDR_ANY,
394202181Sthompsa		.direction = UE_DIR_IN,
395202181Sthompsa		.flags = { .pipe_bof = 1, .short_xfer_ok = 1 },
396202181Sthompsa		.bufsize = 4096,
397202181Sthompsa		.callback = &uhso_bs_read_callback
398202181Sthompsa	},
399202181Sthompsa
400202181Sthompsa	[UHSO_BULK_ENDPT_WRITE] = {
401202181Sthompsa		.type = UE_BULK,
402202181Sthompsa		.endpoint = UE_ADDR_ANY,
403202181Sthompsa		.direction = UE_DIR_OUT,
404202181Sthompsa		.flags = { .pipe_bof = 1, .force_short_xfer = 1 },
405202181Sthompsa		.bufsize = 8192,
406202181Sthompsa		.callback = &uhso_bs_write_callback
407202181Sthompsa	},
408202181Sthompsa
409202181Sthompsa	[UHSO_BULK_ENDPT_INTR] = {
410202181Sthompsa		.type = UE_INTERRUPT,
411202181Sthompsa		.endpoint = UE_ADDR_ANY,
412202181Sthompsa		.direction = UE_DIR_IN,
413202181Sthompsa		.flags = { .short_xfer_ok = 1 },
414202181Sthompsa		.bufsize = 0,
415202181Sthompsa		.callback = &uhso_bs_intr_callback,
416202181Sthompsa	}
417202181Sthompsa};
418202181Sthompsa
419202181Sthompsastatic int uhso_probe_iface(struct uhso_softc *, int,
420202181Sthompsa    int (*probe)(struct uhso_softc *, int));
421202181Sthompsastatic int uhso_probe_iface_auto(struct uhso_softc *, int);
422202181Sthompsastatic int uhso_probe_iface_static(struct uhso_softc *, int);
423202181Sthompsa
424202181Sthompsastatic int uhso_attach_muxserial(struct uhso_softc *, struct usb_interface *,
425202181Sthompsa    int type);
426202181Sthompsastatic int uhso_attach_bulkserial(struct uhso_softc *, struct usb_interface *,
427202181Sthompsa    int type);
428202181Sthompsastatic int uhso_attach_ifnet(struct uhso_softc *, struct usb_interface *,
429202181Sthompsa    int type);
430202181Sthompsastatic void uhso_test_autoinst(void *, struct usb_device *,
431202181Sthompsa		struct usb_attach_arg *);
432202181Sthompsastatic int uhso_driver_loaded(struct module *, int, void *);
433202181Sthompsa
434202181Sthompsastatic void uhso_ucom_start_read(struct ucom_softc *);
435202181Sthompsastatic void uhso_ucom_stop_read(struct ucom_softc *);
436202181Sthompsastatic void uhso_ucom_start_write(struct ucom_softc *);
437202181Sthompsastatic void uhso_ucom_stop_write(struct ucom_softc *);
438202181Sthompsastatic void uhso_ucom_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *);
439202181Sthompsastatic void uhso_ucom_cfg_set_dtr(struct ucom_softc *, uint8_t);
440202181Sthompsastatic void uhso_ucom_cfg_set_rts(struct ucom_softc *, uint8_t);
441202181Sthompsa
442202181Sthompsastatic void uhso_if_init(void *);
443202181Sthompsastatic void uhso_if_start(struct ifnet *);
444202181Sthompsastatic void uhso_if_stop(struct uhso_softc *);
445202181Sthompsastatic int uhso_if_ioctl(struct ifnet *, u_long, caddr_t);
446202181Sthompsastatic int uhso_if_output(struct ifnet *, struct mbuf *, struct sockaddr *,
447202181Sthompsa    struct route *);
448202181Sthompsastatic void uhso_if_rxflush(void *);
449202181Sthompsa
450202181Sthompsastatic device_probe_t uhso_probe;
451202181Sthompsastatic device_attach_t uhso_attach;
452202181Sthompsastatic device_detach_t uhso_detach;
453202181Sthompsa
454202181Sthompsastatic device_method_t uhso_methods[] = {
455202181Sthompsa	DEVMETHOD(device_probe,		uhso_probe),
456202181Sthompsa	DEVMETHOD(device_attach,	uhso_attach),
457202181Sthompsa	DEVMETHOD(device_detach,	uhso_detach),
458202181Sthompsa	{ 0, 0 }
459202181Sthompsa};
460202181Sthompsa
461202181Sthompsastatic driver_t uhso_driver = {
462202181Sthompsa	"uhso",
463202181Sthompsa	uhso_methods,
464202181Sthompsa	sizeof(struct uhso_softc)
465202181Sthompsa};
466202181Sthompsa
467202181Sthompsastatic devclass_t uhso_devclass;
468202181SthompsaDRIVER_MODULE(uhso, uhub, uhso_driver, uhso_devclass, uhso_driver_loaded, 0);
469202181SthompsaMODULE_DEPEND(uhso, ucom, 1, 1, 1);
470202181SthompsaMODULE_DEPEND(uhso, usb, 1, 1, 1);
471202181SthompsaMODULE_VERSION(uhso, 1);
472202181Sthompsa
473202181Sthompsastatic struct ucom_callback uhso_ucom_callback = {
474202181Sthompsa	.ucom_cfg_get_status = &uhso_ucom_cfg_get_status,
475202181Sthompsa	.ucom_cfg_set_dtr = &uhso_ucom_cfg_set_dtr,
476202181Sthompsa	.ucom_cfg_set_rts = &uhso_ucom_cfg_set_rts,
477202181Sthompsa	.ucom_start_read = uhso_ucom_start_read,
478202181Sthompsa	.ucom_stop_read = uhso_ucom_stop_read,
479202181Sthompsa	.ucom_start_write = uhso_ucom_start_write,
480202181Sthompsa	.ucom_stop_write = uhso_ucom_stop_write
481202181Sthompsa};
482202181Sthompsa
483202181Sthompsastatic int
484202181Sthompsauhso_probe(device_t self)
485202181Sthompsa{
486202181Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(self);
487202181Sthompsa
488202181Sthompsa	if (uaa->usb_mode != USB_MODE_HOST)
489202181Sthompsa		return (ENXIO);
490202181Sthompsa	if (uaa->info.bConfigIndex != 0)
491202181Sthompsa		return (ENXIO);
492202181Sthompsa	if (uaa->device->ddesc.bDeviceClass != 0xff)
493202181Sthompsa		return (ENXIO);
494202181Sthompsa
495202181Sthompsa	return (usbd_lookup_id_by_uaa(uhso_devs, sizeof(uhso_devs), uaa));
496202181Sthompsa}
497202181Sthompsa
498202181Sthompsastatic int
499202181Sthompsauhso_attach(device_t self)
500202181Sthompsa{
501202181Sthompsa	struct uhso_softc *sc = device_get_softc(self);
502202181Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(self);
503202181Sthompsa	struct usb_config_descriptor *cd;
504202181Sthompsa	struct usb_interface_descriptor *id;
505202181Sthompsa	struct sysctl_ctx_list *sctx;
506202181Sthompsa	struct sysctl_oid *soid;
507202181Sthompsa	struct sysctl_oid *tree, *tty_node;
508202181Sthompsa	struct ucom_softc *ucom;
509202181Sthompsa	struct uhso_tty *ht;
510202181Sthompsa	int i, error, port;
511202181Sthompsa	void *probe_f;
512202181Sthompsa	usb_error_t uerr;
513202181Sthompsa	char *desc;
514202181Sthompsa
515202181Sthompsa	device_set_usb_desc(self);
516202181Sthompsa
517202181Sthompsa	UHSO_DPRINTF(0, "Device is in modem mode, devClass=%x\n",
518202181Sthompsa	    uaa->device->ddesc.bDeviceClass);
519202181Sthompsa
520202181Sthompsa	sc->sc_dev = self;
521202181Sthompsa	sc->sc_udev = uaa->device;
522202181Sthompsa	mtx_init(&sc->sc_mtx, "uhso", NULL, MTX_DEF);
523202181Sthompsa
524202181Sthompsa	sc->sc_ucom = NULL;
525202181Sthompsa	sc->sc_ttys = 0;
526202181Sthompsa
527202181Sthompsa	cd = usbd_get_config_descriptor(uaa->device);
528202181Sthompsa	id = usbd_get_interface_descriptor(uaa->iface);
529202181Sthompsa	sc->sc_ctrl_iface_no = id->bInterfaceNumber;
530202181Sthompsa
531202181Sthompsa	sc->sc_iface_no = uaa->info.bIfaceNum;
532202181Sthompsa	sc->sc_iface_index = uaa->info.bIfaceIndex;
533202181Sthompsa
534202181Sthompsa	/* Setup control pipe */
535202181Sthompsa	uerr = usbd_transfer_setup(uaa->device,
536202181Sthompsa	    &sc->sc_iface_index, sc->sc_ctrl_xfer,
537202181Sthompsa	    uhso_ctrl_config, UHSO_CTRL_MAX, sc, &sc->sc_mtx);
538202181Sthompsa	if (uerr) {
539202181Sthompsa		device_printf(self, "Failed to setup control pipe: %s\n",
540202181Sthompsa		    usbd_errstr(uerr));
541202181Sthompsa		goto out;
542202181Sthompsa	}
543202181Sthompsa
544202181Sthompsa	if (USB_GET_DRIVER_INFO(uaa) == UHSO_STATIC_IFACE)
545202181Sthompsa		probe_f = uhso_probe_iface_static;
546202181Sthompsa	else if (USB_GET_DRIVER_INFO(uaa) == UHSO_AUTO_IFACE)
547202181Sthompsa		probe_f = uhso_probe_iface_auto;
548202181Sthompsa	else
549202181Sthompsa		goto out;
550202181Sthompsa
551202181Sthompsa	error = uhso_probe_iface(sc, uaa->info.bIfaceNum, probe_f);
552202181Sthompsa	if (error != 0)
553202181Sthompsa		goto out;
554202181Sthompsa
555202181Sthompsa
556202181Sthompsa	sctx = device_get_sysctl_ctx(sc->sc_dev);
557202181Sthompsa	soid = device_get_sysctl_tree(sc->sc_dev);
558202181Sthompsa
559202181Sthompsa	SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "type",
560202181Sthompsa	    CTLFLAG_RD, uhso_port[UHSO_IFACE_PORT(sc->sc_type)], 0,
561202181Sthompsa	    "Port available at this interface");
562202181Sthompsa
563202181Sthompsa	if (sc->sc_ttys > 0) {
564202181Sthompsa		SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "ports",
565202181Sthompsa		    CTLFLAG_RD, &sc->sc_ttys, 0, "Number of attached serial ports");
566202181Sthompsa
567202181Sthompsa		tree = SYSCTL_ADD_NODE(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
568202181Sthompsa		    "port", CTLFLAG_RD, NULL, "Serial ports");
569202181Sthompsa	}
570202181Sthompsa
571202181Sthompsa	for (i = 0; i < sc->sc_ttys; i++) {
572202181Sthompsa		ht = &sc->sc_tty[i];
573202181Sthompsa		ucom = &sc->sc_ucom[i];
574202181Sthompsa
575202181Sthompsa
576202181Sthompsa		if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_MUX)
577202181Sthompsa			port = uhso_mux_port_map[ht->ht_muxport];
578202181Sthompsa		else
579202181Sthompsa			port = UHSO_IFACE_PORT_TYPE(sc->sc_type);
580202181Sthompsa
581202181Sthompsa		desc = uhso_port_type_sysctl[port];
582202181Sthompsa
583202181Sthompsa		tty_node = SYSCTL_ADD_NODE(sctx, SYSCTL_CHILDREN(tree), OID_AUTO,
584202181Sthompsa		    desc, CTLFLAG_RD, NULL, "");
585202181Sthompsa
586202181Sthompsa		ht->ht_name[0] = 0;
587202181Sthompsa		if (sc->sc_ttys == 1)
588202181Sthompsa			snprintf(ht->ht_name, 32, "cuaU%d", ucom->sc_unit);
589202181Sthompsa		else {
590202181Sthompsa			snprintf(ht->ht_name, 32, "cuaU%d.%d",
591202181Sthompsa			    ucom->sc_unit - ucom->sc_local_unit,
592202181Sthompsa			    ucom->sc_local_unit);
593202181Sthompsa		}
594202181Sthompsa
595202181Sthompsa		desc = uhso_port_type[port];
596202181Sthompsa		SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(tty_node), OID_AUTO,
597202181Sthompsa		    "tty", CTLFLAG_RD, ht->ht_name, 0, "");
598202181Sthompsa		SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(tty_node), OID_AUTO,
599202181Sthompsa		    "desc", CTLFLAG_RD, desc, 0, "");
600202181Sthompsa
601202181Sthompsa		if (bootverbose)
602202181Sthompsa			device_printf(sc->sc_dev,
603202181Sthompsa			    "\"%s\" port at %s\n", desc, ht->ht_name);
604202181Sthompsa	}
605202181Sthompsa
606202181Sthompsa	return (0);
607202181Sthompsaout:
608202181Sthompsa	uhso_detach(sc->sc_dev);
609202181Sthompsa	return (ENXIO);
610202181Sthompsa
611202181Sthompsa}
612202181Sthompsa
613202181Sthompsastatic int
614202181Sthompsauhso_detach(device_t self)
615202181Sthompsa{
616202181Sthompsa	struct uhso_softc *sc = device_get_softc(self);
617202181Sthompsa	int i;
618202181Sthompsa
619202181Sthompsa	usbd_transfer_unsetup(sc->sc_xfer, 3);
620202181Sthompsa	usbd_transfer_unsetup(sc->sc_ctrl_xfer, UHSO_CTRL_MAX);
621202181Sthompsa	if (sc->sc_ttys > 0) {
622202181Sthompsa		ucom_detach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_ttys);
623202181Sthompsa
624202181Sthompsa		for (i = 0; i < sc->sc_ttys; i++) {
625202181Sthompsa			if (sc->sc_tty[i].ht_muxport != -1) {
626202181Sthompsa				usbd_transfer_unsetup(sc->sc_tty[i].ht_xfer,
627202181Sthompsa				    UHSO_CTRL_MAX);
628202181Sthompsa			}
629202181Sthompsa		}
630202181Sthompsa
631202181Sthompsa		free(sc->sc_tty, M_USBDEV);
632202181Sthompsa		free(sc->sc_ucom, M_USBDEV);
633202181Sthompsa	}
634202181Sthompsa
635202181Sthompsa	if (sc->sc_ifp != NULL) {
636202181Sthompsa
637202181Sthompsa		callout_drain(&sc->sc_c);
638202181Sthompsa
639202181Sthompsa		mtx_lock(&sc->sc_mtx);
640202181Sthompsa		uhso_if_stop(sc);
641202181Sthompsa		bpfdetach(sc->sc_ifp);
642202181Sthompsa		if_detach(sc->sc_ifp);
643202181Sthompsa		if_free(sc->sc_ifp);
644202181Sthompsa		mtx_unlock(&sc->sc_mtx);
645202181Sthompsa
646202181Sthompsa		usbd_transfer_unsetup(sc->sc_if_xfer, UHSO_IFNET_MAX);
647202181Sthompsa	}
648202181Sthompsa
649202181Sthompsa	mtx_destroy(&sc->sc_mtx);
650202181Sthompsa
651202181Sthompsa	return (0);
652202181Sthompsa}
653202181Sthompsa
654202181Sthompsastatic void
655202181Sthompsauhso_test_autoinst(void *arg, struct usb_device *udev,
656202181Sthompsa    struct usb_attach_arg *uaa)
657202181Sthompsa{
658202181Sthompsa	struct usb_interface *iface;
659202181Sthompsa	struct usb_interface_descriptor *id;
660202181Sthompsa
661202181Sthompsa	if (uaa->dev_state != UAA_DEV_READY)
662202181Sthompsa		return;
663202181Sthompsa
664202181Sthompsa	iface = usbd_get_iface(udev, 0);
665202181Sthompsa	if (iface == NULL)
666202181Sthompsa		return;
667202181Sthompsa	id = iface->idesc;
668202181Sthompsa	if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
669202181Sthompsa		return;
670202181Sthompsa	if (usbd_lookup_id_by_uaa(uhso_devs, sizeof(uhso_devs), uaa))
671202181Sthompsa		return;		/* no device match */
672202181Sthompsa
673202181Sthompsa	if (usb_msc_eject(udev, 0, MSC_EJECT_REZERO) == 0) {
674202181Sthompsa		/* success, mark the udev as disappearing */
675202181Sthompsa		uaa->dev_state = UAA_DEV_EJECTING;
676202181Sthompsa	}
677202181Sthompsa}
678202181Sthompsa
679202181Sthompsastatic int
680202181Sthompsauhso_driver_loaded(struct module *mod, int what, void *arg)
681202181Sthompsa{
682202181Sthompsa	switch (what) {
683202181Sthompsa	case MOD_LOAD:
684202181Sthompsa		/* register our autoinstall handler */
685202181Sthompsa		uhso_etag = EVENTHANDLER_REGISTER(usb_dev_configured,
686202181Sthompsa		    uhso_test_autoinst, NULL, EVENTHANDLER_PRI_ANY);
687202181Sthompsa		break;
688202181Sthompsa	case MOD_UNLOAD:
689202181Sthompsa		EVENTHANDLER_DEREGISTER(usb_dev_configured, uhso_etag);
690202181Sthompsa		break;
691202181Sthompsa	default:
692202181Sthompsa		return (EOPNOTSUPP);
693202181Sthompsa	}
694202181Sthompsa	return (0);
695202181Sthompsa}
696202181Sthompsa
697202181Sthompsastatic int uhso_probe_iface_auto(struct uhso_softc *sc, int index)
698202181Sthompsa{
699202181Sthompsa	struct usb_device_request req;
700202181Sthompsa	usb_error_t uerr;
701202181Sthompsa	uint16_t actlen = 0;
702202181Sthompsa	char port;
703202181Sthompsa	char buf[17] = {0};
704202181Sthompsa
705202181Sthompsa	req.bmRequestType = UT_READ_VENDOR_DEVICE;
706202181Sthompsa	req.bRequest = 0x86;
707202181Sthompsa	USETW(req.wValue, 0);
708202181Sthompsa	USETW(req.wIndex, 0);
709202181Sthompsa	USETW(req.wLength, 17);
710202181Sthompsa
711202181Sthompsa	uerr = usbd_do_request_flags(sc->sc_udev, NULL, &req, buf,
712202181Sthompsa	    0, &actlen, USB_MS_HZ);
713202181Sthompsa	if (uerr != 0) {
714202181Sthompsa		device_printf(sc->sc_dev, "usbd_do_request_flags failed: %s\n",
715202181Sthompsa		    usbd_errstr(uerr));
716202181Sthompsa		return (0);
717202181Sthompsa	}
718202181Sthompsa
719202181Sthompsa	UHSO_DPRINTF(3, "actlen=%d\n", actlen);
720202181Sthompsa	UHSO_HEXDUMP(buf, 17);
721202181Sthompsa
722202181Sthompsa	if (index < 0 || index > 16) {
723202181Sthompsa		UHSO_DPRINTF(0, "Index %d out of range\n", index);
724202181Sthompsa		return (0);
725202181Sthompsa	}
726202181Sthompsa
727202181Sthompsa	UHSO_DPRINTF(3, "index=%d, type=%x\n", index, buf[index]);
728202181Sthompsa
729202181Sthompsa	if (buf[index] >= uhso_port_map_max)
730202181Sthompsa		port = 0;
731202181Sthompsa	else
732202181Sthompsa		port = uhso_port_map[(int)buf[index]];
733202181Sthompsa
734202181Sthompsa	if (port == UHSO_PORT_TYPE_NETWORK)
735202181Sthompsa		return (UHSO_IFACE_SPEC(UHSO_IF_BULK,
736202181Sthompsa		    UHSO_PORT_NETWORK, port));
737202181Sthompsa	else if (port == UHSO_PORT_TYPE_VOICE)
738202181Sthompsa		return (0);
739202181Sthompsa	else
740202181Sthompsa		return (UHSO_IFACE_SPEC(UHSO_IF_BULK,
741202181Sthompsa		    UHSO_PORT_SERIAL, port));
742202181Sthompsa
743202181Sthompsa	return (0);
744202181Sthompsa}
745202181Sthompsa
746202181Sthompsastatic int
747202181Sthompsauhso_probe_iface_static(struct uhso_softc *sc, int index)
748202181Sthompsa{
749202181Sthompsa	struct usb_config_descriptor *cd;
750202181Sthompsa
751202181Sthompsa	cd = usbd_get_config_descriptor(sc->sc_udev);
752202181Sthompsa	if (cd->bNumInterface <= 3) {
753202181Sthompsa		switch (index) {
754202181Sthompsa		case 0:
755202181Sthompsa			return UHSO_IFACE_SPEC(UHSO_IF_NET | UHSO_IF_MUX,
756202181Sthompsa			    UHSO_PORT_SERIAL | UHSO_PORT_NETWORK, 0);
757202181Sthompsa		case 1:
758202181Sthompsa			return UHSO_IFACE_SPEC(UHSO_IF_BULK,
759202181Sthompsa			    UHSO_PORT_SERIAL, UHSO_PORT_TYPE_DIAG);
760202181Sthompsa		case 2:
761202181Sthompsa			return UHSO_IFACE_SPEC(UHSO_IF_BULK,
762202181Sthompsa			    UHSO_PORT_SERIAL, UHSO_PORT_TYPE_MODEM);
763202181Sthompsa		}
764202181Sthompsa	}
765202181Sthompsa	else {
766202181Sthompsa		switch (index) {
767202181Sthompsa		case 0:
768202181Sthompsa			return UHSO_IFACE_SPEC(UHSO_IF_NET | UHSO_IF_MUX,
769202181Sthompsa			    UHSO_PORT_SERIAL | UHSO_PORT_NETWORK, 0);
770202181Sthompsa		case 1:
771202181Sthompsa			return UHSO_IFACE_SPEC(UHSO_IF_BULK,
772202181Sthompsa			    UHSO_PORT_SERIAL, UHSO_PORT_TYPE_DIAG2);
773202181Sthompsa		case 2:
774202181Sthompsa			return UHSO_IFACE_SPEC(UHSO_IF_BULK,
775202181Sthompsa			    UHSO_PORT_SERIAL, UHSO_PORT_TYPE_MODEM);
776202181Sthompsa		case 3:
777202181Sthompsa			return UHSO_IFACE_SPEC(UHSO_IF_BULK,
778202181Sthompsa			    UHSO_PORT_SERIAL, UHSO_PORT_TYPE_DIAG);
779202181Sthompsa		}
780202181Sthompsa	}
781202181Sthompsa	return (0);
782202181Sthompsa}
783202181Sthompsa
784202181Sthompsastatic int
785202181Sthompsauhso_probe_iface(struct uhso_softc *sc, int index,
786202181Sthompsa    int (*probe)(struct uhso_softc *, int))
787202181Sthompsa{
788202181Sthompsa	struct usb_interface *iface;
789202181Sthompsa	int type, error, error0;
790202181Sthompsa
791202181Sthompsa	UHSO_DPRINTF(1, "Probing for interface %d, cb=%p\n", index, probe);
792202181Sthompsa
793202181Sthompsa	type = probe(sc, index);
794202181Sthompsa	UHSO_DPRINTF(1, "Probe result %x\n", type);
795202181Sthompsa	if (type <= 0)
796202181Sthompsa		return (ENXIO);
797202181Sthompsa
798202181Sthompsa	sc->sc_type = type;
799202181Sthompsa	iface = usbd_get_iface(sc->sc_udev, index);
800202181Sthompsa
801202181Sthompsa	if (UHSO_IFACE_USB_TYPE(type) & (UHSO_IF_MUX | UHSO_IF_NET)) {
802202181Sthompsa		error0 = uhso_attach_muxserial(sc, iface, type);
803202181Sthompsa		error = uhso_attach_ifnet(sc, iface, type);
804202181Sthompsa
805202181Sthompsa		if (error0 && error)
806202181Sthompsa			return (ENXIO);
807202181Sthompsa
808202181Sthompsa		if (sc->sc_ttys > 0) {
809202181Sthompsa			error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom,
810202181Sthompsa			    sc->sc_ttys, sc, &uhso_ucom_callback, &sc->sc_mtx);
811202181Sthompsa			if (error) {
812202181Sthompsa				device_printf(sc->sc_dev, "ucom_attach failed\n");
813202181Sthompsa				return (ENXIO);
814202181Sthompsa			}
815202181Sthompsa		}
816202181Sthompsa
817202181Sthompsa		mtx_lock(&sc->sc_mtx);
818202181Sthompsa		usbd_transfer_start(sc->sc_xfer[UHSO_MUX_ENDPT_INTR]);
819202181Sthompsa		mtx_unlock(&sc->sc_mtx);
820202181Sthompsa	}
821202181Sthompsa	else if ((UHSO_IFACE_USB_TYPE(type) & UHSO_IF_BULK) &&
822202181Sthompsa	    UHSO_IFACE_PORT(type) & UHSO_PORT_SERIAL) {
823202181Sthompsa
824202181Sthompsa		error = uhso_attach_bulkserial(sc, iface, type);
825202181Sthompsa		if (error)
826202181Sthompsa			return (ENXIO);
827202181Sthompsa
828202181Sthompsa		error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom,
829202181Sthompsa		    sc->sc_ttys, sc, &uhso_ucom_callback, &sc->sc_mtx);
830202181Sthompsa		if (error) {
831202181Sthompsa			device_printf(sc->sc_dev, "ucom_attach failed\n");
832202181Sthompsa			return (ENXIO);
833202181Sthompsa		}
834202181Sthompsa	}
835202181Sthompsa	else {
836202181Sthompsa		return (ENXIO);
837202181Sthompsa	}
838202181Sthompsa
839202181Sthompsa	return (0);
840202181Sthompsa}
841202181Sthompsa
842202181Sthompsastatic int
843202181Sthompsauhso_alloc_tty(struct uhso_softc *sc)
844202181Sthompsa{
845202181Sthompsa
846202181Sthompsa	sc->sc_ttys++;
847202181Sthompsa	sc->sc_tty = reallocf(sc->sc_tty, sizeof(struct uhso_tty) * sc->sc_ttys,
848202181Sthompsa	    M_USBDEV, M_WAITOK | M_ZERO);
849202181Sthompsa	if (sc->sc_tty == NULL)
850202181Sthompsa		return (-1);
851202181Sthompsa
852202181Sthompsa	sc->sc_ucom = reallocf(sc->sc_ucom,
853202181Sthompsa	    sizeof(struct ucom_softc) * sc->sc_ttys, M_USBDEV, M_WAITOK | M_ZERO);
854202181Sthompsa	if (sc->sc_ucom == NULL)
855202181Sthompsa		return (-1);
856202181Sthompsa
857202181Sthompsa	sc->sc_tty[sc->sc_ttys - 1].ht_sc = sc;
858202181Sthompsa
859202181Sthompsa	UHSO_DPRINTF(2, "Allocated TTY %d\n", sc->sc_ttys - 1);
860202181Sthompsa	return (sc->sc_ttys - 1);
861202181Sthompsa}
862202181Sthompsa
863202181Sthompsa
864202181Sthompsastatic int
865202181Sthompsauhso_attach_muxserial(struct uhso_softc *sc, struct usb_interface *iface,
866202181Sthompsa    int type)
867202181Sthompsa{
868202181Sthompsa	struct usb_descriptor *desc;
869202181Sthompsa	int i, port, tty;
870202181Sthompsa	usb_error_t uerr;
871202181Sthompsa
872202181Sthompsa	/*
873202181Sthompsa	 * The class specific interface (type 0x24) descriptor subtype field
874202181Sthompsa	 * contains a bitmask that specifies which (and how many) ports that
875202181Sthompsa	 * are available through this multiplexed serial port.
876202181Sthompsa 	 */
877202181Sthompsa	desc = usbd_find_descriptor(sc->sc_udev, NULL,
878202181Sthompsa	    iface->idesc->bInterfaceNumber, UDESC_CS_INTERFACE, 0xff, 0, 0);
879202181Sthompsa	if (desc == NULL) {
880202181Sthompsa		UHSO_DPRINTF(0, "Failed to find UDESC_CS_INTERFACE\n");
881202181Sthompsa		return (ENXIO);
882202181Sthompsa	}
883202181Sthompsa
884202181Sthompsa	UHSO_DPRINTF(1, "Mux port mask %x\n", desc->bDescriptorSubtype);
885202181Sthompsa	if (desc->bDescriptorSubtype == 0)
886202181Sthompsa		return (ENXIO);
887202181Sthompsa
888202181Sthompsa	for (i = 0; i < 8; i++) {
889202181Sthompsa		port = (1 << i);
890202181Sthompsa		if ((port & desc->bDescriptorSubtype) == port) {
891202181Sthompsa			UHSO_DPRINTF(2, "Found mux port %x (%d)\n", port, i);
892202181Sthompsa			tty = uhso_alloc_tty(sc);
893202181Sthompsa			if (tty < 0)
894202181Sthompsa				return (ENOMEM);
895202181Sthompsa			sc->sc_tty[tty].ht_muxport = i;
896202181Sthompsa			uerr = usbd_transfer_setup(sc->sc_udev,
897202181Sthompsa			    &sc->sc_iface_index, sc->sc_tty[tty].ht_xfer,
898202181Sthompsa			    uhso_ctrl_config, UHSO_CTRL_MAX, sc, &sc->sc_mtx);
899202181Sthompsa			if (uerr) {
900202181Sthompsa				device_printf(sc->sc_dev,
901202181Sthompsa				    "Failed to setup control pipe: %s\n",
902202181Sthompsa				    usbd_errstr(uerr));
903202181Sthompsa				return (ENXIO);
904202181Sthompsa			}
905202181Sthompsa		}
906202181Sthompsa	}
907202181Sthompsa
908202181Sthompsa	uerr = usbd_transfer_setup(sc->sc_udev,
909202181Sthompsa	    &iface->idesc->bInterfaceNumber, sc->sc_xfer,
910202181Sthompsa	    uhso_mux_config, 1, sc, &sc->sc_mtx);
911202181Sthompsa	if (uerr)
912202181Sthompsa		return (ENXIO);
913202181Sthompsa
914202181Sthompsa	return (0);
915202181Sthompsa}
916202181Sthompsa
917202181Sthompsastatic void
918202181Sthompsauhso_mux_intr_callback(struct usb_xfer *xfer, usb_error_t error)
919202181Sthompsa{
920202181Sthompsa	struct usb_page_cache *pc;
921202181Sthompsa	struct usb_page_search res;
922202181Sthompsa	struct uhso_softc *sc = usbd_xfer_softc(xfer);
923202181Sthompsa	unsigned int i, mux;
924202181Sthompsa
925202181Sthompsa	UHSO_DPRINTF(3, "status %d\n", USB_GET_STATE(xfer));
926202181Sthompsa
927202181Sthompsa	switch (USB_GET_STATE(xfer)) {
928202181Sthompsa	case USB_ST_TRANSFERRED:
929202181Sthompsa		/*
930202181Sthompsa		 * The multiplexed port number can be found at the first byte.
931202181Sthompsa		 * It contains a bit mask, we transform this in to an integer.
932202181Sthompsa		 */
933202181Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
934202181Sthompsa		usbd_get_page(pc, 0, &res);
935202181Sthompsa
936202181Sthompsa		i = *((unsigned char *)res.buffer);
937202181Sthompsa		mux = 0;
938202181Sthompsa		while (i >>= 1) {
939202181Sthompsa			mux++;
940202181Sthompsa		}
941202181Sthompsa
942202181Sthompsa		UHSO_DPRINTF(3, "mux port %d (%d)\n", mux, i);
943202181Sthompsa		if (mux > UHSO_MPORT_TYPE_NOMAX)
944202181Sthompsa			break;
945202181Sthompsa
946202181Sthompsa		usbd_xfer_set_priv(
947202181Sthompsa		    sc->sc_tty[mux].ht_xfer[UHSO_CTRL_READ],
948202181Sthompsa		    &sc->sc_tty[mux]);
949202181Sthompsa		usbd_transfer_start(sc->sc_tty[mux].ht_xfer[UHSO_CTRL_READ]);
950202181Sthompsa
951202181Sthompsa		break;
952202181Sthompsa	case USB_ST_SETUP:
953202181Sthompsatr_setup:
954202181Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
955202181Sthompsa		usbd_transfer_submit(xfer);
956202181Sthompsa		break;
957202181Sthompsa	default:
958202181Sthompsa		UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error));
959202181Sthompsa		if (error == USB_ERR_CANCELLED)
960202181Sthompsa			break;
961202181Sthompsa
962202181Sthompsa		usbd_xfer_set_stall(xfer);
963202181Sthompsa		goto tr_setup;
964202181Sthompsa	}
965202181Sthompsa
966202181Sthompsa}
967202181Sthompsa
968202181Sthompsastatic void
969202181Sthompsauhso_mux_read_callback(struct usb_xfer *xfer, usb_error_t error)
970202181Sthompsa{
971202181Sthompsa	struct uhso_softc *sc = usbd_xfer_softc(xfer);
972202181Sthompsa	struct usb_page_cache *pc;
973202181Sthompsa	struct usb_device_request req;
974202181Sthompsa	struct uhso_tty *ht;
975202181Sthompsa	int actlen, len;
976202181Sthompsa
977202181Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
978202181Sthompsa
979202181Sthompsa	UHSO_DPRINTF(3, "status %d\n", USB_GET_STATE(xfer));
980202181Sthompsa
981202181Sthompsa	ht = usbd_xfer_get_priv(xfer);
982202181Sthompsa	UHSO_DPRINTF(3, "ht=%p open=%d\n", ht, ht->ht_open);
983202181Sthompsa
984202181Sthompsa	switch (USB_GET_STATE(xfer)) {
985202181Sthompsa	case USB_ST_TRANSFERRED:
986202181Sthompsa		/* Got data, send to ucom */
987202181Sthompsa		pc = usbd_xfer_get_frame(xfer, 1);
988202181Sthompsa		len = usbd_xfer_frame_len(xfer, 1);
989202181Sthompsa
990202181Sthompsa		UHSO_DPRINTF(3, "got %d bytes on mux port %d\n", len,
991202181Sthompsa		    ht->ht_muxport);
992202181Sthompsa		if (len <= 0) {
993202181Sthompsa			usbd_transfer_start(sc->sc_xfer[UHSO_MUX_ENDPT_INTR]);
994202181Sthompsa			break;
995202181Sthompsa		}
996202181Sthompsa
997202181Sthompsa		/* Deliver data if the TTY is open, discard otherwise */
998202181Sthompsa		if (ht->ht_open)
999202181Sthompsa			ucom_put_data(&sc->sc_ucom[ht->ht_muxport], pc, 0, len);
1000202181Sthompsa		/* FALLTHROUGH */
1001202181Sthompsa	case USB_ST_SETUP:
1002202181Sthompsatr_setup:
1003202181Sthompsa		bzero(&req, sizeof(struct usb_device_request));
1004202181Sthompsa		req.bmRequestType = UT_READ_CLASS_INTERFACE;
1005202181Sthompsa		req.bRequest = UCDC_GET_ENCAPSULATED_RESPONSE;
1006202181Sthompsa		USETW(req.wValue, 0);
1007202181Sthompsa		USETW(req.wIndex, ht->ht_muxport);
1008202181Sthompsa		USETW(req.wLength, 1024);
1009202181Sthompsa
1010202181Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
1011202181Sthompsa		usbd_copy_in(pc, 0, &req, sizeof(req));
1012202181Sthompsa
1013202181Sthompsa		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
1014202181Sthompsa		usbd_xfer_set_frame_len(xfer, 1, 1024);
1015202181Sthompsa		usbd_xfer_set_frames(xfer, 2);
1016202181Sthompsa		usbd_transfer_submit(xfer);
1017202181Sthompsa		break;
1018202181Sthompsa	default:
1019202181Sthompsa		UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error));
1020202181Sthompsa		if (error == USB_ERR_CANCELLED)
1021202181Sthompsa			break;
1022202181Sthompsa		usbd_xfer_set_stall(xfer);
1023202181Sthompsa		goto tr_setup;
1024202181Sthompsa	}
1025202181Sthompsa}
1026202181Sthompsa
1027202181Sthompsastatic void
1028202181Sthompsauhso_mux_write_callback(struct usb_xfer *xfer, usb_error_t error)
1029202181Sthompsa{
1030202181Sthompsa	struct uhso_softc *sc = usbd_xfer_softc(xfer);
1031202181Sthompsa	struct uhso_tty *ht;
1032202181Sthompsa	struct usb_page_cache *pc;
1033202181Sthompsa	struct usb_device_request req;
1034202181Sthompsa	int actlen;
1035202181Sthompsa	struct usb_page_search res;
1036202181Sthompsa
1037202181Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1038202181Sthompsa
1039202181Sthompsa	ht = usbd_xfer_get_priv(xfer);
1040202181Sthompsa	UHSO_DPRINTF(3, "status=%d, using mux port %d\n",
1041202181Sthompsa	    USB_GET_STATE(xfer), ht->ht_muxport);
1042202181Sthompsa
1043202181Sthompsa	switch (USB_GET_STATE(xfer)) {
1044202181Sthompsa	case USB_ST_TRANSFERRED:
1045202181Sthompsa		UHSO_DPRINTF(3, "wrote %zd data bytes to muxport %d\n",
1046202181Sthompsa		    actlen - sizeof(struct usb_device_request) ,
1047202181Sthompsa		    ht->ht_muxport);
1048202181Sthompsa		/* FALLTHROUGH */
1049202181Sthompsa	case USB_ST_SETUP:
1050202181Sthompsa		pc = usbd_xfer_get_frame(xfer, 1);
1051202181Sthompsa		if (ucom_get_data(&sc->sc_ucom[ht->ht_muxport], pc,
1052202181Sthompsa		    0, 32, &actlen)) {
1053202181Sthompsa
1054202181Sthompsa			usbd_get_page(pc, 0, &res);
1055202181Sthompsa
1056202181Sthompsa			bzero(&req, sizeof(struct usb_device_request));
1057202181Sthompsa			req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
1058202181Sthompsa			req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND;
1059202181Sthompsa			USETW(req.wValue, 0);
1060202181Sthompsa			USETW(req.wIndex, ht->ht_muxport);
1061202181Sthompsa			USETW(req.wLength, actlen);
1062202181Sthompsa
1063202181Sthompsa			pc = usbd_xfer_get_frame(xfer, 0);
1064202181Sthompsa			usbd_copy_in(pc, 0, &req, sizeof(req));
1065202181Sthompsa
1066202181Sthompsa			usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
1067202181Sthompsa			usbd_xfer_set_frame_len(xfer, 1, actlen);
1068202181Sthompsa			usbd_xfer_set_frames(xfer, 2);
1069202181Sthompsa
1070202181Sthompsa			UHSO_DPRINTF(3, "Prepared %d bytes for transmit "
1071202181Sthompsa			    "on muxport %d\n", actlen, ht->ht_muxport);
1072202181Sthompsa
1073202181Sthompsa			usbd_transfer_submit(xfer);
1074202181Sthompsa		}
1075202181Sthompsa		break;
1076202181Sthompsa	default:
1077202181Sthompsa		UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error));
1078202181Sthompsa		if (error == USB_ERR_CANCELLED)
1079202181Sthompsa			break;
1080202181Sthompsa		break;
1081202181Sthompsa	}
1082202181Sthompsa
1083202181Sthompsa}
1084202181Sthompsa
1085202181Sthompsastatic int
1086202181Sthompsauhso_attach_bulkserial(struct uhso_softc *sc, struct usb_interface *iface,
1087202181Sthompsa    int type)
1088202181Sthompsa{
1089202181Sthompsa	usb_error_t uerr;
1090202181Sthompsa	int tty;
1091202181Sthompsa
1092202181Sthompsa	/*
1093202181Sthompsa	 * Try attaching RD/WR/INTR first
1094202181Sthompsa	 */
1095202181Sthompsa	uerr = usbd_transfer_setup(sc->sc_udev,
1096202181Sthompsa	    &iface->idesc->bInterfaceNumber, sc->sc_xfer,
1097202181Sthompsa	    uhso_bs_config, UHSO_BULK_ENDPT_MAX, sc, &sc->sc_mtx);
1098202181Sthompsa	if (uerr) {
1099202181Sthompsa		/* Try only RD/WR */
1100202181Sthompsa		uerr = usbd_transfer_setup(sc->sc_udev,
1101202181Sthompsa		    &iface->idesc->bInterfaceNumber, sc->sc_xfer,
1102202181Sthompsa		    uhso_bs_config, UHSO_BULK_ENDPT_MAX - 1, sc, &sc->sc_mtx);
1103202181Sthompsa	}
1104202181Sthompsa	if (uerr) {
1105202181Sthompsa		UHSO_DPRINTF(0, "usbd_transfer_setup failed");
1106202181Sthompsa		return (-1);
1107202181Sthompsa	}
1108202181Sthompsa
1109202181Sthompsa	tty = uhso_alloc_tty(sc);
1110202181Sthompsa	if (tty < 0) {
1111202181Sthompsa		usbd_transfer_unsetup(sc->sc_xfer, UHSO_BULK_ENDPT_MAX);
1112202181Sthompsa		return (ENOMEM);
1113202181Sthompsa	}
1114202181Sthompsa
1115202181Sthompsa	sc->sc_tty[tty].ht_muxport = -1;
1116202181Sthompsa	return (0);
1117202181Sthompsa}
1118202181Sthompsa
1119202181Sthompsastatic void
1120202181Sthompsauhso_bs_read_callback(struct usb_xfer *xfer, usb_error_t error)
1121202181Sthompsa{
1122202181Sthompsa	struct uhso_softc *sc = usbd_xfer_softc(xfer);
1123202181Sthompsa	struct usb_page_cache *pc;
1124202181Sthompsa	int actlen;
1125202181Sthompsa
1126202181Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1127202181Sthompsa
1128202181Sthompsa	UHSO_DPRINTF(3, "status %d, actlen=%d\n", USB_GET_STATE(xfer), actlen);
1129202181Sthompsa
1130202181Sthompsa	switch (USB_GET_STATE(xfer)) {
1131202181Sthompsa	case USB_ST_TRANSFERRED:
1132202181Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
1133202181Sthompsa		ucom_put_data(&sc->sc_ucom[0], pc, 0, actlen);
1134202181Sthompsa		/* FALLTHROUGH */
1135202181Sthompsa	case USB_ST_SETUP:
1136202181Sthompsatr_setup:
1137202181Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
1138202181Sthompsa		usbd_transfer_submit(xfer);
1139202181Sthompsa	break;
1140202181Sthompsa	default:
1141202181Sthompsa		UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error));
1142202181Sthompsa		if (error == USB_ERR_CANCELLED)
1143202181Sthompsa			break;
1144202181Sthompsa		usbd_xfer_set_stall(xfer);
1145202181Sthompsa		goto tr_setup;
1146202181Sthompsa	}
1147202181Sthompsa}
1148202181Sthompsa
1149202181Sthompsa
1150202181Sthompsastatic void
1151202181Sthompsauhso_bs_write_callback(struct usb_xfer *xfer, usb_error_t error)
1152202181Sthompsa{
1153202181Sthompsa	struct uhso_softc *sc = usbd_xfer_softc(xfer);
1154202181Sthompsa	struct usb_page_cache *pc;
1155202181Sthompsa	int actlen;
1156202181Sthompsa
1157202181Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1158202181Sthompsa
1159202181Sthompsa	UHSO_DPRINTF(3, "status %d, actlen=%d\n", USB_GET_STATE(xfer), actlen);
1160202181Sthompsa
1161202181Sthompsa	switch (USB_GET_STATE(xfer)) {
1162202181Sthompsa	case USB_ST_TRANSFERRED:
1163202181Sthompsa	case USB_ST_SETUP:
1164202181Sthompsatr_setup:
1165202181Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
1166202181Sthompsa		if (ucom_get_data(&sc->sc_ucom[0], pc, 0, 8192, &actlen)) {
1167202181Sthompsa			usbd_xfer_set_frame_len(xfer, 0, actlen);
1168202181Sthompsa			usbd_transfer_submit(xfer);
1169202181Sthompsa		}
1170202181Sthompsa		break;
1171202181Sthompsa	break;
1172202181Sthompsa	default:
1173202181Sthompsa		UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error));
1174202181Sthompsa		if (error == USB_ERR_CANCELLED)
1175202181Sthompsa			break;
1176202181Sthompsa		usbd_xfer_set_stall(xfer);
1177202181Sthompsa		goto tr_setup;
1178202181Sthompsa	}
1179202181Sthompsa}
1180202181Sthompsa
1181202181Sthompsastatic void
1182202181Sthompsauhso_bs_cfg(struct uhso_softc *sc)
1183202181Sthompsa{
1184202181Sthompsa	struct usb_device_request req;
1185202181Sthompsa	usb_error_t uerr;
1186202181Sthompsa
1187202181Sthompsa	if (!(UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_BULK))
1188202181Sthompsa		return;
1189202181Sthompsa
1190202181Sthompsa	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
1191202181Sthompsa	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
1192202181Sthompsa	USETW(req.wValue, sc->sc_line);
1193202181Sthompsa	USETW(req.wIndex, sc->sc_iface_no);
1194202181Sthompsa	USETW(req.wLength, 0);
1195202181Sthompsa
1196202181Sthompsa	uerr = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom[0], &req, NULL, 0, 1000);
1197202181Sthompsa	if (uerr != 0) {
1198202181Sthompsa		device_printf(sc->sc_dev, "failed to set ctrl line state to "
1199202181Sthompsa		    "0x%02x: %s\n", sc->sc_line, usbd_errstr(uerr));
1200202181Sthompsa	}
1201202181Sthompsa}
1202202181Sthompsa
1203202181Sthompsastatic void
1204202181Sthompsauhso_bs_intr_callback(struct usb_xfer *xfer, usb_error_t error)
1205202181Sthompsa{
1206202181Sthompsa	struct uhso_softc *sc = usbd_xfer_softc(xfer);
1207202181Sthompsa	struct usb_page_cache *pc;
1208202181Sthompsa	int actlen;
1209202181Sthompsa	struct usb_cdc_notification cdc;
1210202181Sthompsa
1211202181Sthompsa
1212202181Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1213202181Sthompsa
1214202181Sthompsa	UHSO_DPRINTF(3, "status %d, actlen=%d\n", USB_GET_STATE(xfer), actlen);
1215202181Sthompsa
1216202181Sthompsa	switch (USB_GET_STATE(xfer)) {
1217202181Sthompsa	case USB_ST_TRANSFERRED:
1218202181Sthompsa		if (actlen < UCDC_NOTIFICATION_LENGTH) {
1219202181Sthompsa			UHSO_DPRINTF(0, "UCDC notification too short: %d\n", actlen);
1220202181Sthompsa			goto tr_setup;
1221202181Sthompsa		}
1222202181Sthompsa		else if (actlen > sizeof(struct usb_cdc_notification)) {
1223202181Sthompsa			UHSO_DPRINTF(0, "UCDC notification too large: %d\n", actlen);
1224202181Sthompsa			actlen = sizeof(struct usb_cdc_notification);
1225202181Sthompsa		}
1226202181Sthompsa
1227202181Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
1228202181Sthompsa		usbd_copy_out(pc, 0, &cdc, actlen);
1229202181Sthompsa
1230202181Sthompsa		if (UGETW(cdc.wIndex) != sc->sc_iface_no) {
1231202181Sthompsa			UHSO_DPRINTF(0, "Interface missmatch, got %d expected %d\n",
1232202181Sthompsa			    UGETW(cdc.wIndex), sc->sc_iface_no);
1233202181Sthompsa			goto tr_setup;
1234202181Sthompsa		}
1235202181Sthompsa
1236202181Sthompsa		if (cdc.bmRequestType == UCDC_NOTIFICATION &&
1237202181Sthompsa		    cdc.bNotification == UCDC_N_SERIAL_STATE) {
1238202181Sthompsa			UHSO_DPRINTF(1, "notify = 0x%02x\n", cdc.data[0]);
1239202181Sthompsa
1240202181Sthompsa			sc->sc_msr = 0;
1241202181Sthompsa			sc->sc_lsr = 0;
1242202181Sthompsa			if (cdc.data[0] & UCDC_N_SERIAL_RI)
1243202181Sthompsa				sc->sc_msr |= SER_RI;
1244202181Sthompsa			if (cdc.data[0] & UCDC_N_SERIAL_DSR)
1245202181Sthompsa				sc->sc_msr |= SER_DSR;
1246202181Sthompsa			if (cdc.data[0] & UCDC_N_SERIAL_DCD)
1247202181Sthompsa				sc->sc_msr |= SER_DCD;
1248202181Sthompsa
1249202181Sthompsa			ucom_status_change(&sc->sc_ucom[0]);
1250202181Sthompsa		}
1251202181Sthompsa	case USB_ST_SETUP:
1252202181Sthompsatr_setup:
1253202181Sthompsa	default:
1254202181Sthompsa		if (error == USB_ERR_CANCELLED)
1255202181Sthompsa			break;
1256202181Sthompsa		usbd_xfer_set_stall(xfer);
1257202181Sthompsa		goto tr_setup;
1258202181Sthompsa	}
1259202181Sthompsa}
1260202181Sthompsa
1261202181Sthompsastatic void
1262202181Sthompsauhso_ucom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
1263202181Sthompsa{
1264202181Sthompsa	struct uhso_softc *sc = ucom->sc_parent;
1265202181Sthompsa
1266202181Sthompsa	*lsr = sc->sc_lsr;
1267202181Sthompsa	*msr = sc->sc_msr;
1268202181Sthompsa}
1269202181Sthompsa
1270202181Sthompsastatic void
1271202181Sthompsauhso_ucom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
1272202181Sthompsa{
1273202181Sthompsa	struct uhso_softc *sc = ucom->sc_parent;
1274202181Sthompsa
1275202181Sthompsa	if (!(UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_BULK))
1276202181Sthompsa		return;
1277202181Sthompsa
1278202181Sthompsa	if (onoff)
1279202181Sthompsa		sc->sc_line |= UCDC_LINE_DTR;
1280202181Sthompsa	else
1281202181Sthompsa		sc->sc_line &= UCDC_LINE_DTR;
1282202181Sthompsa
1283202181Sthompsa	uhso_bs_cfg(sc);
1284202181Sthompsa}
1285202181Sthompsa
1286202181Sthompsastatic void
1287202181Sthompsauhso_ucom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
1288202181Sthompsa{
1289202181Sthompsa	struct uhso_softc *sc = ucom->sc_parent;
1290202181Sthompsa
1291202181Sthompsa	if (!(UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_BULK))
1292202181Sthompsa		return;
1293202181Sthompsa
1294202181Sthompsa	if (onoff)
1295202181Sthompsa		sc->sc_line |= UCDC_LINE_RTS;
1296202181Sthompsa	else
1297202181Sthompsa		sc->sc_line &= UCDC_LINE_DTR;
1298202181Sthompsa
1299202181Sthompsa	uhso_bs_cfg(sc);
1300202181Sthompsa}
1301202181Sthompsa
1302202181Sthompsa
1303202181Sthompsastatic void
1304202181Sthompsauhso_ucom_start_read(struct ucom_softc *ucom)
1305202181Sthompsa{
1306202181Sthompsa	struct uhso_softc *sc = ucom->sc_parent;
1307202181Sthompsa
1308202181Sthompsa	UHSO_DPRINTF(3, "unit=%d, local_unit=%d\n",
1309202181Sthompsa	    ucom->sc_unit, ucom->sc_local_unit);
1310202181Sthompsa
1311202181Sthompsa	if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_MUX) {
1312202181Sthompsa		sc->sc_tty[ucom->sc_local_unit].ht_open = 1;
1313202181Sthompsa		usbd_transfer_start(sc->sc_xfer[UHSO_MUX_ENDPT_INTR]);
1314202181Sthompsa	}
1315202181Sthompsa	else if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_BULK) {
1316202181Sthompsa		sc->sc_tty[0].ht_open = 1;
1317202181Sthompsa		usbd_transfer_start(sc->sc_xfer[UHSO_BULK_ENDPT_READ]);
1318202181Sthompsa		if (sc->sc_xfer[UHSO_BULK_ENDPT_INTR] != NULL)
1319202181Sthompsa			usbd_transfer_start(sc->sc_xfer[UHSO_BULK_ENDPT_INTR]);
1320202181Sthompsa	}
1321202181Sthompsa}
1322202181Sthompsa
1323202181Sthompsastatic void
1324202181Sthompsauhso_ucom_stop_read(struct ucom_softc *ucom)
1325202181Sthompsa{
1326202181Sthompsa
1327202181Sthompsa	struct uhso_softc *sc = ucom->sc_parent;
1328202181Sthompsa
1329202181Sthompsa	if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_MUX) {
1330202181Sthompsa		sc->sc_tty[ucom->sc_local_unit].ht_open = 0;
1331202181Sthompsa		usbd_transfer_stop(
1332202181Sthompsa		    sc->sc_tty[ucom->sc_local_unit].ht_xfer[UHSO_CTRL_READ]);
1333202181Sthompsa	}
1334202181Sthompsa	else if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_BULK) {
1335202181Sthompsa		sc->sc_tty[0].ht_open = 0;
1336202181Sthompsa		usbd_transfer_start(sc->sc_xfer[UHSO_BULK_ENDPT_READ]);
1337202181Sthompsa		if (sc->sc_xfer[UHSO_BULK_ENDPT_INTR] != NULL)
1338202181Sthompsa			usbd_transfer_stop(sc->sc_xfer[UHSO_BULK_ENDPT_INTR]);
1339202181Sthompsa	}
1340202181Sthompsa}
1341202181Sthompsa
1342202181Sthompsastatic void
1343202181Sthompsauhso_ucom_start_write(struct ucom_softc *ucom)
1344202181Sthompsa{
1345202181Sthompsa	struct uhso_softc *sc = ucom->sc_parent;
1346202181Sthompsa
1347202181Sthompsa	if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_MUX) {
1348202181Sthompsa		UHSO_DPRINTF(3, "local unit %d\n", ucom->sc_local_unit);
1349202181Sthompsa
1350202181Sthompsa		usbd_transfer_start(sc->sc_xfer[UHSO_MUX_ENDPT_INTR]);
1351202181Sthompsa
1352202181Sthompsa		usbd_xfer_set_priv(
1353202181Sthompsa		    sc->sc_tty[ucom->sc_local_unit].ht_xfer[UHSO_CTRL_WRITE],
1354202181Sthompsa		    &sc->sc_tty[ucom->sc_local_unit]);
1355202181Sthompsa		usbd_transfer_start(
1356202181Sthompsa		    sc->sc_tty[ucom->sc_local_unit].ht_xfer[UHSO_CTRL_WRITE]);
1357202181Sthompsa
1358202181Sthompsa	}
1359202181Sthompsa	else if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_BULK) {
1360202181Sthompsa		usbd_transfer_start(sc->sc_xfer[UHSO_BULK_ENDPT_WRITE]);
1361202181Sthompsa	}
1362202181Sthompsa}
1363202181Sthompsa
1364202181Sthompsastatic void
1365202181Sthompsauhso_ucom_stop_write(struct ucom_softc *ucom)
1366202181Sthompsa{
1367202181Sthompsa	struct uhso_softc *sc = ucom->sc_parent;
1368202181Sthompsa
1369202181Sthompsa	if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_MUX) {
1370202181Sthompsa		usbd_transfer_stop(
1371202181Sthompsa		    sc->sc_tty[ucom->sc_local_unit].ht_xfer[UHSO_CTRL_WRITE]);
1372202181Sthompsa	}
1373202181Sthompsa	else if (UHSO_IFACE_USB_TYPE(sc->sc_type) & UHSO_IF_BULK) {
1374202181Sthompsa		usbd_transfer_stop(sc->sc_xfer[UHSO_BULK_ENDPT_WRITE]);
1375202181Sthompsa	}
1376202181Sthompsa
1377202181Sthompsa}
1378202181Sthompsa
1379202181Sthompsastatic int uhso_attach_ifnet(struct uhso_softc *sc, struct usb_interface *iface,
1380202181Sthompsa    int type)
1381202181Sthompsa{
1382202181Sthompsa	struct ifnet *ifp;
1383202181Sthompsa	usb_error_t uerr;
1384202181Sthompsa	struct sysctl_ctx_list *sctx;
1385202181Sthompsa	struct sysctl_oid *soid;
1386202181Sthompsa
1387202181Sthompsa	uerr = usbd_transfer_setup(sc->sc_udev,
1388202181Sthompsa	    &iface->idesc->bInterfaceNumber, sc->sc_if_xfer,
1389202181Sthompsa	    uhso_ifnet_config, UHSO_IFNET_MAX, sc, &sc->sc_mtx);
1390202181Sthompsa	if (uerr) {
1391202181Sthompsa		UHSO_DPRINTF(0, "usbd_transfer_setup failed: %s\n",
1392202181Sthompsa		    usbd_errstr(uerr));
1393202181Sthompsa		return (-1);
1394202181Sthompsa	}
1395202181Sthompsa
1396202181Sthompsa	sc->sc_ifp = ifp = if_alloc(IFT_PPP);
1397202181Sthompsa	if (sc->sc_ifp == NULL) {
1398202181Sthompsa		device_printf(sc->sc_dev, "if_alloc() failed\n");
1399202181Sthompsa		return (-1);
1400202181Sthompsa	}
1401202181Sthompsa
1402202181Sthompsa	callout_init_mtx(&sc->sc_c, &sc->sc_mtx, 0);
1403202181Sthompsa	mtx_lock(&sc->sc_mtx);
1404202181Sthompsa	callout_reset(&sc->sc_c, 1, uhso_if_rxflush, sc);
1405202181Sthompsa	mtx_unlock(&sc->sc_mtx);
1406202181Sthompsa
1407202181Sthompsa	if_initname(ifp, device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev));
1408202181Sthompsa	ifp->if_mtu = UHSO_MAX_MTU;
1409202181Sthompsa
1410202181Sthompsa	ifp->if_ioctl = uhso_if_ioctl;
1411202181Sthompsa	ifp->if_init = uhso_if_init;
1412202181Sthompsa	ifp->if_start = uhso_if_start;
1413202181Sthompsa	ifp->if_output = uhso_if_output;
1414202181Sthompsa	ifp->if_flags = 0;
1415202181Sthompsa	ifp->if_softc = sc;
1416202181Sthompsa	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
1417202181Sthompsa	ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
1418202181Sthompsa	IFQ_SET_READY(&ifp->if_snd);
1419202181Sthompsa
1420202181Sthompsa	if_attach(ifp);
1421202181Sthompsa	bpfattach(ifp, DLT_RAW, 0);
1422202181Sthompsa
1423202181Sthompsa	sctx = device_get_sysctl_ctx(sc->sc_dev);
1424202181Sthompsa	soid = device_get_sysctl_tree(sc->sc_dev);
1425202181Sthompsa	/* Unlocked read... */
1426202181Sthompsa	SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "netif",
1427202181Sthompsa	    CTLFLAG_RD, ifp->if_xname, 0, "Attached network interface");
1428202181Sthompsa
1429202181Sthompsa	return (0);
1430202181Sthompsa}
1431202181Sthompsa
1432202181Sthompsastatic void
1433202181Sthompsauhso_ifnet_read_callback(struct usb_xfer *xfer, usb_error_t error)
1434202181Sthompsa{
1435202181Sthompsa	struct uhso_softc *sc = usbd_xfer_softc(xfer);
1436202181Sthompsa	struct mbuf *m;
1437202181Sthompsa	struct usb_page_cache *pc;
1438202181Sthompsa	int actlen;
1439202181Sthompsa
1440202181Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1441202181Sthompsa
1442202181Sthompsa	UHSO_DPRINTF(3, "status=%d, actlen=%d\n", USB_GET_STATE(xfer), actlen);
1443202181Sthompsa
1444202181Sthompsa	switch (USB_GET_STATE(xfer)) {
1445202181Sthompsa	case USB_ST_TRANSFERRED:
1446202181Sthompsa		if (actlen > 0 && (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING)) {
1447202181Sthompsa			pc = usbd_xfer_get_frame(xfer, 0);
1448202181Sthompsa			m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
1449202181Sthompsa			usbd_copy_out(pc, 0, mtod(m, uint8_t *), actlen);
1450202181Sthompsa			m->m_pkthdr.len = m->m_len = actlen;
1451202181Sthompsa			_IF_ENQUEUE(&sc->sc_rxq, m);
1452202181Sthompsa			if (!callout_pending(&sc->sc_c) ||
1453202181Sthompsa			    !callout_active(&sc->sc_c)) {
1454202181Sthompsa				callout_schedule(&sc->sc_c, 1);
1455202181Sthompsa			}
1456202181Sthompsa		}
1457202181Sthompsa	/* FALLTHROUGH */
1458202181Sthompsa	case USB_ST_SETUP:
1459202181Sthompsatr_setup:
1460202181Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
1461202181Sthompsa		usbd_transfer_submit(xfer);
1462202181Sthompsa		break;
1463202181Sthompsa	default:
1464202181Sthompsa		UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error));
1465202181Sthompsa		if (error == USB_ERR_CANCELLED)
1466202181Sthompsa			break;
1467202181Sthompsa		usbd_xfer_set_stall(xfer);
1468202181Sthompsa		goto tr_setup;
1469202181Sthompsa	}
1470202181Sthompsa}
1471202181Sthompsa
1472202181Sthompsa/*
1473202181Sthompsa * Defered RX processing, called with mutex locked.
1474202181Sthompsa */
1475202181Sthompsastatic void
1476202181Sthompsauhso_if_rxflush(void *arg)
1477202181Sthompsa{
1478202181Sthompsa	struct uhso_softc *sc = arg;
1479202181Sthompsa	struct ifnet *ifp = sc->sc_ifp;
1480202181Sthompsa	uint8_t *cp;
1481202181Sthompsa	struct mbuf *m, *m0, *mwait;
1482202181Sthompsa	struct ip *ip;
1483202181Sthompsa#ifdef INET6
1484202181Sthompsa	struct ip6_hdr *ip6;
1485202181Sthompsa#endif
1486202181Sthompsa	uint16_t iplen;
1487202181Sthompsa	int len, isr;
1488202181Sthompsa
1489202181Sthompsa	m = NULL;
1490202181Sthompsa	mwait = sc->sc_mwait;
1491202181Sthompsa	for (;;) {
1492202181Sthompsa		if (m == NULL) {
1493202181Sthompsa			_IF_DEQUEUE(&sc->sc_rxq, m);
1494202181Sthompsa			if (m == NULL)
1495202181Sthompsa				break;
1496202181Sthompsa			UHSO_DPRINTF(2, "dequeue m=%p, len=%d\n", m, m->m_len);
1497202181Sthompsa		}
1498202181Sthompsa		mtx_unlock(&sc->sc_mtx);
1499202181Sthompsa
1500202181Sthompsa		/* Do we have a partial packet waiting? */
1501202181Sthompsa		if (mwait != NULL) {
1502202181Sthompsa			m0 = mwait;
1503202181Sthompsa			mwait = NULL;
1504202181Sthompsa
1505202181Sthompsa			UHSO_DPRINTF(1, "partial m0=%p(%d), concat w/ m=%p(%d)\n",
1506202181Sthompsa			    m0, m0->m_len, m, m->m_len);
1507202181Sthompsa			len = m->m_len + m0->m_len;
1508202181Sthompsa
1509202181Sthompsa			/* Concat mbufs and fix headers */
1510202181Sthompsa			m_cat(m0, m);
1511202181Sthompsa			m0->m_pkthdr.len = len;
1512202181Sthompsa			m->m_flags &= ~M_PKTHDR;
1513202181Sthompsa
1514202181Sthompsa			m = m_pullup(m0, sizeof(struct ip));
1515202181Sthompsa			if (m == NULL) {
1516202181Sthompsa				ifp->if_ierrors++;
1517202181Sthompsa				UHSO_DPRINTF(0, "m_pullup failed\n");
1518202181Sthompsa				mtx_lock(&sc->sc_mtx);
1519202181Sthompsa				continue;
1520202181Sthompsa			}
1521202181Sthompsa			UHSO_DPRINTF(2, "Constructed mbuf=%p, len=%d\n",
1522202181Sthompsa			    m, m->m_pkthdr.len);
1523202181Sthompsa		}
1524202181Sthompsa
1525202181Sthompsa		cp = mtod(m, uint8_t *);
1526202181Sthompsa		ip = (struct ip *)cp;
1527202181Sthompsa#ifdef INET6
1528202181Sthompsa		ip6 = (struct ip6_hdr *)cp;
1529202181Sthompsa#endif
1530202181Sthompsa
1531202181Sthompsa		/* Check for IPv4 */
1532202181Sthompsa		if (ip->ip_v == IPVERSION) {
1533202181Sthompsa			iplen = htons(ip->ip_len);
1534202181Sthompsa			isr = NETISR_IP;
1535202181Sthompsa		}
1536202181Sthompsa#ifdef INET6
1537202181Sthompsa		/* Check for IPv6 */
1538202181Sthompsa		else if ((ip6->ip6_vfc & IPV6_VERSION_MASK) == IPV6_VERSION) {
1539202181Sthompsa			iplen = htons(ip6->ip6_plen);
1540202181Sthompsa			isr = NETISR_IPV6;
1541202181Sthompsa		}
1542202181Sthompsa#endif
1543202181Sthompsa		else {
1544202181Sthompsa			UHSO_DPRINTF(0, "got unexpected ip version %d, "
1545202181Sthompsa			    "m=%p, len=%d\n", (*cp & 0xf0) >> 4, m, m->m_len);
1546202181Sthompsa			ifp->if_ierrors++;
1547202181Sthompsa			UHSO_HEXDUMP(cp, 4);
1548202181Sthompsa			m_freem(m);
1549202181Sthompsa			m = NULL;
1550202181Sthompsa			mtx_lock(&sc->sc_mtx);
1551202181Sthompsa			continue;
1552202181Sthompsa		}
1553202181Sthompsa
1554202181Sthompsa		if (iplen == 0) {
1555202181Sthompsa			UHSO_DPRINTF(0, "Zero IP length\n");
1556202181Sthompsa			ifp->if_ierrors++;
1557202181Sthompsa			m_freem(m);
1558202181Sthompsa			m = NULL;
1559202181Sthompsa			mtx_lock(&sc->sc_mtx);
1560202181Sthompsa			continue;
1561202181Sthompsa		}
1562202181Sthompsa
1563202181Sthompsa		UHSO_DPRINTF(1, "m=%p, len=%d, cp=%p, iplen=%d\n",
1564202181Sthompsa		    m, m->m_pkthdr.len, cp, iplen);
1565202181Sthompsa
1566202181Sthompsa		m0 = NULL;
1567202181Sthompsa
1568202181Sthompsa		/* More IP packets in this mbuf */
1569202181Sthompsa		if (iplen < m->m_pkthdr.len) {
1570202181Sthompsa			m0 = m;
1571202181Sthompsa
1572202181Sthompsa			/*
1573202181Sthompsa			 * Allocate a new mbuf for this IP packet and
1574202181Sthompsa			 * copy the IP-packet into it.
1575202181Sthompsa			 */
1576202181Sthompsa			m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
1577202181Sthompsa			bcopy(mtod(m0, uint8_t *), mtod(m, uint8_t *), iplen);
1578202181Sthompsa			m->m_pkthdr.len = m->m_len = iplen;
1579202181Sthompsa
1580202181Sthompsa			/* Adjust the size of the original mbuf */
1581202181Sthompsa			m_adj(m0, iplen);
1582202181Sthompsa			m0 = m_defrag(m0, M_WAIT);
1583202181Sthompsa
1584202181Sthompsa			UHSO_DPRINTF(1, "New mbuf=%p, len=%d/%d, m0=%p, "
1585202181Sthompsa			    "m0_len=%d/%d\n", m, m->m_pkthdr.len, m->m_len,
1586202181Sthompsa			    m0, m0->m_pkthdr.len, m0->m_len);
1587202181Sthompsa		}
1588202181Sthompsa		else if (iplen > m->m_pkthdr.len) {
1589202181Sthompsa			UHSO_DPRINTF(1, "Defered mbuf=%p, len=%d\n",
1590202181Sthompsa			    m, m->m_pkthdr.len);
1591202181Sthompsa			mwait = m;
1592202181Sthompsa			m = NULL;
1593202181Sthompsa			mtx_lock(&sc->sc_mtx);
1594202181Sthompsa			continue;
1595202181Sthompsa		}
1596202181Sthompsa
1597202181Sthompsa		ifp->if_ipackets++;
1598202181Sthompsa		m->m_pkthdr.rcvif = ifp;
1599202181Sthompsa
1600202181Sthompsa		/* Dispatch to IP layer */
1601202181Sthompsa		BPF_MTAP(sc->sc_ifp, m);
1602202181Sthompsa		netisr_dispatch(isr, m);
1603202181Sthompsa		m = m0 != NULL ? m0 : NULL;
1604202181Sthompsa		mtx_lock(&sc->sc_mtx);
1605202181Sthompsa	}
1606202181Sthompsa	sc->sc_mwait = mwait;
1607202181Sthompsa}
1608202181Sthompsa
1609202181Sthompsastatic void
1610202181Sthompsauhso_ifnet_write_callback(struct usb_xfer *xfer, usb_error_t error)
1611202181Sthompsa{
1612202181Sthompsa	struct uhso_softc *sc = usbd_xfer_softc(xfer);
1613202181Sthompsa	struct ifnet *ifp = sc->sc_ifp;
1614202181Sthompsa	struct usb_page_cache *pc;
1615202181Sthompsa	struct mbuf *m;
1616202181Sthompsa	int actlen;
1617202181Sthompsa
1618202181Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1619202181Sthompsa
1620202181Sthompsa	UHSO_DPRINTF(3, "status %d, actlen=%d\n", USB_GET_STATE(xfer), actlen);
1621202181Sthompsa
1622202181Sthompsa	switch (USB_GET_STATE(xfer)) {
1623202181Sthompsa	case USB_ST_TRANSFERRED:
1624202181Sthompsa		ifp->if_opackets++;
1625202181Sthompsa		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1626202181Sthompsa	case USB_ST_SETUP:
1627202181Sthompsatr_setup:
1628202181Sthompsa		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
1629202181Sthompsa		if (m == NULL)
1630202181Sthompsa			break;
1631202181Sthompsa
1632202181Sthompsa		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1633202181Sthompsa
1634202181Sthompsa		if (m->m_pkthdr.len > MCLBYTES)
1635202181Sthompsa			m->m_pkthdr.len = MCLBYTES;
1636202181Sthompsa
1637202181Sthompsa		usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);
1638202181Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
1639202181Sthompsa		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
1640202181Sthompsa		usbd_transfer_submit(xfer);
1641202181Sthompsa
1642202181Sthompsa		BPF_MTAP(ifp, m);
1643202181Sthompsa		m_freem(m);
1644202181Sthompsa		break;
1645202181Sthompsa	default:
1646202181Sthompsa		UHSO_DPRINTF(0, "error: %s\n", usbd_errstr(error));
1647202181Sthompsa		if (error == USB_ERR_CANCELLED)
1648202181Sthompsa			break;
1649202181Sthompsa		usbd_xfer_set_stall(xfer);
1650202181Sthompsa		goto tr_setup;
1651202181Sthompsa	}
1652202181Sthompsa
1653202181Sthompsa}
1654202181Sthompsa
1655202181Sthompsastatic int
1656202181Sthompsauhso_if_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
1657202181Sthompsa{
1658202181Sthompsa	struct uhso_softc *sc;
1659202181Sthompsa
1660202181Sthompsa	sc = ifp->if_softc;
1661202181Sthompsa
1662202181Sthompsa	switch (cmd) {
1663202181Sthompsa	case SIOCSIFFLAGS:
1664202181Sthompsa		if (ifp->if_flags & IFF_UP) {
1665202181Sthompsa			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
1666202181Sthompsa				uhso_if_init(sc);
1667202181Sthompsa			}
1668202181Sthompsa		}
1669202181Sthompsa		else {
1670202181Sthompsa			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
1671202181Sthompsa				mtx_lock(&sc->sc_mtx);
1672202181Sthompsa				uhso_if_stop(sc);
1673202181Sthompsa				mtx_unlock(&sc->sc_mtx);
1674202181Sthompsa			}
1675202181Sthompsa		}
1676202181Sthompsa		break;
1677202181Sthompsa	case SIOCSIFADDR:
1678202181Sthompsa	case SIOCSIFDSTADDR:
1679202181Sthompsa	case SIOCADDMULTI:
1680202181Sthompsa	case SIOCDELMULTI:
1681202181Sthompsa		break;
1682202181Sthompsa	default:
1683202181Sthompsa		return (EINVAL);
1684202181Sthompsa	}
1685202181Sthompsa	return (0);
1686202181Sthompsa}
1687202181Sthompsa
1688202181Sthompsastatic void
1689202181Sthompsauhso_if_init(void *priv)
1690202181Sthompsa{
1691202181Sthompsa	struct uhso_softc *sc = priv;
1692202181Sthompsa	struct ifnet *ifp = sc->sc_ifp;
1693202181Sthompsa
1694202181Sthompsa	mtx_lock(&sc->sc_mtx);
1695202181Sthompsa	uhso_if_stop(sc);
1696202181Sthompsa	ifp = sc->sc_ifp;
1697202181Sthompsa	ifp->if_flags |= IFF_UP;
1698202181Sthompsa	ifp->if_drv_flags |= IFF_DRV_RUNNING;
1699202181Sthompsa	mtx_unlock(&sc->sc_mtx);
1700202181Sthompsa
1701202181Sthompsa	UHSO_DPRINTF(3, "ifnet initialized\n");
1702202181Sthompsa}
1703202181Sthompsa
1704202181Sthompsastatic int
1705202181Sthompsauhso_if_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst,
1706202181Sthompsa    struct route *ro)
1707202181Sthompsa{
1708202181Sthompsa	int error;
1709202181Sthompsa
1710202181Sthompsa	/* Only IPv4/6 support */
1711202181Sthompsa	if (dst->sa_family != AF_INET
1712202181Sthompsa#ifdef INET6
1713202181Sthompsa	   && dst->sa_family != AF_INET6
1714202181Sthompsa#endif
1715202181Sthompsa	 ) {
1716202181Sthompsa		return (EAFNOSUPPORT);
1717202181Sthompsa	}
1718202181Sthompsa
1719202181Sthompsa	error = (ifp->if_transmit)(ifp, m0);
1720202181Sthompsa	if (error) {
1721202181Sthompsa		ifp->if_oerrors++;
1722202181Sthompsa		return (ENOBUFS);
1723202181Sthompsa	}
1724202181Sthompsa	ifp->if_opackets++;
1725202181Sthompsa
1726202181Sthompsa	return (0);
1727202181Sthompsa}
1728202181Sthompsa
1729202181Sthompsastatic void
1730202181Sthompsauhso_if_start(struct ifnet *ifp)
1731202181Sthompsa{
1732202181Sthompsa	struct uhso_softc *sc = ifp->if_softc;
1733202181Sthompsa
1734202181Sthompsa	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
1735202181Sthompsa		UHSO_DPRINTF(1, "Not running\n");
1736202181Sthompsa		return;
1737202181Sthompsa	}
1738202181Sthompsa
1739202181Sthompsa	mtx_lock(&sc->sc_mtx);
1740202181Sthompsa	usbd_transfer_start(sc->sc_if_xfer[UHSO_IFNET_READ]);
1741202181Sthompsa	usbd_transfer_start(sc->sc_if_xfer[UHSO_IFNET_WRITE]);
1742202181Sthompsa	mtx_unlock(&sc->sc_mtx);
1743202181Sthompsa	UHSO_DPRINTF(3, "interface started\n");
1744202181Sthompsa}
1745202181Sthompsa
1746202181Sthompsastatic void
1747202181Sthompsauhso_if_stop(struct uhso_softc *sc)
1748202181Sthompsa{
1749202181Sthompsa
1750202181Sthompsa	usbd_transfer_stop(sc->sc_if_xfer[UHSO_IFNET_READ]);
1751202181Sthompsa	usbd_transfer_stop(sc->sc_if_xfer[UHSO_IFNET_WRITE]);
1752202181Sthompsa
1753202181Sthompsa	sc->sc_ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
1754202181Sthompsa}
1755