xref: /freebsd/sys/dev/usb/serial/u3g.c (revision 39beb93c3f8bdbf72a61fda42300b5ebed7390c8)
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 #include <dev/usb/usb_defs.h>
38 
39 #define	USB_DEBUG_VAR u3g_debug
40 
41 #include <dev/usb/usb_core.h>
42 #include <dev/usb/usb_debug.h>
43 #include <dev/usb/usb_process.h>
44 #include <dev/usb/usb_request.h>
45 #include <dev/usb/usb_lookup.h>
46 #include <dev/usb/usb_util.h>
47 #include <dev/usb/usb_busdma.h>
48 #include <dev/usb/usb_msctest.h>
49 #include <dev/usb/usb_dynamic.h>
50 #include <dev/usb/usb_device.h>
51 
52 #include <dev/usb/serial/usb_serial.h>
53 
54 #if USB_DEBUG
55 static int u3g_debug = 0;
56 
57 SYSCTL_NODE(_hw_usb2, OID_AUTO, u3g, CTLFLAG_RW, 0, "USB 3g");
58 SYSCTL_INT(_hw_usb2_u3g, OID_AUTO, debug, CTLFLAG_RW,
59     &u3g_debug, 0, "Debug level");
60 #endif
61 
62 #define	U3G_MAXPORTS		4
63 #define	U3G_CONFIG_INDEX	0
64 #define	U3G_BSIZE		2048
65 
66 #define	U3GSP_GPRS		0
67 #define	U3GSP_EDGE		1
68 #define	U3GSP_CDMA		2
69 #define	U3GSP_UMTS		3
70 #define	U3GSP_HSDPA		4
71 #define	U3GSP_HSUPA		5
72 #define	U3GSP_HSPA		6
73 #define	U3GSP_MAX		7
74 
75 #define	U3GFL_NONE		0x0000	/* No flags */
76 #define	U3GFL_HUAWEI_INIT	0x0001	/* Init command required */
77 #define	U3GFL_SCSI_EJECT	0x0002	/* SCSI eject command required */
78 #define	U3GFL_SIERRA_INIT	0x0004	/* Init command required */
79 #define	U3GFL_SAEL_M460_INIT	0x0008	/* Init device */
80 
81 enum {
82 	U3G_BULK_WR,
83 	U3G_BULK_RD,
84 	U3G_N_TRANSFER,
85 };
86 
87 struct u3g_softc {
88 	struct usb2_com_super_softc sc_super_ucom;
89 	struct usb2_com_softc sc_ucom[U3G_MAXPORTS];
90 
91 	struct usb2_xfer *sc_xfer[U3G_MAXPORTS][U3G_N_TRANSFER];
92 	struct usb2_device *sc_udev;
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 		.mh.bufsize = U3G_BSIZE,/* bytes */
120 		.mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
121 		.mh.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 		.mh.bufsize = U3G_BSIZE,/* bytes */
129 		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
130 		.mh.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, ushub, 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 	uint16_t len;
302 	uint8_t buf[0x300];
303 	uint8_t n;
304 
305 	DPRINTFN(1, "\n");
306 
307 	if (usb2_req_set_alt_interface_no(udev, NULL, 0, 0)) {
308 		DPRINTFN(0, "Alt setting 0 failed\n");
309 		return;
310 	}
311 
312 	for (n = 0; n != (sizeof(setup)/sizeof(setup[0])); n++) {
313 
314 		memcpy(&req, setup[n], sizeof(req));
315 
316 		len = UGETW(req.wLength);
317 		if (req.bmRequestType & UE_DIR_IN) {
318 			if (len > sizeof(buf)) {
319 				DPRINTFN(0, "too small buffer\n");
320 				continue;
321 			}
322 			if (usb2_do_request(udev, NULL, &req, buf)) {
323 				DPRINTFN(0, "request %u failed\n",
324 				    (unsigned int)n);
325 				break;
326 			}
327 		} else {
328 			if (len > (sizeof(setup[0]) - 8)) {
329 				DPRINTFN(0, "too small buffer\n");
330 				continue;
331 			}
332 			if (usb2_do_request(udev, NULL, &req,
333 			    __DECONST(uint8_t *, &setup[n][8]))) {
334 				DPRINTFN(0, "request %u failed\n",
335 				    (unsigned int)n);
336 				break;
337 			}
338 		}
339 	}
340 	return;
341 }
342 
343 static int
344 u3g_lookup_huawei(struct usb2_attach_arg *uaa)
345 {
346 	/* Calling the lookup function will also set the driver info! */
347 	return (usb2_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa));
348 }
349 
350 /*
351  * The following function handles 3G modem devices (E220, Mobile,
352  * etc.) with auto-install flash disks for Windows/MacOSX on the first
353  * interface.  After some command or some delay they change appearance
354  * to a modem.
355  */
356 static usb2_error_t
357 u3g_test_huawei_autoinst(struct usb2_device *udev,
358     struct usb2_attach_arg *uaa)
359 {
360 	struct usb2_interface *iface;
361 	struct usb2_interface_descriptor *id;
362 	uint32_t flags;
363 
364 	if (udev == NULL) {
365 		return (USB_ERR_INVAL);
366 	}
367 	iface = usb2_get_iface(udev, 0);
368 	if (iface == NULL) {
369 		return (USB_ERR_INVAL);
370 	}
371 	id = iface->idesc;
372 	if (id == NULL) {
373 		return (USB_ERR_INVAL);
374 	}
375 	if (id->bInterfaceClass != UICLASS_MASS) {
376 		return (USB_ERR_INVAL);
377 	}
378 	if (u3g_lookup_huawei(uaa)) {
379 		/* no device match */
380 		return (USB_ERR_INVAL);
381 	}
382 	flags = USB_GET_DRIVER_INFO(uaa);
383 
384 	if (flags & U3GFL_HUAWEI_INIT) {
385 		u3g_huawei_init(udev);
386 	} else if (flags & U3GFL_SCSI_EJECT) {
387 		return (usb2_test_autoinstall(udev, 0, 1));
388 	} else if (flags & U3GFL_SIERRA_INIT) {
389 		u3g_sierra_init(udev);
390 	} else {
391 		/* no quirks */
392 		return (USB_ERR_INVAL);
393 	}
394 	return (0);			/* success */
395 }
396 
397 static int
398 u3g_driver_loaded(struct module *mod, int what, void *arg)
399 {
400 	switch (what) {
401 	case MOD_LOAD:
402 		/* register our autoinstall handler */
403 		usb2_test_huawei_autoinst_p = &u3g_test_huawei_autoinst;
404 		break;
405 	case MOD_UNLOAD:
406 		usb2_test_huawei_unload(NULL);
407 		break;
408 	default:
409 		return (EOPNOTSUPP);
410 	}
411  	return (0);
412 }
413 
414 static int
415 u3g_probe(device_t self)
416 {
417 	struct usb2_attach_arg *uaa = device_get_ivars(self);
418 
419 	if (uaa->usb2_mode != USB_MODE_HOST) {
420 		return (ENXIO);
421 	}
422 	if (uaa->info.bConfigIndex != U3G_CONFIG_INDEX) {
423 		return (ENXIO);
424 	}
425 	if (uaa->info.bInterfaceClass != UICLASS_VENDOR) {
426 		return (ENXIO);
427 	}
428 	return (u3g_lookup_huawei(uaa));
429 }
430 
431 static int
432 u3g_attach(device_t dev)
433 {
434 	struct usb2_config u3g_config_tmp[U3G_N_TRANSFER];
435 	struct usb2_attach_arg *uaa = device_get_ivars(dev);
436 	struct u3g_softc *sc = device_get_softc(dev);
437 	struct usb2_interface *iface;
438 	struct usb2_interface_descriptor *id;
439 	uint32_t flags;
440 	uint8_t m;
441 	uint8_t n;
442 	uint8_t i;
443 	uint8_t x;
444 	int error;
445 
446 	DPRINTF("sc=%p\n", sc);
447 
448 	flags = USB_GET_DRIVER_INFO(uaa);
449 
450 	if (flags & U3GFL_SAEL_M460_INIT)
451 		u3g_sael_m460_init(uaa->device);
452 
453 	/* copy in USB config */
454 	for (n = 0; n != U3G_N_TRANSFER; n++)
455 		u3g_config_tmp[n] = u3g_config[n];
456 
457 	device_set_usb2_desc(dev);
458 
459 	sc->sc_udev = uaa->device;
460 
461 	x = 0;		/* interface index */
462 	i = 0;		/* endpoint index */
463 	m = 0;		/* number of ports */
464 
465 	while (m != U3G_MAXPORTS) {
466 
467 		/* update BULK endpoint index */
468 		for (n = 0; n != U3G_N_TRANSFER; n++)
469 			u3g_config_tmp[n].ep_index = i;
470 
471 		iface = usb2_get_iface(uaa->device, x);
472 		if (iface == NULL) {
473 			if (m != 0)
474 				break;	/* end of interfaces */
475 			DPRINTF("did not find any modem endpoints\n");
476 			goto detach;
477 		}
478 
479 		id = usb2_get_interface_descriptor(iface);
480 		if ((id == NULL) ||
481 		    (id->bInterfaceClass != UICLASS_VENDOR)) {
482 			/* next interface */
483 			x++;
484 			i = 0;
485 			continue;
486 		}
487 
488 		/* try to allocate a set of BULK endpoints */
489 		error = usb2_transfer_setup(uaa->device, &x,
490 		    sc->sc_xfer[m], u3g_config_tmp, U3G_N_TRANSFER,
491 		    &sc->sc_ucom[m], &Giant);
492 		if (error) {
493 			/* next interface */
494 			x++;
495 			i = 0;
496 			continue;
497 		}
498 
499 		/* grab other interface, if any */
500                 if (x != uaa->info.bIfaceIndex)
501                         usb2_set_parent_iface(uaa->device, x,
502                             uaa->info.bIfaceIndex);
503 
504 		/* set stall by default */
505 		usb2_transfer_set_stall(sc->sc_xfer[m][U3G_BULK_WR]);
506 		usb2_transfer_set_stall(sc->sc_xfer[m][U3G_BULK_RD]);
507 
508 		m++;	/* found one port */
509 		i++;	/* next endpoint index */
510 	}
511 
512 	sc->sc_numports = m;
513 
514 	error = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom,
515 	    sc->sc_numports, sc, &u3g_callback, &Giant);
516 	if (error) {
517 		DPRINTF("usb2_com_attach failed\n");
518 		goto detach;
519 	}
520 	if (sc->sc_numports != 1) {
521 		/* be verbose */
522 		device_printf(dev, "Found %u ports.\n",
523 		    (unsigned int)sc->sc_numports);
524 	}
525 	return (0);
526 
527 detach:
528 	u3g_detach(dev);
529 	return (ENXIO);
530 }
531 
532 static int
533 u3g_detach(device_t dev)
534 {
535 	struct u3g_softc *sc = device_get_softc(dev);
536 	uint8_t m;
537 
538 	DPRINTF("sc=%p\n", sc);
539 
540 	/* NOTE: It is not dangerous to detach more ports than attached! */
541 	usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, U3G_MAXPORTS);
542 
543 	for (m = 0; m != U3G_MAXPORTS; m++)
544 		usb2_transfer_unsetup(sc->sc_xfer[m], U3G_N_TRANSFER);
545 
546 	return (0);
547 }
548 
549 static void
550 u3g_start_read(struct usb2_com_softc *ucom)
551 {
552 	struct u3g_softc *sc = ucom->sc_parent;
553 
554 	/* start read endpoint */
555 	usb2_transfer_start(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_RD]);
556 	return;
557 }
558 
559 static void
560 u3g_stop_read(struct usb2_com_softc *ucom)
561 {
562 	struct u3g_softc *sc = ucom->sc_parent;
563 
564 	/* stop read endpoint */
565 	usb2_transfer_stop(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_RD]);
566 	return;
567 }
568 
569 static void
570 u3g_start_write(struct usb2_com_softc *ucom)
571 {
572 	struct u3g_softc *sc = ucom->sc_parent;
573 
574 	usb2_transfer_start(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_WR]);
575 	return;
576 }
577 
578 static void
579 u3g_stop_write(struct usb2_com_softc *ucom)
580 {
581 	struct u3g_softc *sc = ucom->sc_parent;
582 
583 	usb2_transfer_stop(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_WR]);
584 	return;
585 }
586 
587 static void
588 u3g_write_callback(struct usb2_xfer *xfer)
589 {
590 	struct usb2_com_softc *ucom = xfer->priv_sc;
591 	uint32_t actlen;
592 
593 	switch (USB_GET_STATE(xfer)) {
594 	case USB_ST_TRANSFERRED:
595 	case USB_ST_SETUP:
596 tr_setup:
597 		if (usb2_com_get_data(ucom, xfer->frbuffers, 0,
598 		    U3G_BSIZE, &actlen)) {
599 			xfer->frlengths[0] = actlen;
600 			usb2_start_hardware(xfer);
601 		}
602 		break;
603 
604 	default:			/* Error */
605 		if (xfer->error != USB_ERR_CANCELLED) {
606 			/* do a builtin clear-stall */
607 			xfer->flags.stall_pipe = 1;
608 			goto tr_setup;
609 		}
610 		break;
611 	}
612 	return;
613 }
614 
615 static void
616 u3g_read_callback(struct usb2_xfer *xfer)
617 {
618 	struct usb2_com_softc *ucom = xfer->priv_sc;
619 
620 	switch (USB_GET_STATE(xfer)) {
621 	case USB_ST_TRANSFERRED:
622 		usb2_com_put_data(ucom, xfer->frbuffers, 0, xfer->actlen);
623 
624 	case USB_ST_SETUP:
625 tr_setup:
626 		xfer->frlengths[0] = xfer->max_data_length;
627 		usb2_start_hardware(xfer);
628 		break;
629 
630 	default:			/* Error */
631 		if (xfer->error != USB_ERR_CANCELLED) {
632 			/* do a builtin clear-stall */
633 			xfer->flags.stall_pipe = 1;
634 			goto tr_setup;
635 		}
636 		break;
637 	}
638 	return;
639 }
640