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