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;
+}