xref: /freebsd/sys/dev/usb/serial/u3g.c (revision c0020399a650364d0134f79f3fa319f84064372d)
1 /*
2  * Copyright (c) 2008 AnyWi Technologies
3  * Author: Andrea Guzzo <aguzzo@anywi.com>
4  * * based on uark.c 1.1 2006/08/14 08:30:22 jsg *
5  * * parts from ubsa.c 183348 2008-09-25 12:00:56Z phk *
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  *
19  * $FreeBSD$
20  */
21 
22 /*
23  * NOTE:
24  *
25  * - The detour through the tty layer is ridiculously expensive wrt
26  *   buffering due to the high speeds.
27  *
28  *   We should consider adding a simple r/w device which allows
29  *   attaching of PPP in a more efficient way.
30  *
31  */
32 
33 #include "usbdevs.h"
34 #include <dev/usb/usb.h>
35 #include <dev/usb/usb_mfunc.h>
36 #include <dev/usb/usb_error.h>
37 
38 #define	USB_DEBUG_VAR u3g_debug
39 
40 #include <dev/usb/usb_core.h>
41 #include <dev/usb/usb_debug.h>
42 #include <dev/usb/usb_process.h>
43 #include <dev/usb/usb_request.h>
44 #include <dev/usb/usb_lookup.h>
45 #include <dev/usb/usb_util.h>
46 #include <dev/usb/usb_busdma.h>
47 #include <dev/usb/usb_msctest.h>
48 #include <dev/usb/usb_dynamic.h>
49 #include <dev/usb/usb_device.h>
50 
51 #include <dev/usb/serial/usb_serial.h>
52 
53 #if USB_DEBUG
54 static int u3g_debug = 0;
55 
56 SYSCTL_NODE(_hw_usb2, OID_AUTO, u3g, CTLFLAG_RW, 0, "USB 3g");
57 SYSCTL_INT(_hw_usb2_u3g, OID_AUTO, debug, CTLFLAG_RW,
58     &u3g_debug, 0, "Debug level");
59 #endif
60 
61 #define	U3G_MAXPORTS		4
62 #define	U3G_CONFIG_INDEX	0
63 #define	U3G_BSIZE		2048
64 
65 #define	U3GSP_GPRS		0
66 #define	U3GSP_EDGE		1
67 #define	U3GSP_CDMA		2
68 #define	U3GSP_UMTS		3
69 #define	U3GSP_HSDPA		4
70 #define	U3GSP_HSUPA		5
71 #define	U3GSP_HSPA		6
72 #define	U3GSP_MAX		7
73 
74 #define	U3GFL_NONE		0x0000	/* No flags */
75 #define	U3GFL_HUAWEI_INIT	0x0001	/* Init command required */
76 #define	U3GFL_SCSI_EJECT	0x0002	/* SCSI eject command required */
77 #define	U3GFL_SIERRA_INIT	0x0004	/* Init command required */
78 #define	U3GFL_SAEL_M460_INIT	0x0008	/* Init device */
79 
80 enum {
81 	U3G_BULK_WR,
82 	U3G_BULK_RD,
83 	U3G_N_TRANSFER,
84 };
85 
86 struct u3g_softc {
87 	struct usb2_com_super_softc sc_super_ucom;
88 	struct usb2_com_softc sc_ucom[U3G_MAXPORTS];
89 
90 	struct usb2_xfer *sc_xfer[U3G_MAXPORTS][U3G_N_TRANSFER];
91 	struct usb2_device *sc_udev;
92 	struct mtx sc_mtx;
93 
94 	uint8_t	sc_lsr;			/* local status register */
95 	uint8_t	sc_msr;			/* U3G status register */
96 	uint8_t	sc_numports;
97 };
98 
99 static device_probe_t u3g_probe;
100 static device_attach_t u3g_attach;
101 static device_detach_t u3g_detach;
102 
103 static usb2_callback_t u3g_write_callback;
104 static usb2_callback_t u3g_read_callback;
105 
106 static void u3g_start_read(struct usb2_com_softc *ucom);
107 static void u3g_stop_read(struct usb2_com_softc *ucom);
108 static void u3g_start_write(struct usb2_com_softc *ucom);
109 static void u3g_stop_write(struct usb2_com_softc *ucom);
110 
111 static int u3g_driver_loaded(struct module *mod, int what, void *arg);
112 
113 static const struct usb2_config u3g_config[U3G_N_TRANSFER] = {
114 
115 	[U3G_BULK_WR] = {
116 		.type = UE_BULK,
117 		.endpoint = UE_ADDR_ANY,
118 		.direction = UE_DIR_OUT,
119 		.bufsize = U3G_BSIZE,/* bytes */
120 		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
121 		.callback = &u3g_write_callback,
122 	},
123 
124 	[U3G_BULK_RD] = {
125 		.type = UE_BULK,
126 		.endpoint = UE_ADDR_ANY,
127 		.direction = UE_DIR_IN,
128 		.bufsize = U3G_BSIZE,/* bytes */
129 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
130 		.callback = &u3g_read_callback,
131 	},
132 };
133 
134 static const struct usb2_com_callback u3g_callback = {
135 	.usb2_com_start_read = &u3g_start_read,
136 	.usb2_com_stop_read = &u3g_stop_read,
137 	.usb2_com_start_write = &u3g_start_write,
138 	.usb2_com_stop_write = &u3g_stop_write,
139 };
140 
141 static device_method_t u3g_methods[] = {
142 	DEVMETHOD(device_probe, u3g_probe),
143 	DEVMETHOD(device_attach, u3g_attach),
144 	DEVMETHOD(device_detach, u3g_detach),
145 	{0, 0}
146 };
147 
148 static devclass_t u3g_devclass;
149 
150 static driver_t u3g_driver = {
151 	.name = "u3g",
152 	.methods = u3g_methods,
153 	.size = sizeof(struct u3g_softc),
154 };
155 
156 DRIVER_MODULE(u3g, uhub, u3g_driver, u3g_devclass, u3g_driver_loaded, 0);
157 MODULE_DEPEND(u3g, ucom, 1, 1, 1);
158 MODULE_DEPEND(u3g, usb, 1, 1, 1);
159 
160 static const struct usb2_device_id u3g_devs[] = {
161 	/* OEM: Option */
162 	{USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G, U3GFL_NONE)},
163 	{USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD, U3GFL_NONE)},
164 	{USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS, U3GFL_NONE)},
165 	{USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36, U3GFL_NONE)},
166 	{USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAXHSUPA, U3GFL_NONE)},
167 	{USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G, U3GFL_NONE)},
168 	/* OEM: Qualcomm, Inc. */
169 	{USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_ZTE_STOR, U3GFL_SCSI_EJECT)},
170 	{USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM, U3GFL_SCSI_EJECT)},
171 	/* OEM: Huawei */
172 	{USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, U3GFL_HUAWEI_INIT)},
173 	{USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220, U3GFL_HUAWEI_INIT)},
174 	/* OEM: Novatel */
175 	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM, U3GFL_SCSI_EJECT)},
176 	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620, U3GFL_SCSI_EJECT)},
177 	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MC950D, U3GFL_SCSI_EJECT)},
178 	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720, U3GFL_SCSI_EJECT)},
179 	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727, U3GFL_SCSI_EJECT)},
180 	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740, U3GFL_SCSI_EJECT)},
181 	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2, U3GFL_SCSI_EJECT)},
182 	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870, U3GFL_SCSI_EJECT)},
183 	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V620, U3GFL_SCSI_EJECT)},
184 	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V640, U3GFL_SCSI_EJECT)},
185 	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720, U3GFL_SCSI_EJECT)},
186 	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740, U3GFL_SCSI_EJECT)},
187 	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D, U3GFL_SCSI_EJECT)},
188 	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870, U3GFL_SCSI_EJECT)},
189 	{USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ZEROCD, U3GFL_SCSI_EJECT)},
190 	{USB_VPI(USB_VENDOR_DELL, USB_PRODUCT_DELL_U740, U3GFL_SCSI_EJECT)},
191 	/* OEM: Merlin */
192 	{USB_VPI(USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, U3GFL_NONE)},
193 	/* OEM: Sierra Wireless: */
194 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580, U3GFL_NONE)},
195 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595, U3GFL_NONE)},
196 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U, U3GFL_NONE)},
197 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E, U3GFL_NONE)},
198 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597, U3GFL_NONE)},
199 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880, U3GFL_NONE)},
200 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E, U3GFL_NONE)},
201 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U, U3GFL_NONE)},
202 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881, U3GFL_NONE)},
203 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E, U3GFL_NONE)},
204 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U, U3GFL_NONE)},
205 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625, U3GFL_NONE)},
206 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, U3GFL_NONE)},
207 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2, U3GFL_NONE)},
208 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725, U3GFL_NONE)},
209 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725, U3GFL_NONE)},
210 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875, U3GFL_NONE)},
211 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755, U3GFL_NONE)},
212 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2, U3GFL_NONE)},
213 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3, U3GFL_NONE)},
214 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765, U3GFL_NONE)},
215 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U, U3GFL_NONE)},
216 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2, U3GFL_NONE)},
217 	{USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HS2300, U3GFL_NONE)},
218 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780, U3GFL_NONE)},
219 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781, U3GFL_NONE)},
220 	{USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HS2300, U3GFL_NONE)},
221 	/* Sierra TruInstaller device ID */
222 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_TRUINSTALL, U3GFL_SIERRA_INIT)},
223 	/* PRUEBA SILABS */
224 	{USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_SAEL, U3GFL_SAEL_M460_INIT)},
225 };
226 
227 static void
228 u3g_sierra_init(struct usb2_device *udev)
229 {
230 	struct usb2_device_request req;
231 
232 	DPRINTFN(0, "\n");
233 
234 	req.bmRequestType = UT_VENDOR;
235 	req.bRequest = UR_SET_INTERFACE;
236 	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
237 	USETW(req.wIndex, UHF_PORT_CONNECTION);
238 	USETW(req.wLength, 0);
239 
240 	if (usb2_do_request_flags(udev, NULL, &req,
241 	    NULL, 0, NULL, USB_MS_HZ)) {
242 		/* ignore any errors */
243 	}
244 	return;
245 }
246 
247 static void
248 u3g_huawei_init(struct usb2_device *udev)
249 {
250 	struct usb2_device_request req;
251 
252 	DPRINTFN(0, "\n");
253 
254 	req.bmRequestType = UT_WRITE_DEVICE;
255 	req.bRequest = UR_SET_FEATURE;
256 	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
257 	USETW(req.wIndex, UHF_PORT_SUSPEND);
258 	USETW(req.wLength, 0);
259 
260 	if (usb2_do_request_flags(udev, NULL, &req,
261 	    NULL, 0, NULL, USB_MS_HZ)) {
262 		/* ignore any errors */
263 	}
264 	return;
265 }
266 
267 static void
268 u3g_sael_m460_init(struct usb2_device *udev)
269 {
270 	static const uint8_t setup[][24] = {
271 	     { 0x41, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
272 	     { 0x41, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 },
273 	     { 0x41, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
274 	       0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
275 	       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
276 	     { 0xc1, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02 },
277 	     { 0xc1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 },
278 	     { 0x41, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 },
279 	     { 0xc1, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 },
280 	     { 0x41, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
281 	     { 0x41, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 },
282 	     { 0x41, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00 },
283 	     { 0x41, 0x19, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,
284 	       0x00, 0x00, 0x00, 0x00, 0x11, 0x13 },
285 	     { 0x41, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
286 	       0x09, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
287 	       0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00 },
288 	     { 0x41, 0x12, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 },
289 	     { 0x41, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
290 	     { 0x41, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 },
291 	     { 0x41, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00 },
292 	     { 0x41, 0x19, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,
293 	       0x00, 0x00, 0x00, 0x00, 0x11, 0x13 },
294 	     { 0x41, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
295 	       0x09, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
296 	       0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00 },
297 	     { 0x41, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 },
298 	};
299 
300 	struct usb2_device_request req;
301 	usb2_error_t err;
302 	uint16_t len;
303 	uint8_t buf[0x300];
304 	uint8_t n;
305 
306 	DPRINTFN(1, "\n");
307 
308 	if (usb2_req_set_alt_interface_no(udev, NULL, 0, 0)) {
309 		DPRINTFN(0, "Alt setting 0 failed\n");
310 		return;
311 	}
312 
313 	for (n = 0; n != (sizeof(setup)/sizeof(setup[0])); n++) {
314 
315 		memcpy(&req, setup[n], sizeof(req));
316 
317 		len = UGETW(req.wLength);
318 		if (req.bmRequestType & UE_DIR_IN) {
319 			if (len > sizeof(buf)) {
320 				DPRINTFN(0, "too small buffer\n");
321 				continue;
322 			}
323 			err = usb2_do_request(udev, NULL, &req, buf);
324 		} else {
325 			if (len > (sizeof(setup[0]) - 8)) {
326 				DPRINTFN(0, "too small buffer\n");
327 				continue;
328 			}
329 			err = usb2_do_request(udev, NULL, &req,
330 			    __DECONST(uint8_t *, &setup[n][8]));
331 		}
332 		if (err) {
333 			DPRINTFN(1, "request %u failed\n",
334 			    (unsigned int)n);
335 			/*
336 			 * Some of the requests will fail. Stop doing
337 			 * requests when we are getting timeouts so
338 			 * that we don't block the explore/attach
339 			 * thread forever.
340 			 */
341 			if (err == USB_ERR_TIMEOUT)
342 				break;
343 		}
344 	}
345 }
346 
347 static int
348 u3g_lookup_huawei(struct usb2_attach_arg *uaa)
349 {
350 	/* Calling the lookup function will also set the driver info! */
351 	return (usb2_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa));
352 }
353 
354 /*
355  * The following function handles 3G modem devices (E220, Mobile,
356  * etc.) with auto-install flash disks for Windows/MacOSX on the first
357  * interface.  After some command or some delay they change appearance
358  * to a modem.
359  */
360 static usb2_error_t
361 u3g_test_huawei_autoinst(struct usb2_device *udev,
362     struct usb2_attach_arg *uaa)
363 {
364 	struct usb2_interface *iface;
365 	struct usb2_interface_descriptor *id;
366 	uint32_t flags;
367 
368 	if (udev == NULL) {
369 		return (USB_ERR_INVAL);
370 	}
371 	iface = usb2_get_iface(udev, 0);
372 	if (iface == NULL) {
373 		return (USB_ERR_INVAL);
374 	}
375 	id = iface->idesc;
376 	if (id == NULL) {
377 		return (USB_ERR_INVAL);
378 	}
379 	if (id->bInterfaceClass != UICLASS_MASS) {
380 		return (USB_ERR_INVAL);
381 	}
382 	if (u3g_lookup_huawei(uaa)) {
383 		/* no device match */
384 		return (USB_ERR_INVAL);
385 	}
386 	flags = USB_GET_DRIVER_INFO(uaa);
387 
388 	if (flags & U3GFL_HUAWEI_INIT) {
389 		u3g_huawei_init(udev);
390 	} else if (flags & U3GFL_SCSI_EJECT) {
391 		return (usb2_test_autoinstall(udev, 0, 1));
392 	} else if (flags & U3GFL_SIERRA_INIT) {
393 		u3g_sierra_init(udev);
394 	} else {
395 		/* no quirks */
396 		return (USB_ERR_INVAL);
397 	}
398 	return (0);			/* success */
399 }
400 
401 static int
402 u3g_driver_loaded(struct module *mod, int what, void *arg)
403 {
404 	switch (what) {
405 	case MOD_LOAD:
406 		/* register our autoinstall handler */
407 		usb2_test_huawei_autoinst_p = &u3g_test_huawei_autoinst;
408 		break;
409 	case MOD_UNLOAD:
410 		usb2_test_huawei_unload(NULL);
411 		break;
412 	default:
413 		return (EOPNOTSUPP);
414 	}
415  	return (0);
416 }
417 
418 static int
419 u3g_probe(device_t self)
420 {
421 	struct usb2_attach_arg *uaa = device_get_ivars(self);
422 
423 	if (uaa->usb2_mode != USB_MODE_HOST) {
424 		return (ENXIO);
425 	}
426 	if (uaa->info.bConfigIndex != U3G_CONFIG_INDEX) {
427 		return (ENXIO);
428 	}
429 	if (uaa->info.bInterfaceClass != UICLASS_VENDOR) {
430 		return (ENXIO);
431 	}
432 	return (u3g_lookup_huawei(uaa));
433 }
434 
435 static int
436 u3g_attach(device_t dev)
437 {
438 	struct usb2_config u3g_config_tmp[U3G_N_TRANSFER];
439 	struct usb2_attach_arg *uaa = device_get_ivars(dev);
440 	struct u3g_softc *sc = device_get_softc(dev);
441 	struct usb2_interface *iface;
442 	struct usb2_interface_descriptor *id;
443 	uint32_t flags;
444 	uint8_t m;
445 	uint8_t n;
446 	uint8_t i;
447 	uint8_t x;
448 	int error;
449 
450 	DPRINTF("sc=%p\n", sc);
451 
452 	flags = USB_GET_DRIVER_INFO(uaa);
453 
454 	if (flags & U3GFL_SAEL_M460_INIT)
455 		u3g_sael_m460_init(uaa->device);
456 
457 	/* copy in USB config */
458 	for (n = 0; n != U3G_N_TRANSFER; n++)
459 		u3g_config_tmp[n] = u3g_config[n];
460 
461 	device_set_usb2_desc(dev);
462 	mtx_init(&sc->sc_mtx, "u3g", NULL, MTX_DEF);
463 
464 	sc->sc_udev = uaa->device;
465 
466 	x = 0;		/* interface index */
467 	i = 0;		/* endpoint index */
468 	m = 0;		/* number of ports */
469 
470 	while (m != U3G_MAXPORTS) {
471 
472 		/* update BULK endpoint index */
473 		for (n = 0; n != U3G_N_TRANSFER; n++)
474 			u3g_config_tmp[n].ep_index = i;
475 
476 		iface = usb2_get_iface(uaa->device, x);
477 		if (iface == NULL) {
478 			if (m != 0)
479 				break;	/* end of interfaces */
480 			DPRINTF("did not find any modem endpoints\n");
481 			goto detach;
482 		}
483 
484 		id = usb2_get_interface_descriptor(iface);
485 		if ((id == NULL) ||
486 		    (id->bInterfaceClass != UICLASS_VENDOR)) {
487 			/* next interface */
488 			x++;
489 			i = 0;
490 			continue;
491 		}
492 
493 		/* try to allocate a set of BULK endpoints */
494 		error = usb2_transfer_setup(uaa->device, &x,
495 		    sc->sc_xfer[m], u3g_config_tmp, U3G_N_TRANSFER,
496 		    &sc->sc_ucom[m], &sc->sc_mtx);
497 		if (error) {
498 			/* next interface */
499 			x++;
500 			i = 0;
501 			continue;
502 		}
503 
504 		/* grab other interface, if any */
505                 if (x != uaa->info.bIfaceIndex)
506                         usb2_set_parent_iface(uaa->device, x,
507                             uaa->info.bIfaceIndex);
508 
509 		/* set stall by default */
510 		mtx_lock(&sc->sc_mtx);
511 		usb2_transfer_set_stall(sc->sc_xfer[m][U3G_BULK_WR]);
512 		usb2_transfer_set_stall(sc->sc_xfer[m][U3G_BULK_RD]);
513 		mtx_unlock(&sc->sc_mtx);
514 
515 		m++;	/* found one port */
516 		i++;	/* next endpoint index */
517 	}
518 
519 	sc->sc_numports = m;
520 
521 	error = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom,
522 	    sc->sc_numports, sc, &u3g_callback, &sc->sc_mtx);
523 	if (error) {
524 		DPRINTF("usb2_com_attach failed\n");
525 		goto detach;
526 	}
527 	if (sc->sc_numports != 1) {
528 		/* be verbose */
529 		device_printf(dev, "Found %u ports.\n",
530 		    (unsigned int)sc->sc_numports);
531 	}
532 	return (0);
533 
534 detach:
535 	u3g_detach(dev);
536 	return (ENXIO);
537 }
538 
539 static int
540 u3g_detach(device_t dev)
541 {
542 	struct u3g_softc *sc = device_get_softc(dev);
543 	uint8_t m;
544 
545 	DPRINTF("sc=%p\n", sc);
546 
547 	/* NOTE: It is not dangerous to detach more ports than attached! */
548 	usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, U3G_MAXPORTS);
549 
550 	for (m = 0; m != U3G_MAXPORTS; m++)
551 		usb2_transfer_unsetup(sc->sc_xfer[m], U3G_N_TRANSFER);
552 	mtx_destroy(&sc->sc_mtx);
553 
554 	return (0);
555 }
556 
557 static void
558 u3g_start_read(struct usb2_com_softc *ucom)
559 {
560 	struct u3g_softc *sc = ucom->sc_parent;
561 
562 	/* start read endpoint */
563 	usb2_transfer_start(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_RD]);
564 	return;
565 }
566 
567 static void
568 u3g_stop_read(struct usb2_com_softc *ucom)
569 {
570 	struct u3g_softc *sc = ucom->sc_parent;
571 
572 	/* stop read endpoint */
573 	usb2_transfer_stop(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_RD]);
574 	return;
575 }
576 
577 static void
578 u3g_start_write(struct usb2_com_softc *ucom)
579 {
580 	struct u3g_softc *sc = ucom->sc_parent;
581 
582 	usb2_transfer_start(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_WR]);
583 	return;
584 }
585 
586 static void
587 u3g_stop_write(struct usb2_com_softc *ucom)
588 {
589 	struct u3g_softc *sc = ucom->sc_parent;
590 
591 	usb2_transfer_stop(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_WR]);
592 	return;
593 }
594 
595 static void
596 u3g_write_callback(struct usb2_xfer *xfer)
597 {
598 	struct usb2_com_softc *ucom = xfer->priv_sc;
599 	uint32_t actlen;
600 
601 	switch (USB_GET_STATE(xfer)) {
602 	case USB_ST_TRANSFERRED:
603 	case USB_ST_SETUP:
604 tr_setup:
605 		if (usb2_com_get_data(ucom, xfer->frbuffers, 0,
606 		    U3G_BSIZE, &actlen)) {
607 			xfer->frlengths[0] = actlen;
608 			usb2_start_hardware(xfer);
609 		}
610 		break;
611 
612 	default:			/* Error */
613 		if (xfer->error != USB_ERR_CANCELLED) {
614 			/* do a builtin clear-stall */
615 			xfer->flags.stall_pipe = 1;
616 			goto tr_setup;
617 		}
618 		break;
619 	}
620 	return;
621 }
622 
623 static void
624 u3g_read_callback(struct usb2_xfer *xfer)
625 {
626 	struct usb2_com_softc *ucom = xfer->priv_sc;
627 
628 	switch (USB_GET_STATE(xfer)) {
629 	case USB_ST_TRANSFERRED:
630 		usb2_com_put_data(ucom, xfer->frbuffers, 0, xfer->actlen);
631 
632 	case USB_ST_SETUP:
633 tr_setup:
634 		xfer->frlengths[0] = xfer->max_data_length;
635 		usb2_start_hardware(xfer);
636 		break;
637 
638 	default:			/* Error */
639 		if (xfer->error != USB_ERR_CANCELLED) {
640 			/* do a builtin clear-stall */
641 			xfer->flags.stall_pipe = 1;
642 			goto tr_setup;
643 		}
644 		break;
645 	}
646 	return;
647 }
648