Module Name: src Committed By: christos Date: Wed May 24 20:23:58 UTC 2017
Modified Files: src/sys/dev/usb: files.usb u3g.c Added Files: src/sys/dev/usb: umodeswitch.c Log Message: split the mode switch part of the u3g driver into a separate file so that others can use it. To generate a diff of this commit: cvs rdiff -u -r1.144 -r1.145 src/sys/dev/usb/files.usb cvs rdiff -u -r1.33 -r1.34 src/sys/dev/usb/u3g.c cvs rdiff -u -r0 -r1.1 src/sys/dev/usb/umodeswitch.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/usb/files.usb diff -u src/sys/dev/usb/files.usb:1.144 src/sys/dev/usb/files.usb:1.145 --- src/sys/dev/usb/files.usb:1.144 Fri May 19 20:56:32 2017 +++ src/sys/dev/usb/files.usb Wed May 24 16:23:58 2017 @@ -1,4 +1,4 @@ -# $NetBSD: files.usb,v 1.144 2017/05/20 00:56:32 pgoyette Exp $ +# $NetBSD: files.usb,v 1.145 2017/05/24 20:23:58 christos Exp $ # # Config file and device description for machine-independent USB code. # Included by ports that need it. Ports that use it must provide @@ -272,6 +272,11 @@ file dev/usb/emdtv_ir.c emdtv # Misc # +# Mode switch usb driver +device umodeswitch +attach umodeswitch at usbdevif +file dev/usb/umodeswitch.c umodeswitch + # Diamond Multimedia Rio 500 device urio attach urio at usbdevif @@ -298,11 +303,9 @@ attach ugensa at usbdevif file dev/usb/ugensa.c ugensa # 3G Modem -device u3g: ucombus +device u3g: ucombus, umodeswitch attach u3g at usbifif -device u3ginit -attach u3ginit at usbdevif -file dev/usb/u3g.c u3g | u3ginit +file dev/usb/u3g.c u3g # YAP phone firmware loader device uyap: ezload Index: src/sys/dev/usb/u3g.c diff -u src/sys/dev/usb/u3g.c:1.33 src/sys/dev/usb/u3g.c:1.34 --- src/sys/dev/usb/u3g.c:1.33 Thu May 4 10:21:01 2017 +++ src/sys/dev/usb/u3g.c Wed May 24 16:23:58 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: u3g.c,v 1.33 2017/05/04 14:21:01 hauke Exp $ */ +/* $NetBSD: u3g.c,v 1.34 2017/05/24 20:23:58 christos Exp $ */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. @@ -50,7 +50,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: u3g.c,v 1.33 2017/05/04 14:21:01 hauke Exp $"); +__KERNEL_RCSID(0, "$NetBSD: u3g.c,v 1.34 2017/05/24 20:23:58 christos Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -137,19 +137,13 @@ struct u3g_softc { * interface attribute so that a match will claim the entire USB device * for itself. This is used for when a device needs to be mode-switched * and ensures any other interfaces present cannot be claimed by other - * drivers while the mode-switch is in progress. + * drivers while the mode-switch is in progress. This is implemented by + * the umodeswitch driver. * * The second personality uses the 'usbifif' interface attribute so that * it can claim the 3G modem interfaces for itself, leaving others (such * as the mass storage interfaces on some devices) for other drivers. */ -static int u3ginit_match(device_t, cfdata_t, void *); -static void u3ginit_attach(device_t, device_t, void *); -static int u3ginit_detach(device_t, int); - -CFATTACH_DECL2_NEW(u3ginit, 0, u3ginit_match, - u3ginit_attach, u3ginit_detach, NULL, NULL, NULL); - static int u3g_match(device_t, cfdata_t, void *); static void u3g_attach(device_t, device_t, void *); @@ -266,411 +260,6 @@ static const struct usb_devno u3g_devs[] { USB_VENDOR_4GSYSTEMS, USB_PRODUCT_4GSYSTEMS_XSSTICK_W14 }, }; -static int -send_bulkmsg(struct usbd_device *dev, void *cmd, size_t cmdlen) -{ - struct usbd_interface *iface; - usb_interface_descriptor_t *id; - usb_endpoint_descriptor_t *ed; - struct usbd_pipe *pipe; - struct usbd_xfer *xfer; - int err, i; - - /* Move the device into the configured state. */ - err = usbd_set_config_index(dev, 0, 0); - if (err) { - aprint_error("u3ginit: failed to set config index\n"); - return UMATCH_NONE; - } - - err = usbd_device2interface_handle(dev, 0, &iface); - if (err != 0) { - aprint_error("u3ginit: failed to get interface\n"); - return UMATCH_NONE; - } - - id = usbd_get_interface_descriptor(iface); - ed = NULL; - for (i = 0 ; i < id->bNumEndpoints ; i++) { - ed = usbd_interface2endpoint_descriptor(iface, i); - if (ed == NULL) - continue; - if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_OUT) - continue; - if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK) - break; - } - - if (i == id->bNumEndpoints) - return UMATCH_NONE; - - err = usbd_open_pipe(iface, ed->bEndpointAddress, - USBD_EXCLUSIVE_USE, &pipe); - if (err != 0) { - aprint_error("u3ginit: failed to open bulk transfer pipe %d\n", - ed->bEndpointAddress); - return UMATCH_NONE; - } - - int error = usbd_create_xfer(pipe, cmdlen, 0, 0, &xfer); - if (!error) { - - usbd_setup_xfer(xfer, NULL, cmd, cmdlen, - USBD_SYNCHRONOUS, USBD_DEFAULT_TIMEOUT, NULL); - - err = usbd_transfer(xfer); - -#if 0 /* XXXpooka: at least my huawei "fails" this always, but still detaches */ - if (err) - aprint_error("u3ginit: transfer failed\n"); -#else - err = 0; -#endif - usbd_destroy_xfer(xfer); - } else { - aprint_error("u3ginit: failed to allocate xfer\n"); - err = USBD_NOMEM; - } - - usbd_abort_pipe(pipe); - usbd_close_pipe(pipe); - - return err == USBD_NORMAL_COMPLETION ? UMATCH_HIGHEST : UMATCH_NONE; -} - -/* Byte 0..3: Command Block Wrapper (CBW) signature */ -static void -set_cbw(unsigned char *cmd) -{ - cmd[0] = 0x55; - cmd[1] = 0x53; - cmd[2] = 0x42; - cmd[3] = 0x43; -} - -static int -u3g_bulk_scsi_eject(struct usbd_device *dev) -{ - unsigned char cmd[31]; - - memset(cmd, 0, sizeof(cmd)); - /* Byte 0..3: Command Block Wrapper (CBW) signature */ - set_cbw(cmd); - /* 4..7: CBW Tag, has to unique, but only a single transfer used. */ - cmd[4] = 0x01; - /* 8..11: CBW Transfer Length, no data here */ - /* 12: CBW Flag: output, so 0 */ - /* 13: CBW Lun: 0 */ - /* 14: CBW Length */ - cmd[14] = 0x06; - - /* Rest is the SCSI payload */ - - /* 0: SCSI START/STOP opcode */ - cmd[15] = 0x1b; - /* 1..3 unused */ - /* 4 Load/Eject command */ - cmd[19] = 0x02; - /* 5: unused */ - - return send_bulkmsg(dev, cmd, sizeof(cmd)); -} - -static int -u3g_bulk_ata_eject(struct usbd_device *dev) -{ - unsigned char cmd[31]; - - memset(cmd, 0, sizeof(cmd)); - /* Byte 0..3: Command Block Wrapper (CBW) signature */ - set_cbw(cmd); - /* 4..7: CBW Tag, has to unique, but only a single transfer used. */ - cmd[4] = 0x01; - /* 8..11: CBW Transfer Length, no data here */ - /* 12: CBW Flag: output, so 0 */ - /* 13: CBW Lun: 0 */ - /* 14: CBW Length */ - cmd[14] = 0x06; - - /* Rest is the SCSI payload */ - - /* 0: ATA pass-through */ - cmd[15] = 0x85; - /* 1..3 unused */ - /* 4 XXX What is this command? */ - cmd[19] = 0x24; - /* 5: unused */ - - return send_bulkmsg(dev, cmd, sizeof(cmd)); -} - -static int -u3g_huawei_reinit(struct usbd_device *dev) -{ - /* - * The Huawei device presents itself as a umass device with Windows - * drivers on it. After installation of the driver, it reinits into a - * 3G serial device. - */ - usb_device_request_t req; - usb_config_descriptor_t *cdesc; - - /* Get the config descriptor */ - cdesc = usbd_get_config_descriptor(dev); - if (cdesc == NULL) { - usb_device_descriptor_t dd; - - if (usbd_get_device_desc(dev, &dd) != 0) - return UMATCH_NONE; - - if (dd.bNumConfigurations != 1) - return UMATCH_NONE; - - if (usbd_set_config_index(dev, 0, 1) != 0) - return UMATCH_NONE; - - cdesc = usbd_get_config_descriptor(dev); - - if (cdesc == NULL) - return UMATCH_NONE; - } - - /* - * One iface means umass mode, more than 1 (4 usually) means 3G mode. - * - * XXX: We should check the first interface's device class just to be - * sure. If it's a mass storage device, then we can be fairly certain - * it needs a mode-switch. - */ - if (cdesc->bNumInterface > 1) - return UMATCH_NONE; - - req.bmRequestType = UT_WRITE_DEVICE; - req.bRequest = UR_SET_FEATURE; - USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); - USETW(req.wIndex, UHF_PORT_SUSPEND); - USETW(req.wLength, 0); - - (void) usbd_do_request(dev, &req, 0); - - return UMATCH_HIGHEST; /* Prevent umass from attaching */ -} - -static int -u3g_huawei_k3765_reinit(struct usbd_device *dev) -{ - unsigned char cmd[31]; - - /* magic string adapted from some webpage */ - memset(cmd, 0, sizeof(cmd)); - /* Byte 0..3: Command Block Wrapper (CBW) signature */ - set_cbw(cmd); - - cmd[15]= 0x11; - cmd[16]= 0x06; - - return send_bulkmsg(dev, cmd, sizeof(cmd)); -} -static int -u3g_huawei_e171_reinit(struct usbd_device *dev) -{ - unsigned char cmd[31]; - - /* magic string adapted from some webpage */ - memset(cmd, 0, sizeof(cmd)); - /* Byte 0..3: Command Block Wrapper (CBW) signature */ - set_cbw(cmd); - - cmd[15]= 0x11; - cmd[16]= 0x06; - cmd[17]= 0x20; - cmd[20]= 0x01; - - return send_bulkmsg(dev, cmd, sizeof(cmd)); -} - -static int -u3g_huawei_e353_reinit(struct usbd_device *dev) -{ - unsigned char cmd[31]; - - /* magic string adapted from some webpage */ - memset(cmd, 0, sizeof(cmd)); - /* Byte 0..3: Command Block Wrapper (CBW) signature */ - set_cbw(cmd); - - cmd[4] = 0x7f; - cmd[9] = 0x02; - cmd[12] = 0x80; - cmd[14] = 0x0a; - cmd[15] = 0x11; - cmd[16] = 0x06; - cmd[17] = 0x20; - cmd[23] = 0x01; - - return send_bulkmsg(dev, cmd, sizeof(cmd)); -} - -static int -u3g_sierra_reinit(struct usbd_device *dev) -{ - /* Some Sierra devices presents themselves as a umass device with - * Windows drivers on it. After installation of the driver, it - * reinits into a * 3G serial device. - */ - usb_device_request_t req; - - req.bmRequestType = UT_VENDOR; - req.bRequest = UR_SET_INTERFACE; - USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); - USETW(req.wIndex, UHF_PORT_CONNECTION); - USETW(req.wLength, 0); - - (void) usbd_do_request(dev, &req, 0); - - return UMATCH_HIGHEST; /* Match to prevent umass from attaching */ -} - -static int -u3g_4gsystems_reinit(struct usbd_device *dev) -{ - /* magic string adapted from usb_modeswitch database */ - unsigned char cmd[31]; - - memset(cmd, 0, sizeof(cmd)); - /* Byte 0..3: Command Block Wrapper (CBW) signature */ - set_cbw(cmd); - - cmd[4] = 0x12; - cmd[5] = 0x34; - cmd[6] = 0x56; - cmd[7] = 0x78; - cmd[8] = 0x80; - cmd[12] = 0x80; - cmd[14] = 0x06; - cmd[15] = 0x06; - cmd[16] = 0xf5; - cmd[17] = 0x04; - cmd[18] = 0x02; - cmd[19] = 0x52; - cmd[20] = 0x70; - - return send_bulkmsg(dev, cmd, sizeof(cmd)); -} - -/* - * First personality: - * - * Claim the entire device if a mode-switch is required. - */ - -static int -u3ginit_match(device_t parent, cfdata_t match, void *aux) -{ - struct usb_attach_arg *uaa = aux; - - /* - * Huawei changes product when it is configured as a modem. - */ - switch (uaa->uaa_vendor) { - case USB_VENDOR_HUAWEI: - if (uaa->uaa_product == USB_PRODUCT_HUAWEI_K3765) - return UMATCH_NONE; - - switch (uaa->uaa_product) { - case USB_PRODUCT_HUAWEI_E1750INIT: - case USB_PRODUCT_HUAWEI_K3765INIT: - return u3g_huawei_k3765_reinit(uaa->uaa_device); - break; - case USB_PRODUCT_HUAWEI_E171INIT: - return u3g_huawei_e171_reinit(uaa->uaa_device); - break; - case USB_PRODUCT_HUAWEI_E353INIT: - return u3g_huawei_e353_reinit(uaa->uaa_device); - break; - default: - return u3g_huawei_reinit(uaa->uaa_device); - break; - } - break; - - case USB_VENDOR_NOVATEL2: - switch (uaa->uaa_product){ - case USB_PRODUCT_NOVATEL2_MC950D_DRIVER: - case USB_PRODUCT_NOVATEL2_U760_DRIVER: - return u3g_bulk_scsi_eject(uaa->uaa_device); - break; - default: - break; - } - break; - - case USB_VENDOR_SIERRA: - if (uaa->uaa_product == USB_PRODUCT_SIERRA_INSTALLER) - return u3g_sierra_reinit(uaa->uaa_device); - break; - - case USB_VENDOR_QUALCOMM: - if (uaa->uaa_product == USB_PRODUCT_QUALCOMM_NTT_DOCOMO_L02C_STORAGE) - return u3g_bulk_scsi_eject(uaa->uaa_device); - break; - - case USB_VENDOR_ZTE: - switch (uaa->uaa_product){ - case USB_PRODUCT_ZTE_INSTALLER: - case USB_PRODUCT_ZTE_MF820D_INSTALLER: - (void)u3g_bulk_ata_eject(uaa->uaa_device); - (void)u3g_bulk_scsi_eject(uaa->uaa_device); - return UMATCH_HIGHEST; - default: - break; - } - break; - - case USB_VENDOR_4GSYSTEMS: - if (uaa->uaa_product == USB_PRODUCT_4GSYSTEMS_XSSTICK_P14_INSTALLER) - return u3g_4gsystems_reinit(uaa->uaa_device); - break; - - default: - break; - } - - return UMATCH_NONE; -} - -static void -u3ginit_attach(device_t parent, device_t self, void *aux) -{ - struct usb_attach_arg *uaa = aux; - - aprint_naive("\n"); - aprint_normal(": Switching to 3G mode\n"); - - if (uaa->uaa_vendor == USB_VENDOR_NOVATEL2) { - switch (uaa->uaa_product) { - case USB_PRODUCT_NOVATEL2_MC950D_DRIVER: - case USB_PRODUCT_NOVATEL2_U760_DRIVER: - /* About to disappear... */ - return; - break; - default: - break; - } - } - - /* Move the device into the configured state. */ - (void) usbd_set_config_index(uaa->uaa_device, 0, 1); -} - -static int -u3ginit_detach(device_t self, int flags) -{ - - return 0; -} - - /* * Second personality: * Added files: Index: src/sys/dev/usb/umodeswitch.c diff -u /dev/null src/sys/dev/usb/umodeswitch.c:1.1 --- /dev/null Wed May 24 16:23:58 2017 +++ src/sys/dev/usb/umodeswitch.c Wed May 24 16:23:58 2017 @@ -0,0 +1,467 @@ +/* $NetBSD: umodeswitch.c,v 1.1 2017/05/24 20:23:58 christos Exp $ */ + +/*- + * Copyright (c) 2009, 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: umodeswitch.c,v 1.1 2017/05/24 20:23:58 christos Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/kmem.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/tty.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usbdi_util.h> + +#include "usbdevs.h" + +/* + * This device driver handles devices that have two personalities. + * The first uses the 'usbdevif' + * interface attribute so that a match will claim the entire USB device + * for itself. This is used for when a device needs to be mode-switched + * and ensures any other interfaces present cannot be claimed by other + * drivers while the mode-switch is in progress. + */ +static int umodeswitch_match(device_t, cfdata_t, void *); +static void umodeswitch_attach(device_t, device_t, void *); +static int umodeswitch_detach(device_t, int); + +CFATTACH_DECL2_NEW(umodeswitch, 0, umodeswitch_match, + umodeswitch_attach, umodeswitch_detach, NULL, NULL, NULL); + +static int +send_bulkmsg(struct usbd_device *dev, void *cmd, size_t cmdlen) +{ + struct usbd_interface *iface; + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed; + struct usbd_pipe *pipe; + struct usbd_xfer *xfer; + int err, i; + + /* Move the device into the configured state. */ + err = usbd_set_config_index(dev, 0, 0); + if (err) { + aprint_error("%s: failed to set config index\n", __func__); + return UMATCH_NONE; + } + + err = usbd_device2interface_handle(dev, 0, &iface); + if (err != 0) { + aprint_error("%s: failed to get interface\n", __func__); + return UMATCH_NONE; + } + + id = usbd_get_interface_descriptor(iface); + ed = NULL; + for (i = 0 ; i < id->bNumEndpoints ; i++) { + ed = usbd_interface2endpoint_descriptor(iface, i); + if (ed == NULL) + continue; + if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_OUT) + continue; + if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK) + break; + } + + if (i == id->bNumEndpoints) + return UMATCH_NONE; + + err = usbd_open_pipe(iface, ed->bEndpointAddress, + USBD_EXCLUSIVE_USE, &pipe); + if (err != 0) { + aprint_error("%s: failed to open bulk transfer pipe %d\n", + __func__, ed->bEndpointAddress); + return UMATCH_NONE; + } + + int error = usbd_create_xfer(pipe, cmdlen, 0, 0, &xfer); + if (!error) { + + usbd_setup_xfer(xfer, NULL, cmd, cmdlen, + USBD_SYNCHRONOUS, USBD_DEFAULT_TIMEOUT, NULL); + + err = usbd_transfer(xfer); + +#if 0 /* XXXpooka: at least my huawei "fails" this always, but still detaches */ + if (err) + aprint_error("%s: transfer failed\n", __func__); +#else + err = 0; +#endif + usbd_destroy_xfer(xfer); + } else { + aprint_error("%s: failed to allocate xfer\n", __func__); + err = USBD_NOMEM; + } + + usbd_abort_pipe(pipe); + usbd_close_pipe(pipe); + + return err == USBD_NORMAL_COMPLETION ? UMATCH_HIGHEST : UMATCH_NONE; +} + +/* Byte 0..3: Command Block Wrapper (CBW) signature */ +static void +set_cbw(unsigned char *cmd) +{ + cmd[0] = 0x55; + cmd[1] = 0x53; + cmd[2] = 0x42; + cmd[3] = 0x43; +} + +static int +u3g_bulk_scsi_eject(struct usbd_device *dev) +{ + unsigned char cmd[31]; + + memset(cmd, 0, sizeof(cmd)); + /* Byte 0..3: Command Block Wrapper (CBW) signature */ + set_cbw(cmd); + /* 4..7: CBW Tag, has to unique, but only a single transfer used. */ + cmd[4] = 0x01; + /* 8..11: CBW Transfer Length, no data here */ + /* 12: CBW Flag: output, so 0 */ + /* 13: CBW Lun: 0 */ + /* 14: CBW Length */ + cmd[14] = 0x06; + + /* Rest is the SCSI payload */ + + /* 0: SCSI START/STOP opcode */ + cmd[15] = 0x1b; + /* 1..3 unused */ + /* 4 Load/Eject command */ + cmd[19] = 0x02; + /* 5: unused */ + + return send_bulkmsg(dev, cmd, sizeof(cmd)); +} + +static int +u3g_bulk_ata_eject(struct usbd_device *dev) +{ + unsigned char cmd[31]; + + memset(cmd, 0, sizeof(cmd)); + /* Byte 0..3: Command Block Wrapper (CBW) signature */ + set_cbw(cmd); + /* 4..7: CBW Tag, has to unique, but only a single transfer used. */ + cmd[4] = 0x01; + /* 8..11: CBW Transfer Length, no data here */ + /* 12: CBW Flag: output, so 0 */ + /* 13: CBW Lun: 0 */ + /* 14: CBW Length */ + cmd[14] = 0x06; + + /* Rest is the SCSI payload */ + + /* 0: ATA pass-through */ + cmd[15] = 0x85; + /* 1..3 unused */ + /* 4 XXX What is this command? */ + cmd[19] = 0x24; + /* 5: unused */ + + return send_bulkmsg(dev, cmd, sizeof(cmd)); +} + +static int +u3g_huawei_reinit(struct usbd_device *dev) +{ + /* + * The Huawei device presents itself as a umass device with Windows + * drivers on it. After installation of the driver, it reinits into a + * 3G serial device. + */ + usb_device_request_t req; + usb_config_descriptor_t *cdesc; + + /* Get the config descriptor */ + cdesc = usbd_get_config_descriptor(dev); + if (cdesc == NULL) { + usb_device_descriptor_t dd; + + if (usbd_get_device_desc(dev, &dd) != 0) + return UMATCH_NONE; + + if (dd.bNumConfigurations != 1) + return UMATCH_NONE; + + if (usbd_set_config_index(dev, 0, 1) != 0) + return UMATCH_NONE; + + cdesc = usbd_get_config_descriptor(dev); + + if (cdesc == NULL) + return UMATCH_NONE; + } + + /* + * One iface means umass mode, more than 1 (4 usually) means 3G mode. + * + * XXX: We should check the first interface's device class just to be + * sure. If it's a mass storage device, then we can be fairly certain + * it needs a mode-switch. + */ + if (cdesc->bNumInterface > 1) + return UMATCH_NONE; + + req.bmRequestType = UT_WRITE_DEVICE; + req.bRequest = UR_SET_FEATURE; + USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); + USETW(req.wIndex, UHF_PORT_SUSPEND); + USETW(req.wLength, 0); + + (void) usbd_do_request(dev, &req, 0); + + return UMATCH_HIGHEST; /* Prevent umass from attaching */ +} + +static int +u3g_huawei_k3765_reinit(struct usbd_device *dev) +{ + unsigned char cmd[31]; + + /* magic string adapted from some webpage */ + memset(cmd, 0, sizeof(cmd)); + /* Byte 0..3: Command Block Wrapper (CBW) signature */ + set_cbw(cmd); + + cmd[15]= 0x11; + cmd[16]= 0x06; + + return send_bulkmsg(dev, cmd, sizeof(cmd)); +} +static int +u3g_huawei_e171_reinit(struct usbd_device *dev) +{ + unsigned char cmd[31]; + + /* magic string adapted from some webpage */ + memset(cmd, 0, sizeof(cmd)); + /* Byte 0..3: Command Block Wrapper (CBW) signature */ + set_cbw(cmd); + + cmd[15]= 0x11; + cmd[16]= 0x06; + cmd[17]= 0x20; + cmd[20]= 0x01; + + return send_bulkmsg(dev, cmd, sizeof(cmd)); +} + +static int +u3g_huawei_e353_reinit(struct usbd_device *dev) +{ + unsigned char cmd[31]; + + /* magic string adapted from some webpage */ + memset(cmd, 0, sizeof(cmd)); + /* Byte 0..3: Command Block Wrapper (CBW) signature */ + set_cbw(cmd); + + cmd[4] = 0x7f; + cmd[9] = 0x02; + cmd[12] = 0x80; + cmd[14] = 0x0a; + cmd[15] = 0x11; + cmd[16] = 0x06; + cmd[17] = 0x20; + cmd[23] = 0x01; + + return send_bulkmsg(dev, cmd, sizeof(cmd)); +} + +static int +u3g_sierra_reinit(struct usbd_device *dev) +{ + /* Some Sierra devices presents themselves as a umass device with + * Windows drivers on it. After installation of the driver, it + * reinits into a * 3G serial device. + */ + usb_device_request_t req; + + req.bmRequestType = UT_VENDOR; + req.bRequest = UR_SET_INTERFACE; + USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); + USETW(req.wIndex, UHF_PORT_CONNECTION); + USETW(req.wLength, 0); + + (void) usbd_do_request(dev, &req, 0); + + return UMATCH_HIGHEST; /* Match to prevent umass from attaching */ +} + +static int +u3g_4gsystems_reinit(struct usbd_device *dev) +{ + /* magic string adapted from usb_modeswitch database */ + unsigned char cmd[31]; + + memset(cmd, 0, sizeof(cmd)); + /* Byte 0..3: Command Block Wrapper (CBW) signature */ + set_cbw(cmd); + + cmd[4] = 0x12; + cmd[5] = 0x34; + cmd[6] = 0x56; + cmd[7] = 0x78; + cmd[8] = 0x80; + cmd[12] = 0x80; + cmd[14] = 0x06; + cmd[15] = 0x06; + cmd[16] = 0xf5; + cmd[17] = 0x04; + cmd[18] = 0x02; + cmd[19] = 0x52; + cmd[20] = 0x70; + + return send_bulkmsg(dev, cmd, sizeof(cmd)); +} + +/* + * First personality: + * + * Claim the entire device if a mode-switch is required. + */ + +static int +umodeswitch_match(device_t parent, cfdata_t match, void *aux) +{ + struct usb_attach_arg *uaa = aux; + + /* + * Huawei changes product when it is configured as a modem. + */ + switch (uaa->uaa_vendor) { + case USB_VENDOR_HUAWEI: + if (uaa->uaa_product == USB_PRODUCT_HUAWEI_K3765) + return UMATCH_NONE; + + switch (uaa->uaa_product) { + case USB_PRODUCT_HUAWEI_E1750INIT: + case USB_PRODUCT_HUAWEI_K3765INIT: + return u3g_huawei_k3765_reinit(uaa->uaa_device); + break; + case USB_PRODUCT_HUAWEI_E171INIT: + return u3g_huawei_e171_reinit(uaa->uaa_device); + break; + case USB_PRODUCT_HUAWEI_E353INIT: + return u3g_huawei_e353_reinit(uaa->uaa_device); + break; + default: + return u3g_huawei_reinit(uaa->uaa_device); + break; + } + break; + + case USB_VENDOR_NOVATEL2: + switch (uaa->uaa_product){ + case USB_PRODUCT_NOVATEL2_MC950D_DRIVER: + case USB_PRODUCT_NOVATEL2_U760_DRIVER: + return u3g_bulk_scsi_eject(uaa->uaa_device); + break; + default: + break; + } + break; + + case USB_VENDOR_SIERRA: + if (uaa->uaa_product == USB_PRODUCT_SIERRA_INSTALLER) + return u3g_sierra_reinit(uaa->uaa_device); + break; + + case USB_VENDOR_QUALCOMM: + if (uaa->uaa_product == USB_PRODUCT_QUALCOMM_NTT_DOCOMO_L02C_STORAGE) + return u3g_bulk_scsi_eject(uaa->uaa_device); + break; + + case USB_VENDOR_ZTE: + switch (uaa->uaa_product){ + case USB_PRODUCT_ZTE_INSTALLER: + case USB_PRODUCT_ZTE_MF820D_INSTALLER: + (void)u3g_bulk_ata_eject(uaa->uaa_device); + (void)u3g_bulk_scsi_eject(uaa->uaa_device); + return UMATCH_HIGHEST; + default: + break; + } + break; + + case USB_VENDOR_4GSYSTEMS: + if (uaa->uaa_product == USB_PRODUCT_4GSYSTEMS_XSSTICK_P14_INSTALLER) + return u3g_4gsystems_reinit(uaa->uaa_device); + break; + + default: + break; + } + + return UMATCH_NONE; +} + +static void +umodeswitch_attach(device_t parent, device_t self, void *aux) +{ + struct usb_attach_arg *uaa = aux; + + aprint_naive("\n"); + aprint_normal(": Switching off umass mode\n"); + + if (uaa->uaa_vendor == USB_VENDOR_NOVATEL2) { + switch (uaa->uaa_product) { + case USB_PRODUCT_NOVATEL2_MC950D_DRIVER: + case USB_PRODUCT_NOVATEL2_U760_DRIVER: + /* About to disappear... */ + return; + break; + default: + break; + } + } + + /* Move the device into the configured state. */ + (void) usbd_set_config_index(uaa->uaa_device, 0, 1); +} + +static int +umodeswitch_detach(device_t self, int flags) +{ + + return 0; +}