Home | History | Annotate | Line # | Download | only in usb
u3g.c revision 1.20.2.1
      1  1.20.2.1      yamt /*	$NetBSD: u3g.c,v 1.20.2.1 2011/11/10 14:31:47 yamt Exp $	*/
      2       1.9    martin 
      3       1.9    martin /*-
      4       1.9    martin  * Copyright (c) 2009 The NetBSD Foundation, Inc.
      5       1.9    martin  * All rights reserved.
      6       1.9    martin  *
      7       1.9    martin  * This code is derived from software contributed to The NetBSD Foundation.
      8       1.9    martin  *
      9       1.9    martin  * Redistribution and use in source and binary forms, with or without
     10       1.9    martin  * modification, are permitted provided that the following conditions
     11       1.9    martin  * are met:
     12       1.9    martin  * 1. Redistributions of source code must retain the above copyright
     13       1.9    martin  *    notice, this list of conditions and the following disclaimer.
     14       1.9    martin  * 2. Redistributions in binary form must reproduce the above copyright
     15       1.9    martin  *    notice, this list of conditions and the following disclaimer in the
     16       1.9    martin  *    documentation and/or other materials provided with the distribution.
     17       1.9    martin  *
     18       1.9    martin  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     19       1.9    martin  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     20       1.9    martin  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     21       1.9    martin  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     22       1.9    martin  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     23       1.9    martin  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     24       1.9    martin  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     25       1.9    martin  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     26       1.9    martin  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     27       1.9    martin  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     28       1.9    martin  * POSSIBILITY OF SUCH DAMAGE.
     29       1.9    martin  */
     30       1.7    plunky 
     31       1.1     joerg /*
     32       1.1     joerg  * Copyright (c) 2008 AnyWi Technologies
     33       1.1     joerg  *   Author: Andrea Guzzo <aguzzo (at) anywi.com>
     34       1.1     joerg  *   * based on uark.c 1.1 2006/08/14 08:30:22 jsg *
     35       1.1     joerg  *   * parts from ubsa.c 183348 2008-09-25 12:00:56Z phk *
     36       1.1     joerg  *
     37       1.1     joerg  * Permission to use, copy, modify, and distribute this software for any
     38       1.1     joerg  * purpose with or without fee is hereby granted, provided that the above
     39       1.1     joerg  * copyright notice and this permission notice appear in all copies.
     40       1.1     joerg  *
     41       1.1     joerg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     42       1.1     joerg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     43       1.1     joerg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     44       1.1     joerg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     45       1.1     joerg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     46       1.1     joerg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     47       1.1     joerg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     48       1.1     joerg  *
     49       1.1     joerg  * $FreeBSD$
     50       1.1     joerg  */
     51       1.1     joerg 
     52       1.7    plunky #include <sys/cdefs.h>
     53  1.20.2.1      yamt __KERNEL_RCSID(0, "$NetBSD: u3g.c,v 1.20.2.1 2011/11/10 14:31:47 yamt Exp $");
     54       1.7    plunky 
     55       1.1     joerg #include <sys/param.h>
     56       1.1     joerg #include <sys/systm.h>
     57       1.1     joerg #include <sys/kernel.h>
     58       1.1     joerg #include <sys/malloc.h>
     59       1.1     joerg #include <sys/bus.h>
     60       1.1     joerg #include <sys/conf.h>
     61       1.1     joerg #include <sys/tty.h>
     62       1.1     joerg 
     63       1.1     joerg #include <dev/usb/usb.h>
     64       1.1     joerg #include <dev/usb/usbdi.h>
     65       1.1     joerg #include <dev/usb/usbdivar.h>
     66       1.1     joerg #include <dev/usb/usbdi_util.h>
     67       1.1     joerg 
     68       1.1     joerg #include <dev/usb/ucomvar.h>
     69       1.1     joerg 
     70       1.1     joerg #include "usbdevs.h"
     71       1.1     joerg 
     72       1.9    martin /*
     73       1.9    martin  * We read/write data from/to the device in 4KB chunks to maximise
     74       1.9    martin  * performance.
     75       1.9    martin  */
     76       1.9    martin #define U3G_BUFF_SIZE	4096
     77       1.9    martin 
     78       1.9    martin /*
     79       1.9    martin  * Some 3G devices (the Huawei E160/E220 springs to mind here) buffer up
     80       1.9    martin  * data internally even when the USB pipes are closed. So on first open,
     81       1.9    martin  * we can receive a large chunk of stale data.
     82       1.9    martin  *
     83       1.9    martin  * This causes a real problem because the default TTYDEF_LFLAG (applied
     84       1.9    martin  * on first open) has the ECHO flag set, resulting in all the stale data
     85       1.9    martin  * being echoed straight back to the device by the tty(4) layer. Some
     86       1.9    martin  * devices (again, the Huawei E160/E220 for example) react to this spew
     87       1.9    martin  * by going catatonic.
     88       1.9    martin  *
     89       1.9    martin  * All this happens before the application gets a chance to disable ECHO.
     90       1.9    martin  *
     91       1.9    martin  * We work around this by ignoring all data received from the device for
     92       1.9    martin  * a period of two seconds, or until the application starts sending data -
     93       1.9    martin  * whichever comes first.
     94       1.9    martin  */
     95       1.9    martin #define	U3G_PURGE_SECS	2
     96       1.9    martin 
     97       1.9    martin /*
     98       1.9    martin  * Define bits for the virtual modem control pins.
     99       1.9    martin  * The input pin states are reported via the interrupt pipe on some devices.
    100       1.9    martin  */
    101       1.9    martin #define	U3G_OUTPIN_DTR	(1u << 0)
    102       1.9    martin #define	U3G_OUTPIN_RTS	(1u << 1)
    103       1.9    martin #define	U3G_INPIN_DCD	(1u << 0)
    104       1.9    martin #define	U3G_INPIN_DSR	(1u << 1)
    105       1.9    martin #define	U3G_INPIN_RI	(1u << 3)
    106       1.9    martin 
    107       1.9    martin /*
    108       1.9    martin  * USB request to set the output pin status
    109       1.9    martin  */
    110       1.9    martin #define	U3G_SET_PIN	0x22
    111       1.1     joerg 
    112       1.1     joerg struct u3g_softc {
    113       1.9    martin 	device_t		sc_dev;
    114       1.9    martin 	usbd_device_handle	sc_udev;
    115       1.9    martin 	bool			sc_dying;	/* We're going away */
    116       1.9    martin 
    117       1.9    martin 	device_t		sc_ucom;	/* Child ucom(4) handle */
    118       1.9    martin 	int			sc_ifaceno;	/* Device interface number */
    119       1.9    martin 
    120       1.9    martin 	bool			sc_open;	/* Device is in use */
    121       1.9    martin 	bool			sc_purging;	/* Purging stale data */
    122       1.9    martin 	struct timeval		sc_purge_start;	/* Control duration of purge */
    123       1.9    martin 
    124       1.9    martin 	u_char			sc_msr;		/* Emulated 'msr' */
    125       1.9    martin 	uint16_t		sc_outpins;	/* Output pin state */
    126       1.9    martin 
    127       1.9    martin 	usbd_pipe_handle	sc_intr_pipe;	/* Interrupt pipe */
    128       1.9    martin 	u_char			*sc_intr_buff;	/* Interrupt buffer */
    129       1.1     joerg };
    130       1.1     joerg 
    131       1.9    martin /*
    132       1.9    martin  * The device driver has two personalities. The first uses the 'usbdevif'
    133       1.9    martin  * interface attribute so that a match will claim the entire USB device
    134       1.9    martin  * for itself. This is used for when a device needs to be mode-switched
    135       1.9    martin  * and ensures any other interfaces present cannot be claimed by other
    136       1.9    martin  * drivers while the mode-switch is in progress.
    137       1.9    martin  *
    138       1.9    martin  * The second personality uses the 'usbifif' interface attribute so that
    139       1.9    martin  * it can claim the 3G modem interfaces for itself, leaving others (such
    140       1.9    martin  * as the mass storage interfaces on some devices) for other drivers.
    141       1.9    martin  */
    142       1.9    martin static int u3ginit_match(device_t, cfdata_t, void *);
    143       1.9    martin static void u3ginit_attach(device_t, device_t, void *);
    144       1.9    martin static int u3ginit_detach(device_t, int);
    145       1.9    martin 
    146       1.9    martin CFATTACH_DECL2_NEW(u3ginit, 0, u3ginit_match,
    147       1.9    martin     u3ginit_attach, u3ginit_detach, NULL, NULL, NULL);
    148       1.9    martin 
    149       1.9    martin 
    150       1.9    martin static int u3g_match(device_t, cfdata_t, void *);
    151       1.9    martin static void u3g_attach(device_t, device_t, void *);
    152       1.9    martin static int u3g_detach(device_t, int);
    153       1.9    martin static int u3g_activate(device_t, enum devact);
    154       1.9    martin static void u3g_childdet(device_t, device_t);
    155       1.9    martin 
    156       1.9    martin CFATTACH_DECL2_NEW(u3g, sizeof(struct u3g_softc), u3g_match,
    157       1.9    martin     u3g_attach, u3g_detach, u3g_activate, NULL, u3g_childdet);
    158       1.9    martin 
    159       1.9    martin 
    160       1.9    martin static void u3g_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
    161       1.9    martin static void u3g_get_status(void *, int, u_char *, u_char *);
    162       1.9    martin static void u3g_set(void *, int, int, int);
    163       1.9    martin static int  u3g_open(void *, int);
    164       1.9    martin static void u3g_close(void *, int);
    165       1.9    martin static void u3g_read(void *, int, u_char **, uint32_t *);
    166       1.9    martin static void u3g_write(void *, int, u_char *, u_char *, u_int32_t *);
    167       1.9    martin 
    168       1.1     joerg struct ucom_methods u3g_methods = {
    169       1.9    martin 	u3g_get_status,
    170       1.9    martin 	u3g_set,
    171       1.1     joerg 	NULL,
    172       1.1     joerg 	NULL,
    173       1.9    martin 	u3g_open,
    174       1.9    martin 	u3g_close,
    175       1.9    martin 	u3g_read,
    176       1.9    martin 	u3g_write,
    177       1.1     joerg };
    178       1.1     joerg 
    179       1.9    martin /*
    180       1.9    martin  * Allegedly supported devices
    181       1.9    martin  */
    182       1.1     joerg static const struct usb_devno u3g_devs[] = {
    183      1.13       riz         { USB_VENDOR_DELL, USB_PRODUCT_DELL_W5500 },
    184       1.1     joerg 	/* OEM: Huawei */
    185      1.19     veego 	{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E1750 },
    186      1.18       apb 	{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E1820 },
    187       1.1     joerg 	{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220 },
    188      1.11     pooka 	{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_K3765 },
    189      1.13       riz 	{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE },
    190      1.13       riz 	/* OEM: Merlin */
    191      1.13       riz 	{ USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620 },
    192       1.1     joerg 	/* OEM: Novatel */
    193       1.3     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_ES620 },
    194       1.1     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_MC950D },
    195      1.13       riz #if 0
    196      1.14       riz 	/* These are matched in u3ginit_match() */
    197      1.13       riz 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_MC950D_DRIVER },
    198      1.14       riz 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U760_DRIVER },
    199      1.13       riz #endif
    200      1.13       riz 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_MERLINU740 },
    201      1.13       riz 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_MERLINV620 },
    202      1.13       riz 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_S720 },
    203       1.3     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U720 },
    204       1.3     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U727 },
    205       1.3     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U740_2 },
    206      1.14       riz 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U760 },
    207       1.3     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U870 },
    208       1.3     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_V740 },
    209       1.3     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_X950D },
    210       1.3     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_XU870 },
    211      1.13       riz 	/* OEM: Option N.V. */
    212      1.13       riz 	{ USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_QUADPLUSUMTS },
    213      1.13       riz 	{ USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_HSDPA },
    214      1.13       riz 	{ USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_GTMAXHSUPA },
    215      1.13       riz 	/* OEM: Qualcomm, Inc. */
    216      1.13       riz 	{ USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM },
    217      1.13       riz 	{ USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_ZTE_MF626 },
    218       1.1     joerg 
    219       1.3     joerg 	/* OEM: Sierra Wireless: */
    220       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U },
    221       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E },
    222      1.13       riz 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U },
    223       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880 },
    224       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E },
    225       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U },
    226       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881 },
    227       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E },
    228       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U },
    229      1.13       riz 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580 },
    230      1.13       riz 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595 },
    231      1.13       riz 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875 },
    232      1.13       riz 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597 },
    233       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625 },
    234       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720 },
    235       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2 },
    236       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725 },
    237       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755 },
    238       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2 },
    239       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3 },
    240       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765 },
    241       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2 },
    242       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780 },
    243       1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781 },
    244      1.13       riz 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725 },
    245      1.12       riz 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_USB305 },
    246      1.17       scw 
    247      1.17       scw 	/* Toshiba */
    248      1.17       scw 	{ USB_VENDOR_TOSHIBA, USB_PRODUCT_TOSHIBA_HSDPA_MODEM_EU870DT1 },
    249      1.20  christos 
    250      1.20  christos 	/* 4G Systems */
    251      1.20  christos 	{ USB_VENDOR_4GSYSTEMS, USB_PRODUCT_4GSYSTEMS_XSSTICK_P14 },
    252       1.1     joerg };
    253       1.1     joerg 
    254       1.1     joerg static int
    255      1.20  christos send_bulkmsg(usbd_device_handle dev, void *cmd, size_t cmdlen)
    256       1.2     joerg {
    257       1.2     joerg 	usbd_interface_handle iface;
    258       1.2     joerg 	usb_interface_descriptor_t *id;
    259       1.2     joerg 	usb_endpoint_descriptor_t *ed;
    260       1.2     joerg 	usbd_pipe_handle pipe;
    261       1.2     joerg 	usbd_xfer_handle xfer;
    262       1.2     joerg 	int err, i;
    263       1.2     joerg 
    264       1.2     joerg 	/* Move the device into the configured state. */
    265       1.9    martin 	err = usbd_set_config_index(dev, 0, 0);
    266       1.2     joerg 	if (err) {
    267       1.2     joerg 		aprint_error("u3g: failed to set configuration index\n");
    268       1.2     joerg 		return UMATCH_NONE;
    269       1.2     joerg 	}
    270       1.2     joerg 
    271       1.9    martin 	err = usbd_device2interface_handle(dev, 0, &iface);
    272       1.2     joerg 	if (err != 0) {
    273      1.11     pooka 		aprint_error("u3ginit: failed to get interface\n");
    274       1.2     joerg 		return UMATCH_NONE;
    275       1.2     joerg 	}
    276       1.2     joerg 
    277       1.2     joerg 	id = usbd_get_interface_descriptor(iface);
    278       1.2     joerg 	ed = NULL;
    279       1.2     joerg 	for (i = 0 ; i < id->bNumEndpoints ; i++) {
    280       1.2     joerg 		ed = usbd_interface2endpoint_descriptor(iface, i);
    281       1.2     joerg 		if (ed == NULL)
    282       1.2     joerg 			continue;
    283       1.2     joerg 		if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_OUT)
    284       1.2     joerg 			continue;
    285       1.2     joerg 		if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK)
    286       1.2     joerg 			break;
    287       1.2     joerg 	}
    288       1.2     joerg 
    289       1.2     joerg 	if (i == id->bNumEndpoints)
    290       1.2     joerg 		return UMATCH_NONE;
    291       1.2     joerg 
    292      1.11     pooka 	err = usbd_open_pipe(iface, ed->bEndpointAddress,
    293      1.11     pooka 	    USBD_EXCLUSIVE_USE, &pipe);
    294       1.2     joerg 	if (err != 0) {
    295      1.11     pooka 		aprint_error("u3ginit: failed to open bulk transfer pipe %d\n",
    296       1.2     joerg 		    ed->bEndpointAddress);
    297       1.2     joerg 		return UMATCH_NONE;
    298       1.2     joerg 	}
    299       1.2     joerg 
    300       1.9    martin 	xfer = usbd_alloc_xfer(dev);
    301       1.2     joerg 	if (xfer != NULL) {
    302      1.11     pooka 		usbd_setup_xfer(xfer, pipe, NULL, cmd, cmdlen,
    303       1.2     joerg 		    USBD_SYNCHRONOUS, USBD_DEFAULT_TIMEOUT, NULL);
    304       1.2     joerg 
    305       1.2     joerg 		err = usbd_transfer(xfer);
    306      1.11     pooka 
    307      1.11     pooka #if 0 /* XXXpooka: at least my huawei "fails" this always, but still detaches */
    308       1.2     joerg 		if (err)
    309      1.11     pooka 			aprint_error("u3ginit: transfer failed\n");
    310      1.11     pooka #else
    311      1.11     pooka 		err = 0;
    312      1.11     pooka #endif
    313       1.2     joerg 		usbd_free_xfer(xfer);
    314       1.2     joerg 	} else {
    315      1.11     pooka 		aprint_error("u3ginit: failed to allocate xfer\n");
    316       1.2     joerg 		err = USBD_NOMEM;
    317       1.2     joerg 	}
    318       1.2     joerg 
    319       1.2     joerg 	usbd_abort_pipe(pipe);
    320       1.2     joerg 	usbd_close_pipe(pipe);
    321       1.2     joerg 
    322       1.2     joerg 	return (err == USBD_NORMAL_COMPLETION ? UMATCH_HIGHEST : UMATCH_NONE);
    323       1.2     joerg }
    324       1.2     joerg 
    325       1.2     joerg static int
    326      1.11     pooka u3g_novatel_reinit(usbd_device_handle dev)
    327      1.11     pooka {
    328      1.11     pooka 	unsigned char cmd[31];
    329      1.11     pooka 
    330      1.11     pooka 	memset(cmd, 0, sizeof(cmd));
    331      1.11     pooka 	/* Byte 0..3: Command Block Wrapper (CBW) signature */
    332      1.11     pooka 	cmd[0] = 0x55;
    333      1.11     pooka 	cmd[1] = 0x53;
    334      1.11     pooka 	cmd[2] = 0x42;
    335      1.11     pooka 	cmd[3] = 0x43;
    336      1.11     pooka 	/* 4..7: CBW Tag, has to unique, but only a single transfer used. */
    337      1.11     pooka 	cmd[4] = 0x01;
    338      1.11     pooka 	/* 8..11: CBW Transfer Length, no data here */
    339      1.11     pooka 	/* 12: CBW Flag: output, so 0 */
    340      1.11     pooka 	/* 13: CBW Lun: 0 */
    341      1.11     pooka 	/* 14: CBW Length */
    342      1.11     pooka 	cmd[14] = 0x06;
    343      1.11     pooka 	/* Rest is the SCSI payload */
    344      1.11     pooka 	/* 0: SCSI START/STOP opcode */
    345      1.11     pooka 	cmd[15] = 0x1b;
    346      1.11     pooka 	/* 1..3 unused */
    347      1.11     pooka 	/* 4 Load/Eject command */
    348      1.11     pooka 	cmd[19] = 0x02;
    349      1.11     pooka 	/* 5: unused */
    350      1.11     pooka 
    351      1.11     pooka 	return send_bulkmsg(dev, cmd, sizeof(cmd));
    352      1.11     pooka }
    353      1.11     pooka 
    354      1.11     pooka static int
    355       1.1     joerg u3g_huawei_reinit(usbd_device_handle dev)
    356       1.1     joerg {
    357       1.9    martin 	/*
    358       1.9    martin 	 * The Huawei device presents itself as a umass device with Windows
    359       1.1     joerg 	 * drivers on it. After installation of the driver, it reinits into a
    360       1.1     joerg 	 * 3G serial device.
    361       1.1     joerg 	 */
    362       1.1     joerg 	usb_device_request_t req;
    363       1.1     joerg 	usb_config_descriptor_t *cdesc;
    364       1.1     joerg 
    365       1.1     joerg 	/* Get the config descriptor */
    366       1.1     joerg 	cdesc = usbd_get_config_descriptor(dev);
    367       1.9    martin 	if (cdesc == NULL) {
    368       1.9    martin 		usb_device_descriptor_t dd;
    369       1.9    martin 
    370       1.9    martin 		if (usbd_get_device_desc(dev, &dd) != 0)
    371       1.9    martin 			return (UMATCH_NONE);
    372       1.9    martin 
    373       1.9    martin 		if (dd.bNumConfigurations != 1)
    374       1.9    martin 			return (UMATCH_NONE);
    375       1.9    martin 
    376       1.9    martin 		if (usbd_set_config_index(dev, 0, 1) != 0)
    377       1.9    martin 			return (UMATCH_NONE);
    378       1.9    martin 
    379       1.9    martin 		cdesc = usbd_get_config_descriptor(dev);
    380       1.9    martin 
    381       1.9    martin 		if (cdesc == NULL)
    382       1.9    martin 			return (UMATCH_NONE);
    383       1.9    martin 	}
    384       1.1     joerg 
    385       1.9    martin 	/*
    386       1.9    martin 	 * One iface means umass mode, more than 1 (4 usually) means 3G mode.
    387       1.9    martin 	 *
    388       1.9    martin 	 * XXX: We should check the first interface's device class just to be
    389       1.9    martin 	 * sure. If it's a mass storage device, then we can be fairly certain
    390       1.9    martin 	 * it needs a mode-switch.
    391       1.9    martin 	 */
    392       1.1     joerg 	if (cdesc->bNumInterface > 1)
    393       1.9    martin 		return (UMATCH_NONE);
    394       1.1     joerg 
    395       1.1     joerg 	req.bmRequestType = UT_WRITE_DEVICE;
    396       1.1     joerg 	req.bRequest = UR_SET_FEATURE;
    397       1.1     joerg 	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
    398       1.1     joerg 	USETW(req.wIndex, UHF_PORT_SUSPEND);
    399       1.1     joerg 	USETW(req.wLength, 0);
    400       1.1     joerg 
    401       1.1     joerg 	(void) usbd_do_request(dev, &req, 0);
    402       1.1     joerg 
    403      1.11     pooka 	return (UMATCH_HIGHEST); /* Prevent umass from attaching */
    404      1.11     pooka }
    405      1.11     pooka 
    406      1.11     pooka static int
    407      1.11     pooka u3g_huawei_k3765_reinit(usbd_device_handle dev)
    408      1.11     pooka {
    409      1.11     pooka 	unsigned char cmd[31];
    410      1.11     pooka 
    411      1.11     pooka 	/* magic string adapted from some webpage */
    412      1.11     pooka 	memset(cmd, 0, sizeof(cmd));
    413      1.11     pooka 	cmd[0] = 0x55;
    414      1.11     pooka 	cmd[1] = 0x53;
    415      1.11     pooka 	cmd[2] = 0x42;
    416      1.11     pooka 	cmd[3] = 0x43;
    417      1.11     pooka 	cmd[15]= 0x11;
    418      1.11     pooka 	cmd[16]= 0x06;
    419      1.11     pooka 
    420      1.11     pooka 	return send_bulkmsg(dev, cmd, sizeof(cmd));
    421       1.1     joerg }
    422       1.1     joerg 
    423       1.1     joerg static int
    424       1.4  christos u3g_sierra_reinit(usbd_device_handle dev)
    425       1.4  christos {
    426       1.4  christos 	/* Some Sierra devices presents themselves as a umass device with
    427       1.4  christos 	 * Windows drivers on it. After installation of the driver, it
    428       1.4  christos 	 * reinits into a * 3G serial device.
    429       1.4  christos 	 */
    430       1.4  christos 	usb_device_request_t req;
    431       1.4  christos 
    432       1.4  christos 	req.bmRequestType = UT_VENDOR;
    433       1.4  christos 	req.bRequest = UR_SET_INTERFACE;
    434       1.4  christos 	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
    435       1.4  christos 	USETW(req.wIndex, UHF_PORT_CONNECTION);
    436       1.4  christos 	USETW(req.wLength, 0);
    437       1.4  christos 
    438       1.4  christos 	(void) usbd_do_request(dev, &req, 0);
    439       1.4  christos 
    440       1.4  christos 	return (UMATCH_HIGHEST); /* Match to prevent umass from attaching */
    441       1.4  christos }
    442       1.4  christos 
    443      1.20  christos static int
    444      1.20  christos u3g_4gsystems_reinit(usbd_device_handle dev)
    445      1.20  christos {
    446      1.20  christos 	/* magic string adapted from usb_modeswitch database */
    447      1.20  christos 	static unsigned char cmd[31] = {
    448      1.20  christos 		0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78, 0x80, 0x00,
    449      1.20  christos 		0x00, 0x00, 0x80, 0x00, 0x06, 0x06, 0xf5, 0x04, 0x02, 0x52,
    450      1.20  christos 		0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    451      1.20  christos 		0x00
    452      1.20  christos 	};
    453      1.20  christos 
    454      1.20  christos 	return send_bulkmsg(dev, cmd, sizeof(cmd));
    455      1.20  christos }
    456      1.20  christos 
    457       1.9    martin /*
    458       1.9    martin  * First personality:
    459       1.9    martin  *
    460       1.9    martin  * Claim the entire device if a mode-switch is required.
    461       1.9    martin  */
    462       1.9    martin 
    463       1.4  christos static int
    464       1.9    martin u3ginit_match(device_t parent, cfdata_t match, void *aux)
    465       1.1     joerg {
    466       1.1     joerg 	struct usb_attach_arg *uaa = aux;
    467       1.1     joerg 
    468      1.11     pooka 	/*
    469      1.11     pooka 	 * Huawei changes product when it is configured as a modem.
    470      1.11     pooka 	 */
    471      1.16  christos 	switch (uaa->vendor) {
    472      1.16  christos 	case USB_VENDOR_HUAWEI:
    473      1.11     pooka 		if (uaa->product == USB_PRODUCT_HUAWEI_K3765)
    474      1.11     pooka 			return UMATCH_NONE;
    475      1.11     pooka 
    476      1.15    kardel 		switch (uaa->product) {
    477      1.15    kardel 		case USB_PRODUCT_HUAWEI_E1750INIT:
    478      1.15    kardel 		case USB_PRODUCT_HUAWEI_K3765INIT:
    479      1.11     pooka 			return u3g_huawei_k3765_reinit(uaa->device);
    480      1.15    kardel 			break;
    481      1.15    kardel 		default:
    482      1.11     pooka 			return u3g_huawei_reinit(uaa->device);
    483      1.15    kardel 			break;
    484      1.15    kardel 		}
    485      1.16  christos 		break;
    486       1.1     joerg 
    487      1.16  christos 	case USB_VENDOR_NOVATEL2:
    488      1.14       riz 		switch (uaa->product){
    489      1.14       riz 		case USB_PRODUCT_NOVATEL2_MC950D_DRIVER:
    490      1.14       riz 		case USB_PRODUCT_NOVATEL2_U760_DRIVER:
    491      1.14       riz 			return u3g_novatel_reinit(uaa->device);
    492      1.14       riz 			break;
    493      1.14       riz 		default:
    494      1.14       riz 			break;
    495      1.14       riz 		}
    496      1.16  christos 		break;
    497      1.16  christos 
    498      1.16  christos 	case USB_VENDOR_SIERRA:
    499      1.16  christos 		if (uaa->product == USB_PRODUCT_SIERRA_INSTALLER)
    500      1.16  christos 			return u3g_sierra_reinit(uaa->device);
    501      1.16  christos 		break;
    502      1.16  christos 
    503      1.16  christos 	case USB_VENDOR_QUALCOMMINC:
    504      1.16  christos 		if (uaa->product == USB_PRODUCT_QUALCOMMINC_ZTE_STOR)
    505      1.16  christos 			return u3g_novatel_reinit(uaa->device);
    506      1.16  christos 		break;
    507      1.16  christos 
    508      1.20  christos 	case USB_VENDOR_4GSYSTEMS:
    509  1.20.2.1      yamt 		if (uaa->product == USB_PRODUCT_4GSYSTEMS_XSSTICK_P14_INSTALLER)
    510      1.20  christos 			return u3g_4gsystems_reinit(uaa->device);
    511      1.20  christos 		break;
    512      1.20  christos 
    513      1.16  christos 	default:
    514      1.16  christos 		break;
    515      1.14       riz 	}
    516       1.2     joerg 
    517       1.1     joerg 	return UMATCH_NONE;
    518       1.1     joerg }
    519       1.1     joerg 
    520       1.1     joerg static void
    521       1.9    martin u3ginit_attach(device_t parent, device_t self, void *aux)
    522       1.1     joerg {
    523       1.1     joerg 	struct usb_attach_arg *uaa = aux;
    524       1.1     joerg 
    525       1.1     joerg 	aprint_naive("\n");
    526       1.9    martin 	aprint_normal(": Switching to 3G mode\n");
    527       1.1     joerg 
    528      1.14       riz 	if (uaa->vendor == USB_VENDOR_NOVATEL2) {
    529      1.14       riz 		switch (uaa->product) {
    530      1.14       riz 	    	case USB_PRODUCT_NOVATEL2_MC950D_DRIVER:
    531      1.14       riz 	    	case USB_PRODUCT_NOVATEL2_U760_DRIVER:
    532      1.14       riz 			/* About to disappear... */
    533      1.14       riz 			return;
    534      1.14       riz 			break;
    535      1.14       riz 		default:
    536      1.14       riz 			break;
    537      1.14       riz 		}
    538       1.2     joerg 	}
    539       1.2     joerg 
    540       1.1     joerg 	/* Move the device into the configured state. */
    541       1.9    martin 	(void) usbd_set_config_index(uaa->device, 0, 1);
    542       1.9    martin }
    543       1.9    martin 
    544       1.9    martin static int
    545       1.9    martin u3ginit_detach(device_t self, int flags)
    546       1.9    martin {
    547       1.9    martin 
    548       1.9    martin 	return (0);
    549       1.9    martin }
    550       1.9    martin 
    551       1.9    martin 
    552       1.9    martin /*
    553       1.9    martin  * Second personality:
    554       1.9    martin  *
    555       1.9    martin  * Claim only those interfaces required for 3G modem operation.
    556       1.9    martin  */
    557       1.9    martin 
    558       1.9    martin static int
    559       1.9    martin u3g_match(device_t parent, cfdata_t match, void *aux)
    560       1.9    martin {
    561       1.9    martin 	struct usbif_attach_arg *uaa = aux;
    562       1.9    martin 	usbd_interface_handle iface;
    563       1.9    martin 	usb_interface_descriptor_t *id;
    564       1.9    martin 	usbd_status error;
    565       1.9    martin 
    566       1.9    martin 	if (!usb_lookup(u3g_devs, uaa->vendor, uaa->product))
    567       1.9    martin 		return (UMATCH_NONE);
    568       1.9    martin 
    569       1.9    martin 	error = usbd_device2interface_handle(uaa->device, uaa->ifaceno, &iface);
    570       1.1     joerg 	if (error) {
    571       1.9    martin 		printf("u3g_match: failed to get interface, err=%s\n",
    572       1.9    martin 		    usbd_errstr(error));
    573       1.9    martin 		return (UMATCH_NONE);
    574       1.9    martin 	}
    575       1.9    martin 
    576       1.9    martin 	id = usbd_get_interface_descriptor(iface);
    577       1.9    martin 	if (id == NULL) {
    578       1.9    martin 		printf("u3g_match: failed to get interface descriptor\n");
    579       1.9    martin 		return (UMATCH_NONE);
    580       1.1     joerg 	}
    581       1.1     joerg 
    582       1.9    martin 	/*
    583       1.9    martin 	 * 3G modems generally report vendor-specific class
    584       1.9    martin 	 *
    585       1.9    martin 	 * XXX: this may be too generalised.
    586       1.9    martin 	 */
    587       1.9    martin 	return ((id->bInterfaceClass == UICLASS_VENDOR) ?
    588       1.9    martin 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
    589       1.9    martin }
    590       1.9    martin 
    591       1.9    martin static void
    592       1.9    martin u3g_attach(device_t parent, device_t self, void *aux)
    593       1.9    martin {
    594       1.9    martin 	struct u3g_softc *sc = device_private(self);
    595       1.9    martin 	struct usbif_attach_arg *uaa = aux;
    596       1.9    martin 	usbd_device_handle dev = uaa->device;
    597       1.9    martin 	usbd_interface_handle iface;
    598       1.9    martin 	usb_interface_descriptor_t *id;
    599       1.9    martin 	usb_endpoint_descriptor_t *ed;
    600       1.9    martin 	struct ucom_attach_args uca;
    601       1.9    martin 	usbd_status error;
    602       1.9    martin 	int n, intr_address, intr_size;
    603       1.1     joerg 
    604       1.9    martin 	aprint_naive("\n");
    605       1.9    martin 	aprint_normal("\n");
    606       1.1     joerg 
    607       1.9    martin 	sc->sc_dev = self;
    608       1.9    martin 	sc->sc_dying = false;
    609       1.9    martin 	sc->sc_udev = dev;
    610       1.2     joerg 
    611       1.9    martin 	error = usbd_device2interface_handle(dev, uaa->ifaceno, &iface);
    612       1.9    martin 	if (error) {
    613       1.9    martin 		aprint_error_dev(self, "failed to get interface, err=%s\n",
    614       1.9    martin 		    usbd_errstr(error));
    615       1.4  christos 		return;
    616       1.4  christos 	}
    617       1.4  christos 
    618       1.9    martin 	id = usbd_get_interface_descriptor(iface);
    619       1.1     joerg 
    620       1.9    martin 	uca.info = "3G Modem";
    621       1.9    martin 	uca.ibufsize = U3G_BUFF_SIZE;
    622       1.9    martin 	uca.obufsize = U3G_BUFF_SIZE;
    623       1.9    martin 	uca.ibufsizepad = U3G_BUFF_SIZE;
    624       1.9    martin 	uca.portno = uaa->ifaceno;
    625       1.9    martin 	uca.opkthdrlen = 0;
    626       1.9    martin 	uca.device = dev;
    627       1.9    martin 	uca.iface = iface;
    628       1.9    martin 	uca.methods = &u3g_methods;
    629       1.9    martin 	uca.arg = sc;
    630       1.9    martin 	uca.bulkin = uca.bulkout = -1;
    631       1.9    martin 
    632       1.9    martin 	sc->sc_outpins = 0;
    633       1.9    martin 	sc->sc_msr = UMSR_DSR | UMSR_CTS | UMSR_DCD;
    634       1.9    martin 	sc->sc_ifaceno = uaa->ifaceno;
    635       1.9    martin 	sc->sc_open = false;
    636       1.9    martin 	sc->sc_purging = false;
    637       1.9    martin 
    638       1.9    martin 	intr_address = -1;
    639       1.9    martin 	intr_size = 0;
    640       1.9    martin 
    641       1.9    martin 	for (n = 0; n < id->bNumEndpoints; n++) {
    642       1.9    martin 		ed = usbd_interface2endpoint_descriptor(iface, n);
    643       1.9    martin 		if (ed == NULL) {
    644       1.9    martin 			aprint_error_dev(self, "no endpoint descriptor "
    645       1.9    martin 			    "for %d (interface: %d)\n", n, sc->sc_ifaceno);
    646       1.9    martin 			sc->sc_dying = true;
    647       1.1     joerg 			return;
    648       1.1     joerg 		}
    649       1.1     joerg 
    650       1.9    martin 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
    651       1.9    martin 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
    652       1.9    martin 			intr_address = ed->bEndpointAddress;
    653       1.9    martin 			intr_size = UGETW(ed->wMaxPacketSize);
    654       1.9    martin 		} else
    655       1.9    martin 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
    656       1.9    martin 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
    657       1.9    martin 			uca.bulkin = ed->bEndpointAddress;
    658       1.9    martin 		} else
    659       1.9    martin 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
    660       1.9    martin 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
    661       1.9    martin 			uca.bulkout = ed->bEndpointAddress;
    662       1.1     joerg 		}
    663       1.9    martin 	}
    664       1.9    martin 
    665       1.9    martin 	if (uca.bulkin == -1) {
    666       1.9    martin 		aprint_error_dev(self, "Missing bulk in for interface %d\n",
    667       1.9    martin 		    sc->sc_ifaceno);
    668       1.9    martin 		sc->sc_dying = true;
    669       1.9    martin 		return;
    670       1.9    martin 	}
    671       1.1     joerg 
    672       1.9    martin 	if (uca.bulkout == -1) {
    673       1.9    martin 		aprint_error_dev(self, "Missing bulk out for interface %d\n",
    674       1.9    martin 		    sc->sc_ifaceno);
    675       1.9    martin 		sc->sc_dying = true;
    676       1.9    martin 		return;
    677       1.1     joerg 	}
    678       1.1     joerg 
    679       1.9    martin 	sc->sc_ucom = config_found_sm_loc(self, "ucombus",
    680       1.9    martin 	    NULL, &uca, ucomprint, ucomsubmatch);
    681       1.9    martin 
    682       1.9    martin 	/*
    683       1.9    martin 	 * If the interface has an interrupt pipe, open it immediately so
    684       1.9    martin 	 * that we can track input pin state changes regardless of whether
    685       1.9    martin 	 * the tty(4) device is open or not.
    686       1.9    martin 	 */
    687       1.9    martin 	if (intr_address != -1) {
    688       1.9    martin 		sc->sc_intr_buff = malloc(intr_size, M_USBDEV, M_WAITOK);
    689       1.9    martin 		error = usbd_open_pipe_intr(iface, intr_address,
    690       1.9    martin 		    USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, sc->sc_intr_buff,
    691       1.9    martin 		    intr_size, u3g_intr, 100);
    692       1.1     joerg 		if (error) {
    693       1.9    martin 			aprint_error_dev(self, "cannot open interrupt pipe "
    694       1.9    martin 			    "(addr %d)\n", intr_address);
    695       1.1     joerg 			return;
    696       1.1     joerg 		}
    697       1.9    martin 	} else {
    698       1.9    martin 		sc->sc_intr_pipe = NULL;
    699       1.9    martin 		sc->sc_intr_buff = NULL;
    700       1.1     joerg 	}
    701       1.1     joerg 
    702       1.1     joerg 	if (!pmf_device_register(self, NULL, NULL))
    703       1.1     joerg 		aprint_error_dev(self, "couldn't establish power handler\n");
    704       1.1     joerg }
    705       1.1     joerg 
    706       1.1     joerg static int
    707       1.1     joerg u3g_detach(device_t self, int flags)
    708       1.1     joerg {
    709       1.1     joerg 	struct u3g_softc *sc = device_private(self);
    710       1.9    martin 	int rv;
    711       1.1     joerg 
    712       1.9    martin 	if (sc->sc_dying)
    713       1.2     joerg 		return 0;
    714       1.2     joerg 
    715       1.1     joerg 	pmf_device_deregister(self);
    716       1.1     joerg 
    717       1.9    martin 	if (sc->sc_ucom != NULL) {
    718       1.9    martin 		rv = config_detach(sc->sc_ucom, flags);
    719       1.9    martin 		if (rv != 0) {
    720       1.9    martin 			aprint_verbose_dev(self, "Can't deallocate "
    721       1.9    martin 			    "port (%d)", rv);
    722       1.1     joerg 		}
    723       1.1     joerg 	}
    724       1.1     joerg 
    725       1.1     joerg 	if (sc->sc_intr_pipe != NULL) {
    726       1.9    martin 		(void) usbd_abort_pipe(sc->sc_intr_pipe);
    727       1.9    martin 		(void) usbd_close_pipe(sc->sc_intr_pipe);
    728       1.1     joerg 		sc->sc_intr_pipe = NULL;
    729       1.1     joerg 	}
    730       1.9    martin 	if (sc->sc_intr_buff != NULL) {
    731       1.9    martin 		free(sc->sc_intr_buff, M_USBDEV);
    732       1.9    martin 		sc->sc_intr_buff = NULL;
    733       1.9    martin 	}
    734       1.1     joerg 
    735       1.9    martin 	return (0);
    736       1.1     joerg }
    737       1.1     joerg 
    738       1.1     joerg static void
    739       1.1     joerg u3g_childdet(device_t self, device_t child)
    740       1.1     joerg {
    741       1.1     joerg 	struct u3g_softc *sc = device_private(self);
    742       1.9    martin 
    743       1.9    martin 	if (sc->sc_ucom == child)
    744       1.9    martin 		sc->sc_ucom = NULL;
    745       1.9    martin }
    746       1.9    martin 
    747       1.9    martin static int
    748       1.9    martin u3g_activate(device_t self, enum devact act)
    749       1.9    martin {
    750       1.9    martin 	struct u3g_softc *sc = device_private(self);
    751       1.9    martin 	int rv;
    752       1.9    martin 
    753       1.9    martin 	switch (act) {
    754       1.9    martin 	case DVACT_DEACTIVATE:
    755       1.9    martin 		if (sc->sc_ucom != NULL && config_deactivate(sc->sc_ucom))
    756       1.9    martin 			rv = -1;
    757       1.9    martin 		else
    758       1.9    martin 			rv = 0;
    759       1.9    martin 		break;
    760       1.9    martin 
    761       1.9    martin 	default:
    762       1.9    martin 		rv = 0;
    763       1.9    martin 		break;
    764       1.9    martin 	}
    765       1.9    martin 
    766       1.9    martin 	return (rv);
    767       1.9    martin }
    768       1.9    martin 
    769       1.9    martin static void
    770       1.9    martin u3g_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
    771       1.9    martin {
    772       1.9    martin 	struct u3g_softc *sc = (struct u3g_softc *)priv;
    773       1.9    martin 	u_char *buf;
    774       1.9    martin 
    775       1.9    martin 	if (sc->sc_dying)
    776       1.9    martin 		return;
    777       1.9    martin 
    778       1.9    martin 	if (status != USBD_NORMAL_COMPLETION) {
    779       1.9    martin 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
    780       1.9    martin 			return;
    781       1.9    martin 		usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
    782       1.9    martin 		return;
    783       1.9    martin 	}
    784       1.9    martin 
    785       1.9    martin 	buf = sc->sc_intr_buff;
    786       1.9    martin 	if (buf[0] == 0xa1 && buf[1] == 0x20) {
    787       1.9    martin 		u_char msr;
    788       1.9    martin 
    789       1.9    martin 		msr = sc->sc_msr & ~(UMSR_DCD | UMSR_DSR | UMSR_RI);
    790       1.9    martin 
    791       1.9    martin 		if (buf[8] & U3G_INPIN_DCD)
    792       1.9    martin 			msr |= UMSR_DCD;
    793       1.9    martin 
    794       1.9    martin 		if (buf[8] & U3G_INPIN_DSR)
    795       1.9    martin 			msr |= UMSR_DSR;
    796       1.9    martin 
    797       1.9    martin 		if (buf[8] & U3G_INPIN_RI)
    798       1.9    martin 			msr |= UMSR_RI;
    799       1.9    martin 
    800       1.9    martin 		if (msr != sc->sc_msr) {
    801       1.9    martin 			sc->sc_msr = msr;
    802       1.9    martin 			if (sc->sc_open)
    803       1.9    martin 				ucom_status_change(device_private(sc->sc_ucom));
    804       1.9    martin 		}
    805       1.9    martin 	}
    806       1.9    martin }
    807       1.9    martin 
    808       1.9    martin /*ARGSUSED*/
    809       1.9    martin static void
    810       1.9    martin u3g_get_status(void *arg, int portno, u_char *lsr, u_char *msr)
    811       1.9    martin {
    812       1.9    martin 	struct u3g_softc *sc = arg;
    813       1.9    martin 
    814       1.9    martin 	if (lsr != NULL)
    815       1.9    martin 		*lsr = 0;	/* LSR isn't supported */
    816       1.9    martin 	if (msr != NULL)
    817       1.9    martin 		*msr = sc->sc_msr;
    818       1.9    martin }
    819       1.9    martin 
    820       1.9    martin /*ARGSUSED*/
    821       1.9    martin static void
    822       1.9    martin u3g_set(void *arg, int portno, int reg, int onoff)
    823       1.9    martin {
    824       1.9    martin 	struct u3g_softc *sc = arg;
    825       1.9    martin 	usb_device_request_t req;
    826       1.9    martin 	uint16_t mask, new_state;
    827       1.9    martin 	usbd_status err;
    828       1.9    martin 
    829       1.9    martin 	if (sc->sc_dying)
    830       1.9    martin 		return;
    831       1.9    martin 
    832       1.9    martin 	switch (reg) {
    833       1.9    martin 	case UCOM_SET_DTR:
    834       1.9    martin 		mask = U3G_OUTPIN_DTR;
    835       1.9    martin 		break;
    836       1.9    martin 	case UCOM_SET_RTS:
    837       1.9    martin 		mask = U3G_OUTPIN_RTS;
    838       1.9    martin 		break;
    839       1.9    martin 	default:
    840       1.9    martin 		return;
    841       1.9    martin 	}
    842       1.9    martin 
    843       1.9    martin 	new_state = sc->sc_outpins & ~mask;
    844       1.9    martin 	if (onoff)
    845       1.9    martin 		new_state |= mask;
    846       1.9    martin 
    847       1.9    martin 	if (new_state == sc->sc_outpins)
    848       1.9    martin 		return;
    849       1.9    martin 
    850       1.9    martin 	sc->sc_outpins = new_state;
    851       1.9    martin 
    852       1.9    martin 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
    853       1.9    martin 	req.bRequest = U3G_SET_PIN;
    854       1.9    martin 	USETW(req.wValue, new_state);
    855       1.9    martin 	USETW(req.wIndex, sc->sc_ifaceno);
    856       1.9    martin 	USETW(req.wLength, 0);
    857       1.9    martin 
    858       1.9    martin 	err = usbd_do_request(sc->sc_udev, &req, 0);
    859       1.9    martin 	if (err == USBD_STALLED)
    860       1.9    martin 		usbd_clear_endpoint_stall(sc->sc_udev->default_pipe);
    861       1.9    martin }
    862       1.9    martin 
    863       1.9    martin /*ARGSUSED*/
    864       1.9    martin static int
    865       1.9    martin u3g_open(void *arg, int portno)
    866       1.9    martin {
    867       1.9    martin 	struct u3g_softc *sc = arg;
    868       1.9    martin 	usb_device_request_t req;
    869       1.9    martin 	usb_endpoint_descriptor_t *ed;
    870       1.9    martin 	usb_interface_descriptor_t *id;
    871       1.9    martin 	usbd_interface_handle ih;
    872       1.9    martin 	usbd_status err;
    873       1.1     joerg 	int i;
    874       1.1     joerg 
    875       1.9    martin 	if (sc->sc_dying)
    876       1.9    martin 		return (0);
    877       1.9    martin 
    878       1.9    martin 	err = usbd_device2interface_handle(sc->sc_udev, portno, &ih);
    879       1.9    martin 	if (err)
    880       1.9    martin 		return (EIO);
    881       1.9    martin 
    882       1.9    martin 	id = usbd_get_interface_descriptor(ih);
    883       1.9    martin 
    884       1.9    martin 	for (i = 0; i < id->bNumEndpoints; i++) {
    885       1.9    martin 		ed = usbd_interface2endpoint_descriptor(ih, i);
    886       1.9    martin 		if (ed == NULL)
    887       1.9    martin 			return (EIO);
    888       1.9    martin 
    889       1.9    martin 		if (UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
    890       1.9    martin 			/* Issue ENDPOINT_HALT request */
    891       1.9    martin 			req.bmRequestType = UT_WRITE_ENDPOINT;
    892       1.9    martin 			req.bRequest = UR_CLEAR_FEATURE;
    893       1.9    martin 			USETW(req.wValue, UF_ENDPOINT_HALT);
    894       1.9    martin 			USETW(req.wIndex, ed->bEndpointAddress);
    895       1.9    martin 			USETW(req.wLength, 0);
    896       1.9    martin 			err = usbd_do_request(sc->sc_udev, &req, 0);
    897       1.9    martin 			if (err)
    898       1.9    martin 				return (EIO);
    899       1.9    martin 		}
    900       1.1     joerg 	}
    901       1.9    martin 
    902       1.9    martin 	sc->sc_open = true;
    903       1.9    martin 	sc->sc_purging = true;
    904       1.9    martin 	getmicrotime(&sc->sc_purge_start);
    905       1.9    martin 
    906       1.9    martin 	return (0);
    907       1.1     joerg }
    908       1.1     joerg 
    909       1.9    martin /*ARGSUSED*/
    910       1.9    martin static void
    911       1.9    martin u3g_close(void *arg, int portno)
    912       1.9    martin {
    913       1.9    martin 	struct u3g_softc *sc = arg;
    914       1.9    martin 
    915       1.9    martin 	sc->sc_open = false;
    916       1.9    martin }
    917       1.9    martin 
    918       1.9    martin /*ARGSUSED*/
    919       1.9    martin static void
    920       1.9    martin u3g_read(void *arg, int portno, u_char **cpp, uint32_t *ccp)
    921       1.9    martin {
    922       1.9    martin 	struct u3g_softc *sc = arg;
    923       1.9    martin 	struct timeval curr_tv, diff_tv;
    924       1.9    martin 
    925       1.9    martin 	/*
    926       1.9    martin 	 * If we're not purging input data following first open, do nothing.
    927       1.9    martin 	 */
    928       1.9    martin 	if (sc->sc_purging == false)
    929       1.9    martin 		return;
    930       1.9    martin 
    931       1.9    martin 	/*
    932       1.9    martin 	 * Otherwise check if the purge timeout has expired
    933       1.9    martin 	 */
    934       1.9    martin 	getmicrotime(&curr_tv);
    935       1.9    martin 	timersub(&curr_tv, &sc->sc_purge_start, &diff_tv);
    936       1.9    martin 
    937       1.9    martin 	if (diff_tv.tv_sec >= U3G_PURGE_SECS) {
    938       1.9    martin 		/* Timeout expired. */
    939       1.9    martin 		sc->sc_purging = false;
    940       1.9    martin 	} else {
    941       1.9    martin 		/* Still purging. Adjust the caller's byte count. */
    942       1.9    martin 		*ccp = 0;
    943       1.9    martin 	}
    944       1.9    martin }
    945       1.9    martin 
    946       1.9    martin /*ARGSUSED*/
    947       1.9    martin static void
    948       1.9    martin u3g_write(void *arg, int portno, u_char *to, u_char *from, u_int32_t *count)
    949       1.9    martin {
    950       1.9    martin 	struct u3g_softc *sc = arg;
    951       1.9    martin 
    952       1.9    martin 	/*
    953       1.9    martin 	 * Stop purging as soon as the first data is written to the device.
    954       1.9    martin 	 */
    955       1.9    martin 	sc->sc_purging = false;
    956       1.9    martin 	memcpy(to, from, *count);
    957       1.9    martin }
    958