Home | History | Annotate | Line # | Download | only in usb
u3g.c revision 1.22
      1  1.22    nonaka /*	$NetBSD: u3g.c,v 1.22 2011/11/26 18:36:48 nonaka 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.22    nonaka __KERNEL_RCSID(0, "$NetBSD: u3g.c,v 1.22 2011/11/26 18:36:48 nonaka 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.22    nonaka 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_EU8X0D },
    195   1.1     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_MC950D },
    196  1.13       riz #if 0
    197  1.14       riz 	/* These are matched in u3ginit_match() */
    198  1.13       riz 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_MC950D_DRIVER },
    199  1.14       riz 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U760_DRIVER },
    200  1.13       riz #endif
    201  1.13       riz 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_MERLINU740 },
    202  1.13       riz 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_MERLINV620 },
    203  1.13       riz 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_S720 },
    204   1.3     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U720 },
    205   1.3     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U727 },
    206   1.3     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U740_2 },
    207  1.14       riz 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U760 },
    208   1.3     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U870 },
    209   1.3     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_V740 },
    210   1.3     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_X950D },
    211   1.3     joerg 	{ USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_XU870 },
    212  1.13       riz 	/* OEM: Option N.V. */
    213  1.13       riz 	{ USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_QUADPLUSUMTS },
    214  1.13       riz 	{ USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_HSDPA },
    215  1.13       riz 	{ USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_GTMAXHSUPA },
    216  1.13       riz 	/* OEM: Qualcomm, Inc. */
    217  1.13       riz 	{ USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM },
    218  1.13       riz 	{ USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_ZTE_MF626 },
    219   1.1     joerg 
    220   1.3     joerg 	/* OEM: Sierra Wireless: */
    221   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U },
    222   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E },
    223  1.13       riz 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U },
    224   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880 },
    225   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E },
    226   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U },
    227   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881 },
    228   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E },
    229   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U },
    230  1.13       riz 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580 },
    231  1.13       riz 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595 },
    232  1.13       riz 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875 },
    233  1.13       riz 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597 },
    234   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625 },
    235   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720 },
    236   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2 },
    237   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725 },
    238   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755 },
    239   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2 },
    240   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3 },
    241   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765 },
    242   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2 },
    243   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780 },
    244   1.3     joerg 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781 },
    245  1.13       riz 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725 },
    246  1.12       riz 	{ USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_USB305 },
    247  1.17       scw 
    248  1.17       scw 	/* Toshiba */
    249  1.17       scw 	{ USB_VENDOR_TOSHIBA, USB_PRODUCT_TOSHIBA_HSDPA_MODEM_EU870DT1 },
    250  1.20  christos 
    251  1.20  christos 	/* 4G Systems */
    252  1.20  christos 	{ USB_VENDOR_4GSYSTEMS, USB_PRODUCT_4GSYSTEMS_XSSTICK_P14 },
    253   1.1     joerg };
    254   1.1     joerg 
    255   1.1     joerg static int
    256  1.20  christos send_bulkmsg(usbd_device_handle dev, void *cmd, size_t cmdlen)
    257   1.2     joerg {
    258   1.2     joerg 	usbd_interface_handle iface;
    259   1.2     joerg 	usb_interface_descriptor_t *id;
    260   1.2     joerg 	usb_endpoint_descriptor_t *ed;
    261   1.2     joerg 	usbd_pipe_handle pipe;
    262   1.2     joerg 	usbd_xfer_handle xfer;
    263   1.2     joerg 	int err, i;
    264   1.2     joerg 
    265   1.2     joerg 	/* Move the device into the configured state. */
    266   1.9    martin 	err = usbd_set_config_index(dev, 0, 0);
    267   1.2     joerg 	if (err) {
    268   1.2     joerg 		aprint_error("u3g: failed to set configuration index\n");
    269   1.2     joerg 		return UMATCH_NONE;
    270   1.2     joerg 	}
    271   1.2     joerg 
    272   1.9    martin 	err = usbd_device2interface_handle(dev, 0, &iface);
    273   1.2     joerg 	if (err != 0) {
    274  1.11     pooka 		aprint_error("u3ginit: failed to get interface\n");
    275   1.2     joerg 		return UMATCH_NONE;
    276   1.2     joerg 	}
    277   1.2     joerg 
    278   1.2     joerg 	id = usbd_get_interface_descriptor(iface);
    279   1.2     joerg 	ed = NULL;
    280   1.2     joerg 	for (i = 0 ; i < id->bNumEndpoints ; i++) {
    281   1.2     joerg 		ed = usbd_interface2endpoint_descriptor(iface, i);
    282   1.2     joerg 		if (ed == NULL)
    283   1.2     joerg 			continue;
    284   1.2     joerg 		if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_OUT)
    285   1.2     joerg 			continue;
    286   1.2     joerg 		if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK)
    287   1.2     joerg 			break;
    288   1.2     joerg 	}
    289   1.2     joerg 
    290   1.2     joerg 	if (i == id->bNumEndpoints)
    291   1.2     joerg 		return UMATCH_NONE;
    292   1.2     joerg 
    293  1.11     pooka 	err = usbd_open_pipe(iface, ed->bEndpointAddress,
    294  1.11     pooka 	    USBD_EXCLUSIVE_USE, &pipe);
    295   1.2     joerg 	if (err != 0) {
    296  1.11     pooka 		aprint_error("u3ginit: failed to open bulk transfer pipe %d\n",
    297   1.2     joerg 		    ed->bEndpointAddress);
    298   1.2     joerg 		return UMATCH_NONE;
    299   1.2     joerg 	}
    300   1.2     joerg 
    301   1.9    martin 	xfer = usbd_alloc_xfer(dev);
    302   1.2     joerg 	if (xfer != NULL) {
    303  1.11     pooka 		usbd_setup_xfer(xfer, pipe, NULL, cmd, cmdlen,
    304   1.2     joerg 		    USBD_SYNCHRONOUS, USBD_DEFAULT_TIMEOUT, NULL);
    305   1.2     joerg 
    306   1.2     joerg 		err = usbd_transfer(xfer);
    307  1.11     pooka 
    308  1.11     pooka #if 0 /* XXXpooka: at least my huawei "fails" this always, but still detaches */
    309   1.2     joerg 		if (err)
    310  1.11     pooka 			aprint_error("u3ginit: transfer failed\n");
    311  1.11     pooka #else
    312  1.11     pooka 		err = 0;
    313  1.11     pooka #endif
    314   1.2     joerg 		usbd_free_xfer(xfer);
    315   1.2     joerg 	} else {
    316  1.11     pooka 		aprint_error("u3ginit: failed to allocate xfer\n");
    317   1.2     joerg 		err = USBD_NOMEM;
    318   1.2     joerg 	}
    319   1.2     joerg 
    320   1.2     joerg 	usbd_abort_pipe(pipe);
    321   1.2     joerg 	usbd_close_pipe(pipe);
    322   1.2     joerg 
    323   1.2     joerg 	return (err == USBD_NORMAL_COMPLETION ? UMATCH_HIGHEST : UMATCH_NONE);
    324   1.2     joerg }
    325   1.2     joerg 
    326   1.2     joerg static int
    327  1.11     pooka u3g_novatel_reinit(usbd_device_handle dev)
    328  1.11     pooka {
    329  1.11     pooka 	unsigned char cmd[31];
    330  1.11     pooka 
    331  1.11     pooka 	memset(cmd, 0, sizeof(cmd));
    332  1.11     pooka 	/* Byte 0..3: Command Block Wrapper (CBW) signature */
    333  1.11     pooka 	cmd[0] = 0x55;
    334  1.11     pooka 	cmd[1] = 0x53;
    335  1.11     pooka 	cmd[2] = 0x42;
    336  1.11     pooka 	cmd[3] = 0x43;
    337  1.11     pooka 	/* 4..7: CBW Tag, has to unique, but only a single transfer used. */
    338  1.11     pooka 	cmd[4] = 0x01;
    339  1.11     pooka 	/* 8..11: CBW Transfer Length, no data here */
    340  1.11     pooka 	/* 12: CBW Flag: output, so 0 */
    341  1.11     pooka 	/* 13: CBW Lun: 0 */
    342  1.11     pooka 	/* 14: CBW Length */
    343  1.11     pooka 	cmd[14] = 0x06;
    344  1.11     pooka 	/* Rest is the SCSI payload */
    345  1.11     pooka 	/* 0: SCSI START/STOP opcode */
    346  1.11     pooka 	cmd[15] = 0x1b;
    347  1.11     pooka 	/* 1..3 unused */
    348  1.11     pooka 	/* 4 Load/Eject command */
    349  1.11     pooka 	cmd[19] = 0x02;
    350  1.11     pooka 	/* 5: unused */
    351  1.11     pooka 
    352  1.11     pooka 	return send_bulkmsg(dev, cmd, sizeof(cmd));
    353  1.11     pooka }
    354  1.11     pooka 
    355  1.11     pooka static int
    356   1.1     joerg u3g_huawei_reinit(usbd_device_handle dev)
    357   1.1     joerg {
    358   1.9    martin 	/*
    359   1.9    martin 	 * The Huawei device presents itself as a umass device with Windows
    360   1.1     joerg 	 * drivers on it. After installation of the driver, it reinits into a
    361   1.1     joerg 	 * 3G serial device.
    362   1.1     joerg 	 */
    363   1.1     joerg 	usb_device_request_t req;
    364   1.1     joerg 	usb_config_descriptor_t *cdesc;
    365   1.1     joerg 
    366   1.1     joerg 	/* Get the config descriptor */
    367   1.1     joerg 	cdesc = usbd_get_config_descriptor(dev);
    368   1.9    martin 	if (cdesc == NULL) {
    369   1.9    martin 		usb_device_descriptor_t dd;
    370   1.9    martin 
    371   1.9    martin 		if (usbd_get_device_desc(dev, &dd) != 0)
    372   1.9    martin 			return (UMATCH_NONE);
    373   1.9    martin 
    374   1.9    martin 		if (dd.bNumConfigurations != 1)
    375   1.9    martin 			return (UMATCH_NONE);
    376   1.9    martin 
    377   1.9    martin 		if (usbd_set_config_index(dev, 0, 1) != 0)
    378   1.9    martin 			return (UMATCH_NONE);
    379   1.9    martin 
    380   1.9    martin 		cdesc = usbd_get_config_descriptor(dev);
    381   1.9    martin 
    382   1.9    martin 		if (cdesc == NULL)
    383   1.9    martin 			return (UMATCH_NONE);
    384   1.9    martin 	}
    385   1.1     joerg 
    386   1.9    martin 	/*
    387   1.9    martin 	 * One iface means umass mode, more than 1 (4 usually) means 3G mode.
    388   1.9    martin 	 *
    389   1.9    martin 	 * XXX: We should check the first interface's device class just to be
    390   1.9    martin 	 * sure. If it's a mass storage device, then we can be fairly certain
    391   1.9    martin 	 * it needs a mode-switch.
    392   1.9    martin 	 */
    393   1.1     joerg 	if (cdesc->bNumInterface > 1)
    394   1.9    martin 		return (UMATCH_NONE);
    395   1.1     joerg 
    396   1.1     joerg 	req.bmRequestType = UT_WRITE_DEVICE;
    397   1.1     joerg 	req.bRequest = UR_SET_FEATURE;
    398   1.1     joerg 	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
    399   1.1     joerg 	USETW(req.wIndex, UHF_PORT_SUSPEND);
    400   1.1     joerg 	USETW(req.wLength, 0);
    401   1.1     joerg 
    402   1.1     joerg 	(void) usbd_do_request(dev, &req, 0);
    403   1.1     joerg 
    404  1.11     pooka 	return (UMATCH_HIGHEST); /* Prevent umass from attaching */
    405  1.11     pooka }
    406  1.11     pooka 
    407  1.11     pooka static int
    408  1.11     pooka u3g_huawei_k3765_reinit(usbd_device_handle dev)
    409  1.11     pooka {
    410  1.11     pooka 	unsigned char cmd[31];
    411  1.11     pooka 
    412  1.11     pooka 	/* magic string adapted from some webpage */
    413  1.11     pooka 	memset(cmd, 0, sizeof(cmd));
    414  1.11     pooka 	cmd[0] = 0x55;
    415  1.11     pooka 	cmd[1] = 0x53;
    416  1.11     pooka 	cmd[2] = 0x42;
    417  1.11     pooka 	cmd[3] = 0x43;
    418  1.11     pooka 	cmd[15]= 0x11;
    419  1.11     pooka 	cmd[16]= 0x06;
    420  1.11     pooka 
    421  1.11     pooka 	return send_bulkmsg(dev, cmd, sizeof(cmd));
    422   1.1     joerg }
    423   1.1     joerg 
    424   1.1     joerg static int
    425   1.4  christos u3g_sierra_reinit(usbd_device_handle dev)
    426   1.4  christos {
    427   1.4  christos 	/* Some Sierra devices presents themselves as a umass device with
    428   1.4  christos 	 * Windows drivers on it. After installation of the driver, it
    429   1.4  christos 	 * reinits into a * 3G serial device.
    430   1.4  christos 	 */
    431   1.4  christos 	usb_device_request_t req;
    432   1.4  christos 
    433   1.4  christos 	req.bmRequestType = UT_VENDOR;
    434   1.4  christos 	req.bRequest = UR_SET_INTERFACE;
    435   1.4  christos 	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
    436   1.4  christos 	USETW(req.wIndex, UHF_PORT_CONNECTION);
    437   1.4  christos 	USETW(req.wLength, 0);
    438   1.4  christos 
    439   1.4  christos 	(void) usbd_do_request(dev, &req, 0);
    440   1.4  christos 
    441   1.4  christos 	return (UMATCH_HIGHEST); /* Match to prevent umass from attaching */
    442   1.4  christos }
    443   1.4  christos 
    444  1.20  christos static int
    445  1.20  christos u3g_4gsystems_reinit(usbd_device_handle dev)
    446  1.20  christos {
    447  1.20  christos 	/* magic string adapted from usb_modeswitch database */
    448  1.20  christos 	static unsigned char cmd[31] = {
    449  1.20  christos 		0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78, 0x80, 0x00,
    450  1.20  christos 		0x00, 0x00, 0x80, 0x00, 0x06, 0x06, 0xf5, 0x04, 0x02, 0x52,
    451  1.20  christos 		0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    452  1.20  christos 		0x00
    453  1.20  christos 	};
    454  1.20  christos 
    455  1.20  christos 	return send_bulkmsg(dev, cmd, sizeof(cmd));
    456  1.20  christos }
    457  1.20  christos 
    458   1.9    martin /*
    459   1.9    martin  * First personality:
    460   1.9    martin  *
    461   1.9    martin  * Claim the entire device if a mode-switch is required.
    462   1.9    martin  */
    463   1.9    martin 
    464   1.4  christos static int
    465   1.9    martin u3ginit_match(device_t parent, cfdata_t match, void *aux)
    466   1.1     joerg {
    467   1.1     joerg 	struct usb_attach_arg *uaa = aux;
    468   1.1     joerg 
    469  1.11     pooka 	/*
    470  1.11     pooka 	 * Huawei changes product when it is configured as a modem.
    471  1.11     pooka 	 */
    472  1.16  christos 	switch (uaa->vendor) {
    473  1.16  christos 	case USB_VENDOR_HUAWEI:
    474  1.11     pooka 		if (uaa->product == USB_PRODUCT_HUAWEI_K3765)
    475  1.11     pooka 			return UMATCH_NONE;
    476  1.11     pooka 
    477  1.15    kardel 		switch (uaa->product) {
    478  1.15    kardel 		case USB_PRODUCT_HUAWEI_E1750INIT:
    479  1.15    kardel 		case USB_PRODUCT_HUAWEI_K3765INIT:
    480  1.11     pooka 			return u3g_huawei_k3765_reinit(uaa->device);
    481  1.15    kardel 			break;
    482  1.15    kardel 		default:
    483  1.11     pooka 			return u3g_huawei_reinit(uaa->device);
    484  1.15    kardel 			break;
    485  1.15    kardel 		}
    486  1.16  christos 		break;
    487   1.1     joerg 
    488  1.16  christos 	case USB_VENDOR_NOVATEL2:
    489  1.14       riz 		switch (uaa->product){
    490  1.14       riz 		case USB_PRODUCT_NOVATEL2_MC950D_DRIVER:
    491  1.14       riz 		case USB_PRODUCT_NOVATEL2_U760_DRIVER:
    492  1.14       riz 			return u3g_novatel_reinit(uaa->device);
    493  1.14       riz 			break;
    494  1.14       riz 		default:
    495  1.14       riz 			break;
    496  1.14       riz 		}
    497  1.16  christos 		break;
    498  1.16  christos 
    499  1.16  christos 	case USB_VENDOR_SIERRA:
    500  1.16  christos 		if (uaa->product == USB_PRODUCT_SIERRA_INSTALLER)
    501  1.16  christos 			return u3g_sierra_reinit(uaa->device);
    502  1.16  christos 		break;
    503  1.16  christos 
    504  1.16  christos 	case USB_VENDOR_QUALCOMMINC:
    505  1.16  christos 		if (uaa->product == USB_PRODUCT_QUALCOMMINC_ZTE_STOR)
    506  1.16  christos 			return u3g_novatel_reinit(uaa->device);
    507  1.16  christos 		break;
    508  1.16  christos 
    509  1.20  christos 	case USB_VENDOR_4GSYSTEMS:
    510  1.21  christos 		if (uaa->product == USB_PRODUCT_4GSYSTEMS_XSSTICK_P14_INSTALLER)
    511  1.20  christos 			return u3g_4gsystems_reinit(uaa->device);
    512  1.20  christos 		break;
    513  1.20  christos 
    514  1.16  christos 	default:
    515  1.16  christos 		break;
    516  1.14       riz 	}
    517   1.2     joerg 
    518   1.1     joerg 	return UMATCH_NONE;
    519   1.1     joerg }
    520   1.1     joerg 
    521   1.1     joerg static void
    522   1.9    martin u3ginit_attach(device_t parent, device_t self, void *aux)
    523   1.1     joerg {
    524   1.1     joerg 	struct usb_attach_arg *uaa = aux;
    525   1.1     joerg 
    526   1.1     joerg 	aprint_naive("\n");
    527   1.9    martin 	aprint_normal(": Switching to 3G mode\n");
    528   1.1     joerg 
    529  1.14       riz 	if (uaa->vendor == USB_VENDOR_NOVATEL2) {
    530  1.14       riz 		switch (uaa->product) {
    531  1.14       riz 	    	case USB_PRODUCT_NOVATEL2_MC950D_DRIVER:
    532  1.14       riz 	    	case USB_PRODUCT_NOVATEL2_U760_DRIVER:
    533  1.14       riz 			/* About to disappear... */
    534  1.14       riz 			return;
    535  1.14       riz 			break;
    536  1.14       riz 		default:
    537  1.14       riz 			break;
    538  1.14       riz 		}
    539   1.2     joerg 	}
    540   1.2     joerg 
    541   1.1     joerg 	/* Move the device into the configured state. */
    542   1.9    martin 	(void) usbd_set_config_index(uaa->device, 0, 1);
    543   1.9    martin }
    544   1.9    martin 
    545   1.9    martin static int
    546   1.9    martin u3ginit_detach(device_t self, int flags)
    547   1.9    martin {
    548   1.9    martin 
    549   1.9    martin 	return (0);
    550   1.9    martin }
    551   1.9    martin 
    552   1.9    martin 
    553   1.9    martin /*
    554   1.9    martin  * Second personality:
    555   1.9    martin  *
    556   1.9    martin  * Claim only those interfaces required for 3G modem operation.
    557   1.9    martin  */
    558   1.9    martin 
    559   1.9    martin static int
    560   1.9    martin u3g_match(device_t parent, cfdata_t match, void *aux)
    561   1.9    martin {
    562   1.9    martin 	struct usbif_attach_arg *uaa = aux;
    563   1.9    martin 	usbd_interface_handle iface;
    564   1.9    martin 	usb_interface_descriptor_t *id;
    565   1.9    martin 	usbd_status error;
    566   1.9    martin 
    567   1.9    martin 	if (!usb_lookup(u3g_devs, uaa->vendor, uaa->product))
    568   1.9    martin 		return (UMATCH_NONE);
    569   1.9    martin 
    570   1.9    martin 	error = usbd_device2interface_handle(uaa->device, uaa->ifaceno, &iface);
    571   1.1     joerg 	if (error) {
    572   1.9    martin 		printf("u3g_match: failed to get interface, err=%s\n",
    573   1.9    martin 		    usbd_errstr(error));
    574   1.9    martin 		return (UMATCH_NONE);
    575   1.9    martin 	}
    576   1.9    martin 
    577   1.9    martin 	id = usbd_get_interface_descriptor(iface);
    578   1.9    martin 	if (id == NULL) {
    579   1.9    martin 		printf("u3g_match: failed to get interface descriptor\n");
    580   1.9    martin 		return (UMATCH_NONE);
    581   1.1     joerg 	}
    582   1.1     joerg 
    583   1.9    martin 	/*
    584   1.9    martin 	 * 3G modems generally report vendor-specific class
    585   1.9    martin 	 *
    586   1.9    martin 	 * XXX: this may be too generalised.
    587   1.9    martin 	 */
    588   1.9    martin 	return ((id->bInterfaceClass == UICLASS_VENDOR) ?
    589   1.9    martin 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
    590   1.9    martin }
    591   1.9    martin 
    592   1.9    martin static void
    593   1.9    martin u3g_attach(device_t parent, device_t self, void *aux)
    594   1.9    martin {
    595   1.9    martin 	struct u3g_softc *sc = device_private(self);
    596   1.9    martin 	struct usbif_attach_arg *uaa = aux;
    597   1.9    martin 	usbd_device_handle dev = uaa->device;
    598   1.9    martin 	usbd_interface_handle iface;
    599   1.9    martin 	usb_interface_descriptor_t *id;
    600   1.9    martin 	usb_endpoint_descriptor_t *ed;
    601   1.9    martin 	struct ucom_attach_args uca;
    602   1.9    martin 	usbd_status error;
    603   1.9    martin 	int n, intr_address, intr_size;
    604   1.1     joerg 
    605   1.9    martin 	aprint_naive("\n");
    606   1.9    martin 	aprint_normal("\n");
    607   1.1     joerg 
    608   1.9    martin 	sc->sc_dev = self;
    609   1.9    martin 	sc->sc_dying = false;
    610   1.9    martin 	sc->sc_udev = dev;
    611   1.2     joerg 
    612   1.9    martin 	error = usbd_device2interface_handle(dev, uaa->ifaceno, &iface);
    613   1.9    martin 	if (error) {
    614   1.9    martin 		aprint_error_dev(self, "failed to get interface, err=%s\n",
    615   1.9    martin 		    usbd_errstr(error));
    616   1.4  christos 		return;
    617   1.4  christos 	}
    618   1.4  christos 
    619   1.9    martin 	id = usbd_get_interface_descriptor(iface);
    620   1.1     joerg 
    621   1.9    martin 	uca.info = "3G Modem";
    622   1.9    martin 	uca.ibufsize = U3G_BUFF_SIZE;
    623   1.9    martin 	uca.obufsize = U3G_BUFF_SIZE;
    624   1.9    martin 	uca.ibufsizepad = U3G_BUFF_SIZE;
    625   1.9    martin 	uca.portno = uaa->ifaceno;
    626   1.9    martin 	uca.opkthdrlen = 0;
    627   1.9    martin 	uca.device = dev;
    628   1.9    martin 	uca.iface = iface;
    629   1.9    martin 	uca.methods = &u3g_methods;
    630   1.9    martin 	uca.arg = sc;
    631   1.9    martin 	uca.bulkin = uca.bulkout = -1;
    632   1.9    martin 
    633   1.9    martin 	sc->sc_outpins = 0;
    634   1.9    martin 	sc->sc_msr = UMSR_DSR | UMSR_CTS | UMSR_DCD;
    635   1.9    martin 	sc->sc_ifaceno = uaa->ifaceno;
    636   1.9    martin 	sc->sc_open = false;
    637   1.9    martin 	sc->sc_purging = false;
    638   1.9    martin 
    639   1.9    martin 	intr_address = -1;
    640   1.9    martin 	intr_size = 0;
    641   1.9    martin 
    642   1.9    martin 	for (n = 0; n < id->bNumEndpoints; n++) {
    643   1.9    martin 		ed = usbd_interface2endpoint_descriptor(iface, n);
    644   1.9    martin 		if (ed == NULL) {
    645   1.9    martin 			aprint_error_dev(self, "no endpoint descriptor "
    646   1.9    martin 			    "for %d (interface: %d)\n", n, sc->sc_ifaceno);
    647   1.9    martin 			sc->sc_dying = true;
    648   1.1     joerg 			return;
    649   1.1     joerg 		}
    650   1.1     joerg 
    651   1.9    martin 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
    652   1.9    martin 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
    653   1.9    martin 			intr_address = ed->bEndpointAddress;
    654   1.9    martin 			intr_size = UGETW(ed->wMaxPacketSize);
    655   1.9    martin 		} else
    656   1.9    martin 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
    657   1.9    martin 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
    658   1.9    martin 			uca.bulkin = ed->bEndpointAddress;
    659   1.9    martin 		} else
    660   1.9    martin 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
    661   1.9    martin 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
    662   1.9    martin 			uca.bulkout = ed->bEndpointAddress;
    663   1.1     joerg 		}
    664   1.9    martin 	}
    665   1.9    martin 
    666   1.9    martin 	if (uca.bulkin == -1) {
    667   1.9    martin 		aprint_error_dev(self, "Missing bulk in for interface %d\n",
    668   1.9    martin 		    sc->sc_ifaceno);
    669   1.9    martin 		sc->sc_dying = true;
    670   1.9    martin 		return;
    671   1.9    martin 	}
    672   1.1     joerg 
    673   1.9    martin 	if (uca.bulkout == -1) {
    674   1.9    martin 		aprint_error_dev(self, "Missing bulk out for interface %d\n",
    675   1.9    martin 		    sc->sc_ifaceno);
    676   1.9    martin 		sc->sc_dying = true;
    677   1.9    martin 		return;
    678   1.1     joerg 	}
    679   1.1     joerg 
    680   1.9    martin 	sc->sc_ucom = config_found_sm_loc(self, "ucombus",
    681   1.9    martin 	    NULL, &uca, ucomprint, ucomsubmatch);
    682   1.9    martin 
    683   1.9    martin 	/*
    684   1.9    martin 	 * If the interface has an interrupt pipe, open it immediately so
    685   1.9    martin 	 * that we can track input pin state changes regardless of whether
    686   1.9    martin 	 * the tty(4) device is open or not.
    687   1.9    martin 	 */
    688   1.9    martin 	if (intr_address != -1) {
    689   1.9    martin 		sc->sc_intr_buff = malloc(intr_size, M_USBDEV, M_WAITOK);
    690   1.9    martin 		error = usbd_open_pipe_intr(iface, intr_address,
    691   1.9    martin 		    USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, sc->sc_intr_buff,
    692   1.9    martin 		    intr_size, u3g_intr, 100);
    693   1.1     joerg 		if (error) {
    694   1.9    martin 			aprint_error_dev(self, "cannot open interrupt pipe "
    695   1.9    martin 			    "(addr %d)\n", intr_address);
    696   1.1     joerg 			return;
    697   1.1     joerg 		}
    698   1.9    martin 	} else {
    699   1.9    martin 		sc->sc_intr_pipe = NULL;
    700   1.9    martin 		sc->sc_intr_buff = NULL;
    701   1.1     joerg 	}
    702   1.1     joerg 
    703   1.1     joerg 	if (!pmf_device_register(self, NULL, NULL))
    704   1.1     joerg 		aprint_error_dev(self, "couldn't establish power handler\n");
    705   1.1     joerg }
    706   1.1     joerg 
    707   1.1     joerg static int
    708   1.1     joerg u3g_detach(device_t self, int flags)
    709   1.1     joerg {
    710   1.1     joerg 	struct u3g_softc *sc = device_private(self);
    711   1.9    martin 	int rv;
    712   1.1     joerg 
    713   1.9    martin 	if (sc->sc_dying)
    714   1.2     joerg 		return 0;
    715   1.2     joerg 
    716   1.1     joerg 	pmf_device_deregister(self);
    717   1.1     joerg 
    718   1.9    martin 	if (sc->sc_ucom != NULL) {
    719   1.9    martin 		rv = config_detach(sc->sc_ucom, flags);
    720   1.9    martin 		if (rv != 0) {
    721   1.9    martin 			aprint_verbose_dev(self, "Can't deallocate "
    722   1.9    martin 			    "port (%d)", rv);
    723   1.1     joerg 		}
    724   1.1     joerg 	}
    725   1.1     joerg 
    726   1.1     joerg 	if (sc->sc_intr_pipe != NULL) {
    727   1.9    martin 		(void) usbd_abort_pipe(sc->sc_intr_pipe);
    728   1.9    martin 		(void) usbd_close_pipe(sc->sc_intr_pipe);
    729   1.1     joerg 		sc->sc_intr_pipe = NULL;
    730   1.1     joerg 	}
    731   1.9    martin 	if (sc->sc_intr_buff != NULL) {
    732   1.9    martin 		free(sc->sc_intr_buff, M_USBDEV);
    733   1.9    martin 		sc->sc_intr_buff = NULL;
    734   1.9    martin 	}
    735   1.1     joerg 
    736   1.9    martin 	return (0);
    737   1.1     joerg }
    738   1.1     joerg 
    739   1.1     joerg static void
    740   1.1     joerg u3g_childdet(device_t self, device_t child)
    741   1.1     joerg {
    742   1.1     joerg 	struct u3g_softc *sc = device_private(self);
    743   1.9    martin 
    744   1.9    martin 	if (sc->sc_ucom == child)
    745   1.9    martin 		sc->sc_ucom = NULL;
    746   1.9    martin }
    747   1.9    martin 
    748   1.9    martin static int
    749   1.9    martin u3g_activate(device_t self, enum devact act)
    750   1.9    martin {
    751   1.9    martin 	struct u3g_softc *sc = device_private(self);
    752   1.9    martin 	int rv;
    753   1.9    martin 
    754   1.9    martin 	switch (act) {
    755   1.9    martin 	case DVACT_DEACTIVATE:
    756   1.9    martin 		if (sc->sc_ucom != NULL && config_deactivate(sc->sc_ucom))
    757   1.9    martin 			rv = -1;
    758   1.9    martin 		else
    759   1.9    martin 			rv = 0;
    760   1.9    martin 		break;
    761   1.9    martin 
    762   1.9    martin 	default:
    763   1.9    martin 		rv = 0;
    764   1.9    martin 		break;
    765   1.9    martin 	}
    766   1.9    martin 
    767   1.9    martin 	return (rv);
    768   1.9    martin }
    769   1.9    martin 
    770   1.9    martin static void
    771   1.9    martin u3g_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
    772   1.9    martin {
    773   1.9    martin 	struct u3g_softc *sc = (struct u3g_softc *)priv;
    774   1.9    martin 	u_char *buf;
    775   1.9    martin 
    776   1.9    martin 	if (sc->sc_dying)
    777   1.9    martin 		return;
    778   1.9    martin 
    779   1.9    martin 	if (status != USBD_NORMAL_COMPLETION) {
    780   1.9    martin 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
    781   1.9    martin 			return;
    782   1.9    martin 		usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
    783   1.9    martin 		return;
    784   1.9    martin 	}
    785   1.9    martin 
    786   1.9    martin 	buf = sc->sc_intr_buff;
    787   1.9    martin 	if (buf[0] == 0xa1 && buf[1] == 0x20) {
    788   1.9    martin 		u_char msr;
    789   1.9    martin 
    790   1.9    martin 		msr = sc->sc_msr & ~(UMSR_DCD | UMSR_DSR | UMSR_RI);
    791   1.9    martin 
    792   1.9    martin 		if (buf[8] & U3G_INPIN_DCD)
    793   1.9    martin 			msr |= UMSR_DCD;
    794   1.9    martin 
    795   1.9    martin 		if (buf[8] & U3G_INPIN_DSR)
    796   1.9    martin 			msr |= UMSR_DSR;
    797   1.9    martin 
    798   1.9    martin 		if (buf[8] & U3G_INPIN_RI)
    799   1.9    martin 			msr |= UMSR_RI;
    800   1.9    martin 
    801   1.9    martin 		if (msr != sc->sc_msr) {
    802   1.9    martin 			sc->sc_msr = msr;
    803   1.9    martin 			if (sc->sc_open)
    804   1.9    martin 				ucom_status_change(device_private(sc->sc_ucom));
    805   1.9    martin 		}
    806   1.9    martin 	}
    807   1.9    martin }
    808   1.9    martin 
    809   1.9    martin /*ARGSUSED*/
    810   1.9    martin static void
    811   1.9    martin u3g_get_status(void *arg, int portno, u_char *lsr, u_char *msr)
    812   1.9    martin {
    813   1.9    martin 	struct u3g_softc *sc = arg;
    814   1.9    martin 
    815   1.9    martin 	if (lsr != NULL)
    816   1.9    martin 		*lsr = 0;	/* LSR isn't supported */
    817   1.9    martin 	if (msr != NULL)
    818   1.9    martin 		*msr = sc->sc_msr;
    819   1.9    martin }
    820   1.9    martin 
    821   1.9    martin /*ARGSUSED*/
    822   1.9    martin static void
    823   1.9    martin u3g_set(void *arg, int portno, int reg, int onoff)
    824   1.9    martin {
    825   1.9    martin 	struct u3g_softc *sc = arg;
    826   1.9    martin 	usb_device_request_t req;
    827   1.9    martin 	uint16_t mask, new_state;
    828   1.9    martin 	usbd_status err;
    829   1.9    martin 
    830   1.9    martin 	if (sc->sc_dying)
    831   1.9    martin 		return;
    832   1.9    martin 
    833   1.9    martin 	switch (reg) {
    834   1.9    martin 	case UCOM_SET_DTR:
    835   1.9    martin 		mask = U3G_OUTPIN_DTR;
    836   1.9    martin 		break;
    837   1.9    martin 	case UCOM_SET_RTS:
    838   1.9    martin 		mask = U3G_OUTPIN_RTS;
    839   1.9    martin 		break;
    840   1.9    martin 	default:
    841   1.9    martin 		return;
    842   1.9    martin 	}
    843   1.9    martin 
    844   1.9    martin 	new_state = sc->sc_outpins & ~mask;
    845   1.9    martin 	if (onoff)
    846   1.9    martin 		new_state |= mask;
    847   1.9    martin 
    848   1.9    martin 	if (new_state == sc->sc_outpins)
    849   1.9    martin 		return;
    850   1.9    martin 
    851   1.9    martin 	sc->sc_outpins = new_state;
    852   1.9    martin 
    853   1.9    martin 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
    854   1.9    martin 	req.bRequest = U3G_SET_PIN;
    855   1.9    martin 	USETW(req.wValue, new_state);
    856   1.9    martin 	USETW(req.wIndex, sc->sc_ifaceno);
    857   1.9    martin 	USETW(req.wLength, 0);
    858   1.9    martin 
    859   1.9    martin 	err = usbd_do_request(sc->sc_udev, &req, 0);
    860   1.9    martin 	if (err == USBD_STALLED)
    861   1.9    martin 		usbd_clear_endpoint_stall(sc->sc_udev->default_pipe);
    862   1.9    martin }
    863   1.9    martin 
    864   1.9    martin /*ARGSUSED*/
    865   1.9    martin static int
    866   1.9    martin u3g_open(void *arg, int portno)
    867   1.9    martin {
    868   1.9    martin 	struct u3g_softc *sc = arg;
    869   1.9    martin 	usb_device_request_t req;
    870   1.9    martin 	usb_endpoint_descriptor_t *ed;
    871   1.9    martin 	usb_interface_descriptor_t *id;
    872   1.9    martin 	usbd_interface_handle ih;
    873   1.9    martin 	usbd_status err;
    874   1.1     joerg 	int i;
    875   1.1     joerg 
    876   1.9    martin 	if (sc->sc_dying)
    877   1.9    martin 		return (0);
    878   1.9    martin 
    879   1.9    martin 	err = usbd_device2interface_handle(sc->sc_udev, portno, &ih);
    880   1.9    martin 	if (err)
    881   1.9    martin 		return (EIO);
    882   1.9    martin 
    883   1.9    martin 	id = usbd_get_interface_descriptor(ih);
    884   1.9    martin 
    885   1.9    martin 	for (i = 0; i < id->bNumEndpoints; i++) {
    886   1.9    martin 		ed = usbd_interface2endpoint_descriptor(ih, i);
    887   1.9    martin 		if (ed == NULL)
    888   1.9    martin 			return (EIO);
    889   1.9    martin 
    890   1.9    martin 		if (UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
    891   1.9    martin 			/* Issue ENDPOINT_HALT request */
    892   1.9    martin 			req.bmRequestType = UT_WRITE_ENDPOINT;
    893   1.9    martin 			req.bRequest = UR_CLEAR_FEATURE;
    894   1.9    martin 			USETW(req.wValue, UF_ENDPOINT_HALT);
    895   1.9    martin 			USETW(req.wIndex, ed->bEndpointAddress);
    896   1.9    martin 			USETW(req.wLength, 0);
    897   1.9    martin 			err = usbd_do_request(sc->sc_udev, &req, 0);
    898   1.9    martin 			if (err)
    899   1.9    martin 				return (EIO);
    900   1.9    martin 		}
    901   1.1     joerg 	}
    902   1.9    martin 
    903   1.9    martin 	sc->sc_open = true;
    904   1.9    martin 	sc->sc_purging = true;
    905   1.9    martin 	getmicrotime(&sc->sc_purge_start);
    906   1.9    martin 
    907   1.9    martin 	return (0);
    908   1.1     joerg }
    909   1.1     joerg 
    910   1.9    martin /*ARGSUSED*/
    911   1.9    martin static void
    912   1.9    martin u3g_close(void *arg, int portno)
    913   1.9    martin {
    914   1.9    martin 	struct u3g_softc *sc = arg;
    915   1.9    martin 
    916   1.9    martin 	sc->sc_open = false;
    917   1.9    martin }
    918   1.9    martin 
    919   1.9    martin /*ARGSUSED*/
    920   1.9    martin static void
    921   1.9    martin u3g_read(void *arg, int portno, u_char **cpp, uint32_t *ccp)
    922   1.9    martin {
    923   1.9    martin 	struct u3g_softc *sc = arg;
    924   1.9    martin 	struct timeval curr_tv, diff_tv;
    925   1.9    martin 
    926   1.9    martin 	/*
    927   1.9    martin 	 * If we're not purging input data following first open, do nothing.
    928   1.9    martin 	 */
    929   1.9    martin 	if (sc->sc_purging == false)
    930   1.9    martin 		return;
    931   1.9    martin 
    932   1.9    martin 	/*
    933   1.9    martin 	 * Otherwise check if the purge timeout has expired
    934   1.9    martin 	 */
    935   1.9    martin 	getmicrotime(&curr_tv);
    936   1.9    martin 	timersub(&curr_tv, &sc->sc_purge_start, &diff_tv);
    937   1.9    martin 
    938   1.9    martin 	if (diff_tv.tv_sec >= U3G_PURGE_SECS) {
    939   1.9    martin 		/* Timeout expired. */
    940   1.9    martin 		sc->sc_purging = false;
    941   1.9    martin 	} else {
    942   1.9    martin 		/* Still purging. Adjust the caller's byte count. */
    943   1.9    martin 		*ccp = 0;
    944   1.9    martin 	}
    945   1.9    martin }
    946   1.9    martin 
    947   1.9    martin /*ARGSUSED*/
    948   1.9    martin static void
    949   1.9    martin u3g_write(void *arg, int portno, u_char *to, u_char *from, u_int32_t *count)
    950   1.9    martin {
    951   1.9    martin 	struct u3g_softc *sc = arg;
    952   1.9    martin 
    953   1.9    martin 	/*
    954   1.9    martin 	 * Stop purging as soon as the first data is written to the device.
    955   1.9    martin 	 */
    956   1.9    martin 	sc->sc_purging = false;
    957   1.9    martin 	memcpy(to, from, *count);
    958   1.9    martin }
    959