Home | History | Annotate | Line # | Download | only in usb
uhmodem.c revision 1.1
      1 /*	$NetBSD: uhmodem.c,v 1.1 2008/01/21 11:36:47 ichiro Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2008 Yojiro UO <yuo (at) nui.org>.
      5  * All rights reserved.
      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 MIND, USE, DATA OR PROFITS, WHETHER IN
     16  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     17  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18  */
     19 /*-
     20  * Copyright (c) 2002, Alexander Kabaev <kan.FreeBSD.org>.
     21  * All rights reserved.
     22  *
     23  * Redistribution and use in source and binary forms, with or without
     24  * modification, are permitted provided that the following conditions
     25  * are met:
     26  * 1. Redistributions of source code must retain the above copyright
     27  *    notice, this list of conditions and the following disclaimer.
     28  * 2. Redistributions in binary form must reproduce the above copyright
     29  *    notice, this list of conditions and the following disclaimer in the
     30  *    documentation and/or other materials provided with the distribution.
     31  *
     32  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     33  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     34  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     35  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     36  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     40  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     41  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     42  * SUCH DAMAGE.
     43  */
     44 /*
     45  * Copyright (c) 2001 The NetBSD Foundation, Inc.
     46  * All rights reserved.
     47  *
     48  * This code is derived from software contributed to The NetBSD Foundation
     49  * by Ichiro FUKUHARA (ichiro (at) ichiro.org).
     50  *
     51  * Redistribution and use in source and binary forms, with or without
     52  * modification, are permitted provided that the following conditions
     53  * are met:
     54  * 1. Redistributions of source code must retain the above copyright
     55  *    notice, this list of conditions and the following disclaimer.
     56  * 2. Redistributions in binary form must reproduce the above copyright
     57  *    notice, this list of conditions and the following disclaimer in the
     58  *    documentation and/or other materials provided with the distribution.
     59  * 3. All advertising materials mentioning features or use of this software
     60  *    must display the following acknowledgement:
     61  *        This product includes software developed by the NetBSD
     62  *        Foundation, Inc. and its contributors.
     63  * 4. Neither the name of The NetBSD Foundation nor the names of its
     64  *    contributors may be used to endorse or promote products derived
     65  *    from this software without specific prior written permission.
     66  *
     67  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     68  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     69  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     70  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     71  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     72  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     73  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     74  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     75  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     76  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     77  * POSSIBILITY OF SUCH DAMAGE.
     78  */
     79 
     80 #include <sys/cdefs.h>
     81 __KERNEL_RCSID(0, "$NetBSD: uhmodem.c,v 1.1 2008/01/21 11:36:47 ichiro Exp $");
     82 
     83 #include <sys/param.h>
     84 #include <sys/systm.h>
     85 #include <sys/kernel.h>
     86 #include <sys/malloc.h>
     87 #include <sys/ioccom.h>
     88 #include <sys/fcntl.h>
     89 #include <sys/conf.h>
     90 #include <sys/tty.h>
     91 #include <sys/file.h>
     92 #include <sys/select.h>
     93 #include <sys/proc.h>
     94 #include <sys/device.h>
     95 #include <sys/poll.h>
     96 #include <sys/sysctl.h>
     97 #include <sys/bus.h>
     98 
     99 #include <dev/usb/usb.h>
    100 #include <dev/usb/usbdi.h>
    101 #include <dev/usb/usbdi_util.h>
    102 #include <dev/usb/usbdivar.h>
    103 
    104 #include <dev/usb/usbcdc.h>
    105 #include <dev/usb/usbdevs.h>
    106 #include <dev/usb/usb_quirks.h>
    107 #include <dev/usb/ucomvar.h>
    108 #include <dev/usb/ubsavar.h>
    109 
    110 
    111 #define UHMODEMIBUFSIZE	4096
    112 #define UHMODEMOBUFSIZE	4096
    113 
    114 #ifdef UHMODEM_DEBUG
    115 Static int	uhmodemdebug = 0;
    116 #define DPRINTFN(n, x)  do { \
    117 				if (uhmodemdebug > (n)) \
    118 					logprintf x; \
    119 			} while (0)
    120 #else
    121 #define DPRINTFN(n, x)
    122 #endif
    123 #define DPRINTF(x) DPRINTFN(0, x)
    124 
    125 Static int uhmodem_open(void *, int);
    126 Static  usbd_status e220_modechange_request(usbd_device_handle);
    127 Static	usbd_status e220_endpointhalt(usbd_device_handle);
    128 #if 0
    129 Static  usbd_status e220_testreq(usbd_device_handle);
    130 #endif
    131 void e220_modechange_request_test(usbd_device_handle);
    132 
    133 #define UHMODEM_MAXCONN		2
    134 
    135 struct	uhmodem_softc {
    136 	struct ubsa_softc	sc_ubsa;
    137 };
    138 
    139 struct	ucom_methods uhmodem_methods = {
    140 	ubsa_get_status,
    141 	ubsa_set,
    142 	ubsa_param,
    143 	NULL,
    144 	uhmodem_open,
    145 	ubsa_close,
    146 	NULL,
    147 	NULL
    148 };
    149 
    150 Static const struct usb_devno uhmodem_devs[] = {
    151 	/* HUAWEI E220 / Emobile D0[12]HW */
    152 	{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220 },
    153 };
    154 #define uhmodem_lookup(v, p) usb_lookup(uhmodem_devs, v, p)
    155 
    156 USB_DECLARE_DRIVER(uhmodem);
    157 
    158 USB_MATCH(uhmodem)
    159 {
    160 	USB_IFMATCH_START(uhmodem, uaa);
    161 
    162 	if (uhmodem_lookup(uaa->vendor, uaa->product) != NULL)
    163 		/* XXX interface# 0,1 provide modem function, but this driver
    164 		   handles all modem in single device.  */
    165 		if (uaa->ifaceno == 0)
    166 			return (UMATCH_VENDOR_PRODUCT);
    167 	return (UMATCH_NONE);
    168 }
    169 
    170 USB_ATTACH(uhmodem)
    171 {
    172 	USB_IFATTACH_START(uhmodem, sc, uaa);
    173 	usbd_device_handle dev = uaa->device;
    174 	usb_config_descriptor_t *cdesc;
    175 	usb_interface_descriptor_t *id;
    176 	usb_endpoint_descriptor_t *ed;
    177 	char *devinfop;
    178 	const char *devname = USBDEVNAME(sc->sc_ubsa.sc_dev);
    179 	usbd_status err;
    180 	struct ucom_attach_args uca;
    181 	int i;
    182 	int j;
    183 	char comname[16];
    184 
    185 	devinfop = usbd_devinfo_alloc(dev, 0);
    186 	USB_ATTACH_SETUP;
    187 	printf("%s: %s\n", devname, devinfop);
    188 	usbd_devinfo_free(devinfop);
    189 
    190         sc->sc_ubsa.sc_udev = dev;
    191 	sc->sc_ubsa.sc_config_index = UBSA_DEFAULT_CONFIG_INDEX;
    192 	sc->sc_ubsa.sc_numif = 1; /* defaut device has one interface */
    193 
    194 	/* Hauwei E220 need special request to change its mode to modem */
    195 	if ((uaa->ifaceno == 0) && (uaa->class != 255)) {
    196 		err = e220_modechange_request(dev);
    197 		if (err) {
    198 			printf("%s: failed to change mode: %s\n",
    199 				devname, usbd_errstr(err));
    200 			sc->sc_ubsa.sc_dying = 1;
    201 			goto error;
    202 		}
    203 		printf("%s: mass storage only mode, reattach to enable modem\n",
    204 			devname);
    205 		sc->sc_ubsa.sc_dying = 1;
    206 		goto error;
    207 	}
    208 
    209 	/*
    210 	 * initialize rts, dtr variables to something
    211 	 * different from boolean 0, 1
    212 	 */
    213 	sc->sc_ubsa.sc_dtr = -1;
    214 	sc->sc_ubsa.sc_rts = -1;
    215 
    216 	sc->sc_ubsa.sc_quadumts = 1;
    217 	sc->sc_ubsa.sc_config_index = 0;
    218 	sc->sc_ubsa.sc_numif = 2; /* E220 has 2coms */
    219 
    220 	DPRINTF(("uhmodem attach: sc = %p\n", sc));
    221 
    222 	/* Move the device into the configured state. */
    223 	err = usbd_set_config_index(dev, sc->sc_ubsa.sc_config_index, 1);
    224 	if (err) {
    225 		printf("%s: failed to set configuration: %s\n",
    226 		    devname, usbd_errstr(err));
    227 		sc->sc_ubsa.sc_dying = 1;
    228 		goto error;
    229 	}
    230 
    231 	/* get the config descriptor */
    232 	cdesc = usbd_get_config_descriptor(sc->sc_ubsa.sc_udev);
    233 	if (cdesc == NULL) {
    234 		printf("%s: failed to get configuration descriptor\n",
    235 		    devname);
    236 		sc->sc_ubsa.sc_dying = 1;
    237 		goto error;
    238 	}
    239 
    240 	sc->sc_ubsa.sc_intr_number = -1;
    241 	sc->sc_ubsa.sc_intr_pipe = NULL;
    242 
    243 	/* get the interfaces */
    244 	for (i = 0; i < sc->sc_ubsa.sc_numif; i++) {
    245 		err = usbd_device2interface_handle(dev, UBSA_IFACE_INDEX_OFFSET+i,
    246 				 &sc->sc_ubsa.sc_iface[i]);
    247 		if (err) {
    248 			if (i == 0){
    249 				/* can not get main interface */
    250 				sc->sc_ubsa.sc_dying = 1;
    251 				goto error;
    252 			} else
    253 				break;
    254 		}
    255 
    256 		/* Find the endpoints */
    257 		id = usbd_get_interface_descriptor(sc->sc_ubsa.sc_iface[i]);
    258 		sc->sc_ubsa.sc_iface_number[i] = id->bInterfaceNumber;
    259 
    260 		/* initialize endpoints */
    261 		uca.bulkin = uca.bulkout = -1;
    262 
    263 		for (j = 0; j < id->bNumEndpoints; j++) {
    264 			ed = usbd_interface2endpoint_descriptor(
    265 				sc->sc_ubsa.sc_iface[i], j);
    266 			if (ed == NULL) {
    267 				printf("%s: no endpoint descriptor for %d (interface: %d)\n",
    268 			    		devname, j, i);
    269 				break;
    270 			}
    271 
    272 			if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
    273 			    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
    274 				sc->sc_ubsa.sc_intr_number = ed->bEndpointAddress;
    275 				sc->sc_ubsa.sc_isize = UGETW(ed->wMaxPacketSize);
    276 			} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
    277 			    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
    278 				uca.bulkin = ed->bEndpointAddress;
    279 			} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
    280 			    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
    281 				uca.bulkout = ed->bEndpointAddress;
    282 			}
    283 		} /* end of Endpoint loop */
    284 
    285 		if (sc->sc_ubsa.sc_intr_number == -1) {
    286 			printf("%s: HUAWEI E220 need to re-attach to enable modem function\n", devname);
    287 			if (i == 0) {
    288 				/* could not get intr for main tty */
    289 				sc->sc_ubsa.sc_dying = 1;
    290 				goto error;
    291 			} else
    292 				break;
    293 		}
    294 		if (uca.bulkin == -1) {
    295 			printf("%s: Could not find data bulk in\n", devname);
    296 			sc->sc_ubsa.sc_dying = 1;
    297 			goto error;
    298 		}
    299 
    300 		if (uca.bulkout == -1) {
    301 			printf("%s: Could not find data bulk out\n", devname);
    302 			sc->sc_ubsa.sc_dying = 1;
    303 			goto error;
    304 		}
    305 
    306 		switch (i) {
    307 		case 0:
    308 			sprintf(comname, "modem");
    309 			break;
    310 		case 1:
    311 			sprintf(comname, "monitor");
    312 			break;
    313 		case 2:
    314 			sprintf(comname, "unknown");
    315 			break;
    316 		default:
    317 			sprintf(comname, "int#%d", i);
    318 		}
    319 
    320 		uca.portno = i;
    321 		/* bulkin, bulkout set above */
    322 		uca.ibufsize = UHMODEMIBUFSIZE;
    323 		uca.obufsize = UHMODEMOBUFSIZE;
    324 		uca.ibufsizepad = UHMODEMIBUFSIZE;
    325 		uca.opkthdrlen = 0;
    326 		uca.device = dev;
    327 		uca.iface = sc->sc_ubsa.sc_iface[i];
    328 		uca.methods = &uhmodem_methods;
    329 		uca.arg = &sc->sc_ubsa;
    330 		uca.info = comname;
    331 		DPRINTF(("uhmodem: int#=%d, in = 0x%x, out = 0x%x, intr = 0x%x\n",
    332 	    		i, uca.bulkin, uca.bulkout, sc->sc_ubsa.sc_intr_number));
    333 		sc->sc_ubsa.sc_subdevs[i] = config_found_sm_loc(self, "ucombus", NULL,
    334 				 &uca, ucomprint, ucomsubmatch);
    335 	} /* end of Interface loop */
    336 
    337 	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_ubsa.sc_udev,
    338 			   USBDEV(sc->sc_ubsa.sc_dev));
    339 
    340 	USB_ATTACH_SUCCESS_RETURN;
    341 
    342 error:
    343 	USB_ATTACH_ERROR_RETURN;
    344 }
    345 
    346 USB_DETACH(uhmodem)
    347 {
    348 	USB_DETACH_START(uhmodem, sc);
    349 	int i;
    350 	int rv = 0;
    351 
    352 	DPRINTF(("uhmodem_detach: sc = %p\n", sc));
    353 
    354 	if (sc->sc_ubsa.sc_intr_pipe != NULL) {
    355 		usbd_abort_pipe(sc->sc_ubsa.sc_intr_pipe);
    356 		usbd_close_pipe(sc->sc_ubsa.sc_intr_pipe);
    357 		free(sc->sc_ubsa.sc_intr_buf, M_USBDEV);
    358 		sc->sc_ubsa.sc_intr_pipe = NULL;
    359 	}
    360 
    361 	sc->sc_ubsa.sc_dying = 1;
    362 	for (i = 0; i < sc->sc_ubsa.sc_numif; i++) {
    363 		if (sc->sc_ubsa.sc_subdevs[i] != NULL) {
    364 			rv |= config_detach(sc->sc_ubsa.sc_subdevs[i], flags);
    365 			sc->sc_ubsa.sc_subdevs[i] = NULL;
    366 		}
    367 	}
    368 
    369 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_ubsa.sc_udev,
    370 			   USBDEV(sc->sc_ubsa.sc_dev));
    371 
    372 	return (rv);
    373 }
    374 
    375 int
    376 uhmodem_activate(device_ptr_t self, enum devact act)
    377 {
    378 	struct uhmodem_softc *sc = (struct uhmodem_softc *)self;
    379 	int rv = 0;
    380 	int i;
    381 
    382 	switch (act) {
    383 	case DVACT_ACTIVATE:
    384 		return (EOPNOTSUPP);
    385 
    386 	case DVACT_DEACTIVATE:
    387 		for (i = 0; i < sc->sc_ubsa.sc_numif; i++) {
    388 			if (sc->sc_ubsa.sc_subdevs[i] != NULL)
    389 				rv |= config_deactivate(sc->sc_ubsa.sc_subdevs[i]);
    390 		}
    391 		sc->sc_ubsa.sc_dying = 1;
    392 		break;
    393 	}
    394 	return (rv);
    395 }
    396 
    397 Static int
    398 uhmodem_open(void *addr, int portno)
    399 {
    400 	struct ubsa_softc *sc = addr;
    401 	usbd_status err;
    402 
    403 	if (sc->sc_dying)
    404 		return (ENXIO);
    405 
    406 	DPRINTF(("%s: sc = %p\n", __func__, sc));
    407 
    408 	err = e220_endpointhalt(sc->sc_udev);
    409 	if (err)
    410 		printf("%s: endpointhalt fail\n", __func__);
    411 	else
    412 		usbd_delay_ms(sc->sc_udev, 50);
    413 #if 0 /* currenly disable */
    414 	err = e220_testreq(sc->sc_udev);
    415 	if (err)
    416 		printf("%s: send testreq fail\n", __func__);
    417 	else
    418 		usbd_delay_ms(sc->sc_udev, 50);
    419 #endif
    420 
    421 	if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
    422 		sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
    423 		/* XXX only iface# = 0 has intr line */
    424 		/* XXX E220 specific? need to check */
    425 		err = usbd_open_pipe_intr(sc->sc_iface[0],
    426 		    sc->sc_intr_number,
    427 		    USBD_SHORT_XFER_OK,
    428 		    &sc->sc_intr_pipe,
    429 		    sc,
    430 		    sc->sc_intr_buf,
    431 		    sc->sc_isize,
    432 		    ubsa_intr,
    433 		    UBSA_INTR_INTERVAL);
    434 		if (err) {
    435 			printf("%s: cannot open interrupt pipe (addr %d)\n",
    436 			    USBDEVNAME(sc->sc_dev),
    437 			    sc->sc_intr_number);
    438 			return (EIO);
    439 		}
    440 	}
    441 
    442 	return (0);
    443 }
    444 
    445 /*
    446  * Hauwei E220 needs special request to enable modem function.
    447  * -- DEVICE_REMOTE_WAKEUP ruquest to endpoint 2.
    448  */
    449 Static  usbd_status
    450 e220_modechange_request(usbd_device_handle dev)
    451 {
    452 #define E220_MODE_CHANGE_REQUEST 0x2
    453 	usb_device_request_t req;
    454 	usbd_status err;
    455 
    456 	req.bmRequestType = UT_WRITE_DEVICE;
    457 	req.bRequest = UR_SET_FEATURE;
    458 	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
    459 	USETW(req.wIndex, E220_MODE_CHANGE_REQUEST);
    460 	USETW(req.wLength, 0);
    461 
    462 	DPRINTF(("%s: send e220 mode change request\n", __func__));
    463 	err = usbd_do_request(dev, &req, 0);
    464 	if (err) {
    465 		DPRINTF(("%s: E220 mode change fail\n", __func__));
    466 		return (EIO);
    467 	}
    468 
    469 	return (0);
    470 #undef E220_MODE_CHANGE_REQUEST
    471 }
    472 
    473 Static  usbd_status
    474 e220_endpointhalt(usbd_device_handle dev)
    475 {
    476 	usb_device_request_t req;
    477 	usbd_status err;
    478 
    479 	/* CLEAR feature / endpoint halt to modem i/o */
    480 	req.bmRequestType = UT_WRITE_ENDPOINT;
    481 	req.bRequest = UR_CLEAR_FEATURE;
    482 	USETW(req.wValue, UF_ENDPOINT_HALT);
    483 	USETW(req.wIndex, 0x0082); /* should get value from softc etc.*/
    484 	USETW(req.wLength, 0);
    485 	err = usbd_do_request(dev, &req, 0);
    486 	if (err) {
    487 		DPRINTF(("%s: E220 request test ENDPOINT_HALT fail\n", __func__));
    488 		return (EIO);
    489 	}
    490 	req.bmRequestType = UT_WRITE_ENDPOINT;
    491 	req.bRequest = UR_CLEAR_FEATURE;
    492 	USETW(req.wValue, UF_ENDPOINT_HALT);
    493 	USETW(req.wIndex, 0x0002); /* should get value from softc etc.*/
    494 	USETW(req.wLength, 0);
    495 	err = usbd_do_request(dev, &req, 0);
    496 	if (err) {
    497 		DPRINTF(("%s: E220 request test ENDPOINT_HALT fail\n", __func__));
    498 		return (EIO);
    499 	}
    500 
    501 	return (0);
    502 }
    503 
    504 #if 0
    505 /*
    506  * Windows device driver send these sequens of USB requests.
    507  * However currently I can't understand what the messege is,
    508  * disable this code when I get more information about it.
    509  */
    510 Static  usbd_status
    511 e220_testreq(usbd_device_handle dev)
    512 {
    513 	uint8_t data[8];
    514 	usb_device_request_t req;
    515 	usbd_status err;
    516 	int i;
    517 
    518 	/* vendor specific unknown requres */
    519 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
    520 	req.bRequest = 0x02;
    521 	USETW(req.wValue, 0x0001);
    522 	USETW(req.wIndex, 0x0000);
    523 	USETW(req.wLength, 2);
    524 	data[0] = 0x0;
    525 	data[1] = 0x0;
    526 	err = usbd_do_request(dev, &req, data);
    527 	if (err)
    528 		goto error;
    529 
    530 	/* vendor specific unknown sequence */
    531 #define E220_CLASS_SETUP	0x22
    532 #define	E220_CLASS_READ		0x21
    533 #define	E220_CLASS_WRITE	0x20
    534 
    535 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
    536 	req.bRequest = E220_CLASS_SETUP;	// 0x22
    537 	USETW(req.wValue, 0x0001);
    538 	USETW(req.wIndex, 0x0000);
    539 	USETW(req.wLength, 0);
    540 	err = usbd_do_request(dev, &req, 0);
    541 	if (err)
    542 		goto error;
    543 
    544 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
    545 	req.bRequest = E220_CLASS_READ;
    546 	USETW(req.wValue, 0x0000);
    547 	USETW(req.wIndex, 0x0000);
    548 	USETW(req.wLength, 7);
    549 	err = usbd_do_request(dev, &req, &data);
    550 	if (err)
    551 		goto error;
    552 
    553 	data[1] = 0x8;
    554 	data[2] = 0x7;
    555 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
    556 	req.bRequest = E220_CLASS_WRITE;
    557 	USETW(req.wValue, 0x0000);
    558 	USETW(req.wIndex, 0x0000);
    559 	USETW(req.wLength, 7);
    560 	err = usbd_do_request(dev, &req, data);
    561 	if (err)
    562 		goto error;
    563 
    564 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
    565 	req.bRequest = E220_CLASS_READ;
    566 	USETW(req.wValue, 0x0000);
    567 	USETW(req.wIndex, 0x0000);
    568 	USETW(req.wLength, 7);
    569 	err = usbd_do_request(dev, &req, &data);
    570 	if (err)
    571 		goto error;
    572 	printf("%s(0x21):", __func__);
    573 
    574 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
    575 	req.bRequest = E220_CLASS_SETUP;	// 0x22
    576 	USETW(req.wValue, 0x0003);
    577 	USETW(req.wIndex, 0x0000);
    578 	USETW(req.wLength, 0);
    579 	err = usbd_do_request(dev, &req, 0);
    580 	if (err)
    581 		goto error;
    582 
    583 	return (0);
    584 error:
    585 	DPRINTF(("%s: E220 request test SETUP fail\n", __func__));
    586 	return (EIO);
    587 }
    588 #endif
    589 
    590