xref: /netbsd-src/sys/dev/usb/umodeswitch.c (revision a4d9265c1d04b59dfa83e7036b74c4ae96e5712c)
1*a4d9265cSmanu /*	$NetBSD: umodeswitch.c,v 1.6 2023/08/04 13:25:17 manu Exp $	*/
288e04a21Schristos 
388e04a21Schristos /*-
488e04a21Schristos  * Copyright (c) 2009, 2017 The NetBSD Foundation, Inc.
588e04a21Schristos  * All rights reserved.
688e04a21Schristos  *
788e04a21Schristos  * This code is derived from software contributed to The NetBSD Foundation.
888e04a21Schristos  *
988e04a21Schristos  * Redistribution and use in source and binary forms, with or without
1088e04a21Schristos  * modification, are permitted provided that the following conditions
1188e04a21Schristos  * are met:
1288e04a21Schristos  * 1. Redistributions of source code must retain the above copyright
1388e04a21Schristos  *    notice, this list of conditions and the following disclaimer.
1488e04a21Schristos  * 2. Redistributions in binary form must reproduce the above copyright
1588e04a21Schristos  *    notice, this list of conditions and the following disclaimer in the
1688e04a21Schristos  *    documentation and/or other materials provided with the distribution.
1788e04a21Schristos  *
1888e04a21Schristos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1988e04a21Schristos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2088e04a21Schristos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2188e04a21Schristos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2288e04a21Schristos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2388e04a21Schristos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2488e04a21Schristos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2588e04a21Schristos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2688e04a21Schristos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2788e04a21Schristos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2888e04a21Schristos  * POSSIBILITY OF SUCH DAMAGE.
2988e04a21Schristos  */
3088e04a21Schristos 
3188e04a21Schristos 
3288e04a21Schristos #include <sys/cdefs.h>
33*a4d9265cSmanu __KERNEL_RCSID(0, "$NetBSD: umodeswitch.c,v 1.6 2023/08/04 13:25:17 manu Exp $");
3488e04a21Schristos 
3588e04a21Schristos #include <sys/param.h>
3688e04a21Schristos #include <sys/systm.h>
3788e04a21Schristos #include <sys/kernel.h>
3888e04a21Schristos #include <sys/kmem.h>
3988e04a21Schristos #include <sys/bus.h>
4088e04a21Schristos #include <sys/conf.h>
4188e04a21Schristos #include <sys/tty.h>
4288e04a21Schristos 
4388e04a21Schristos #include <dev/usb/usb.h>
4488e04a21Schristos #include <dev/usb/usbdi.h>
4588e04a21Schristos #include <dev/usb/usbdivar.h>
4688e04a21Schristos #include <dev/usb/usbdi_util.h>
4788e04a21Schristos 
4888e04a21Schristos #include "usbdevs.h"
4988e04a21Schristos 
5088e04a21Schristos /*
5188e04a21Schristos  * This device driver handles devices that have two personalities.
5288e04a21Schristos  * The first uses the 'usbdevif'
5388e04a21Schristos  * interface attribute so that a match will claim the entire USB device
5488e04a21Schristos  * for itself. This is used for when a device needs to be mode-switched
5588e04a21Schristos  * and ensures any other interfaces present cannot be claimed by other
5688e04a21Schristos  * drivers while the mode-switch is in progress.
5788e04a21Schristos  */
5888e04a21Schristos static int umodeswitch_match(device_t, cfdata_t, void *);
5988e04a21Schristos static void umodeswitch_attach(device_t, device_t, void *);
6088e04a21Schristos static int umodeswitch_detach(device_t, int);
6188e04a21Schristos 
6288e04a21Schristos CFATTACH_DECL2_NEW(umodeswitch, 0, umodeswitch_match,
6388e04a21Schristos     umodeswitch_attach, umodeswitch_detach, NULL, NULL, NULL);
6488e04a21Schristos 
6588e04a21Schristos static int
send_bulkmsg(struct usbd_device * dev,void * cmd,size_t cmdlen)6688e04a21Schristos send_bulkmsg(struct usbd_device *dev, void *cmd, size_t cmdlen)
6788e04a21Schristos {
6888e04a21Schristos 	struct usbd_interface *iface;
6988e04a21Schristos 	usb_interface_descriptor_t *id;
7088e04a21Schristos 	usb_endpoint_descriptor_t *ed;
7188e04a21Schristos 	struct usbd_pipe *pipe;
7288e04a21Schristos 	struct usbd_xfer *xfer;
7388e04a21Schristos 	int err, i;
7488e04a21Schristos 
7588e04a21Schristos 	/* Move the device into the configured state. */
7688e04a21Schristos 	err = usbd_set_config_index(dev, 0, 0);
7788e04a21Schristos 	if (err) {
7888e04a21Schristos 		aprint_error("%s: failed to set config index\n", __func__);
7988e04a21Schristos 		return UMATCH_NONE;
8088e04a21Schristos 	}
8188e04a21Schristos 
8288e04a21Schristos 	err = usbd_device2interface_handle(dev, 0, &iface);
8388e04a21Schristos 	if (err != 0) {
8488e04a21Schristos 		aprint_error("%s: failed to get interface\n", __func__);
8588e04a21Schristos 		return UMATCH_NONE;
8688e04a21Schristos 	}
8788e04a21Schristos 
8888e04a21Schristos 	id = usbd_get_interface_descriptor(iface);
8988e04a21Schristos 	ed = NULL;
9088e04a21Schristos 	for (i = 0 ; i < id->bNumEndpoints ; i++) {
9188e04a21Schristos 		ed = usbd_interface2endpoint_descriptor(iface, i);
9288e04a21Schristos 		if (ed == NULL)
9388e04a21Schristos 			continue;
9488e04a21Schristos 		if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_OUT)
9588e04a21Schristos 			continue;
9688e04a21Schristos 		if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK)
9788e04a21Schristos 			break;
9888e04a21Schristos 	}
9988e04a21Schristos 
10088e04a21Schristos 	if (i == id->bNumEndpoints)
10188e04a21Schristos 		return UMATCH_NONE;
10288e04a21Schristos 
10388e04a21Schristos 	err = usbd_open_pipe(iface, ed->bEndpointAddress,
10488e04a21Schristos 	    USBD_EXCLUSIVE_USE, &pipe);
10588e04a21Schristos 	if (err != 0) {
10688e04a21Schristos 		aprint_error("%s: failed to open bulk transfer pipe %d\n",
10788e04a21Schristos 		    __func__, ed->bEndpointAddress);
10888e04a21Schristos 		return UMATCH_NONE;
10988e04a21Schristos 	}
11088e04a21Schristos 
11188e04a21Schristos 	int error = usbd_create_xfer(pipe, cmdlen, 0, 0, &xfer);
11288e04a21Schristos 	if (!error) {
11388e04a21Schristos 
11488e04a21Schristos 		usbd_setup_xfer(xfer, NULL, cmd, cmdlen,
11588e04a21Schristos 		    USBD_SYNCHRONOUS, USBD_DEFAULT_TIMEOUT, NULL);
11688e04a21Schristos 
11788e04a21Schristos 		err = usbd_transfer(xfer);
11888e04a21Schristos 
11988e04a21Schristos #if 0 /* XXXpooka: at least my huawei "fails" this always, but still detaches */
12088e04a21Schristos 		if (err)
12188e04a21Schristos 			aprint_error("%s: transfer failed\n", __func__);
12288e04a21Schristos #else
12388e04a21Schristos 		err = 0;
12488e04a21Schristos #endif
12588e04a21Schristos 		usbd_destroy_xfer(xfer);
12688e04a21Schristos 	} else {
12788e04a21Schristos 		aprint_error("%s: failed to allocate xfer\n", __func__);
12888e04a21Schristos 		err = USBD_NOMEM;
12988e04a21Schristos 	}
13088e04a21Schristos 
13188e04a21Schristos 	usbd_abort_pipe(pipe);
13288e04a21Schristos 	usbd_close_pipe(pipe);
13388e04a21Schristos 
13488e04a21Schristos 	return err == USBD_NORMAL_COMPLETION ? UMATCH_HIGHEST : UMATCH_NONE;
13588e04a21Schristos }
13688e04a21Schristos 
13788e04a21Schristos /* Byte 0..3: Command Block Wrapper (CBW) signature */
13888e04a21Schristos static void
set_cbw(unsigned char * cmd)13988e04a21Schristos set_cbw(unsigned char *cmd)
14088e04a21Schristos {
14188e04a21Schristos 	cmd[0] = 0x55;
14288e04a21Schristos 	cmd[1] = 0x53;
14388e04a21Schristos 	cmd[2] = 0x42;
14488e04a21Schristos 	cmd[3] = 0x43;
14588e04a21Schristos }
14688e04a21Schristos 
14788e04a21Schristos static int
u3g_bulk_scsi_eject(struct usbd_device * dev)14888e04a21Schristos u3g_bulk_scsi_eject(struct usbd_device *dev)
14988e04a21Schristos {
15088e04a21Schristos 	unsigned char cmd[31];
15188e04a21Schristos 
15288e04a21Schristos 	memset(cmd, 0, sizeof(cmd));
15388e04a21Schristos 	/* Byte 0..3: Command Block Wrapper (CBW) signature */
15488e04a21Schristos 	set_cbw(cmd);
15588e04a21Schristos 	/* 4..7: CBW Tag, has to unique, but only a single transfer used. */
15688e04a21Schristos 	cmd[4] = 0x01;
15788e04a21Schristos 	/* 8..11: CBW Transfer Length, no data here */
15888e04a21Schristos 	/* 12: CBW Flag: output, so 0 */
15988e04a21Schristos 	/* 13: CBW Lun: 0 */
16088e04a21Schristos 	/* 14: CBW Length */
16188e04a21Schristos 	cmd[14] = 0x06;
16288e04a21Schristos 
16388e04a21Schristos 	/* Rest is the SCSI payload */
16488e04a21Schristos 
16588e04a21Schristos 	/* 0: SCSI START/STOP opcode */
16688e04a21Schristos 	cmd[15] = 0x1b;
16788e04a21Schristos 	/* 1..3 unused */
16888e04a21Schristos 	/* 4 Load/Eject command */
16988e04a21Schristos 	cmd[19] = 0x02;
17088e04a21Schristos 	/* 5: unused */
17188e04a21Schristos 
17288e04a21Schristos 	return send_bulkmsg(dev, cmd, sizeof(cmd));
17388e04a21Schristos }
17488e04a21Schristos 
17588e04a21Schristos static int
u3g_bulk_ata_eject(struct usbd_device * dev)17688e04a21Schristos u3g_bulk_ata_eject(struct usbd_device *dev)
17788e04a21Schristos {
17888e04a21Schristos 	unsigned char cmd[31];
17988e04a21Schristos 
18088e04a21Schristos 	memset(cmd, 0, sizeof(cmd));
18188e04a21Schristos 	/* Byte 0..3: Command Block Wrapper (CBW) signature */
18288e04a21Schristos 	set_cbw(cmd);
18388e04a21Schristos 	/* 4..7: CBW Tag, has to unique, but only a single transfer used. */
18488e04a21Schristos 	cmd[4] = 0x01;
18588e04a21Schristos 	/* 8..11: CBW Transfer Length, no data here */
18688e04a21Schristos 	/* 12: CBW Flag: output, so 0 */
18788e04a21Schristos 	/* 13: CBW Lun: 0 */
18888e04a21Schristos 	/* 14: CBW Length */
18988e04a21Schristos 	cmd[14] = 0x06;
19088e04a21Schristos 
19188e04a21Schristos 	/* Rest is the SCSI payload */
19288e04a21Schristos 
19388e04a21Schristos 	/* 0: ATA pass-through */
19488e04a21Schristos 	cmd[15] = 0x85;
19588e04a21Schristos 	/* 1..3 unused */
19688e04a21Schristos 	/* 4 XXX What is this command? */
19788e04a21Schristos 	cmd[19] = 0x24;
19888e04a21Schristos 	/* 5: unused */
19988e04a21Schristos 
20088e04a21Schristos 	return send_bulkmsg(dev, cmd, sizeof(cmd));
20188e04a21Schristos }
20288e04a21Schristos 
20388e04a21Schristos static int
u3g_huawei_reinit(struct usbd_device * dev)20488e04a21Schristos u3g_huawei_reinit(struct usbd_device *dev)
20588e04a21Schristos {
20688e04a21Schristos 	/*
20788e04a21Schristos 	 * The Huawei device presents itself as a umass device with Windows
20888e04a21Schristos 	 * drivers on it. After installation of the driver, it reinits into a
20988e04a21Schristos 	 * 3G serial device.
21088e04a21Schristos 	 */
21188e04a21Schristos 	usb_device_request_t req;
21288e04a21Schristos 	usb_config_descriptor_t *cdesc;
21388e04a21Schristos 
21488e04a21Schristos 	/* Get the config descriptor */
21588e04a21Schristos 	cdesc = usbd_get_config_descriptor(dev);
21688e04a21Schristos 	if (cdesc == NULL) {
21788e04a21Schristos 		usb_device_descriptor_t dd;
21888e04a21Schristos 
21988e04a21Schristos 		if (usbd_get_device_desc(dev, &dd) != 0)
22088e04a21Schristos 			return UMATCH_NONE;
22188e04a21Schristos 
22288e04a21Schristos 		if (dd.bNumConfigurations != 1)
22388e04a21Schristos 			return UMATCH_NONE;
22488e04a21Schristos 
22588e04a21Schristos 		if (usbd_set_config_index(dev, 0, 1) != 0)
22688e04a21Schristos 			return UMATCH_NONE;
22788e04a21Schristos 
22888e04a21Schristos 		cdesc = usbd_get_config_descriptor(dev);
22988e04a21Schristos 
23088e04a21Schristos 		if (cdesc == NULL)
23188e04a21Schristos 			return UMATCH_NONE;
23288e04a21Schristos 	}
23388e04a21Schristos 
23488e04a21Schristos 	/*
23588e04a21Schristos 	 * One iface means umass mode, more than 1 (4 usually) means 3G mode.
23688e04a21Schristos 	 *
23788e04a21Schristos 	 * XXX: We should check the first interface's device class just to be
23888e04a21Schristos 	 * sure. If it's a mass storage device, then we can be fairly certain
23988e04a21Schristos 	 * it needs a mode-switch.
24088e04a21Schristos 	 */
24188e04a21Schristos 	if (cdesc->bNumInterface > 1)
24288e04a21Schristos 		return UMATCH_NONE;
24388e04a21Schristos 
24488e04a21Schristos 	req.bmRequestType = UT_WRITE_DEVICE;
24588e04a21Schristos 	req.bRequest = UR_SET_FEATURE;
24688e04a21Schristos 	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
24788e04a21Schristos 	USETW(req.wIndex, UHF_PORT_SUSPEND);
24888e04a21Schristos 	USETW(req.wLength, 0);
24988e04a21Schristos 
25088e04a21Schristos 	(void) usbd_do_request(dev, &req, 0);
25188e04a21Schristos 
25288e04a21Schristos 	return UMATCH_HIGHEST; /* Prevent umass from attaching */
25388e04a21Schristos }
25488e04a21Schristos 
25588e04a21Schristos static int
u3g_huawei_k3765_reinit(struct usbd_device * dev)25688e04a21Schristos u3g_huawei_k3765_reinit(struct usbd_device *dev)
25788e04a21Schristos {
25888e04a21Schristos 	unsigned char cmd[31];
25988e04a21Schristos 
26088e04a21Schristos 	/* magic string adapted from some webpage */
26188e04a21Schristos 	memset(cmd, 0, sizeof(cmd));
26288e04a21Schristos 	/* Byte 0..3: Command Block Wrapper (CBW) signature */
26388e04a21Schristos 	set_cbw(cmd);
26488e04a21Schristos 
26588e04a21Schristos 	cmd[15]= 0x11;
26688e04a21Schristos 	cmd[16]= 0x06;
26788e04a21Schristos 
26888e04a21Schristos 	return send_bulkmsg(dev, cmd, sizeof(cmd));
26988e04a21Schristos }
27088e04a21Schristos static int
u3g_huawei_e171_reinit(struct usbd_device * dev)27188e04a21Schristos u3g_huawei_e171_reinit(struct usbd_device *dev)
27288e04a21Schristos {
27388e04a21Schristos 	unsigned char cmd[31];
27488e04a21Schristos 
27588e04a21Schristos 	/* magic string adapted from some webpage */
27688e04a21Schristos 	memset(cmd, 0, sizeof(cmd));
27788e04a21Schristos 	/* Byte 0..3: Command Block Wrapper (CBW) signature */
27888e04a21Schristos 	set_cbw(cmd);
27988e04a21Schristos 
28088e04a21Schristos 	cmd[15]= 0x11;
28188e04a21Schristos 	cmd[16]= 0x06;
28288e04a21Schristos 	cmd[17]= 0x20;
28388e04a21Schristos 	cmd[20]= 0x01;
28488e04a21Schristos 
28588e04a21Schristos 	return send_bulkmsg(dev, cmd, sizeof(cmd));
28688e04a21Schristos }
28788e04a21Schristos 
28888e04a21Schristos static int
u3g_huawei_e353_reinit(struct usbd_device * dev)28988e04a21Schristos u3g_huawei_e353_reinit(struct usbd_device *dev)
29088e04a21Schristos {
29188e04a21Schristos 	unsigned char cmd[31];
29288e04a21Schristos 
29388e04a21Schristos 	/* magic string adapted from some webpage */
29488e04a21Schristos 	memset(cmd, 0, sizeof(cmd));
29588e04a21Schristos 	/* Byte 0..3: Command Block Wrapper (CBW) signature */
29688e04a21Schristos 	set_cbw(cmd);
29788e04a21Schristos 
29888e04a21Schristos 	cmd[4] = 0x7f;
29988e04a21Schristos 	cmd[9] = 0x02;
30088e04a21Schristos 	cmd[12] = 0x80;
30188e04a21Schristos 	cmd[14] = 0x0a;
30288e04a21Schristos 	cmd[15] = 0x11;
30388e04a21Schristos 	cmd[16] = 0x06;
30488e04a21Schristos 	cmd[17] = 0x20;
30588e04a21Schristos 	cmd[23] = 0x01;
30688e04a21Schristos 
30788e04a21Schristos 	return send_bulkmsg(dev, cmd, sizeof(cmd));
30888e04a21Schristos }
30988e04a21Schristos 
31088e04a21Schristos static int
u3g_sierra_reinit(struct usbd_device * dev)31188e04a21Schristos u3g_sierra_reinit(struct usbd_device *dev)
31288e04a21Schristos {
31388e04a21Schristos 	/* Some Sierra devices presents themselves as a umass device with
31488e04a21Schristos 	 * Windows drivers on it. After installation of the driver, it
31588e04a21Schristos 	 * reinits into a * 3G serial device.
31688e04a21Schristos 	 */
31788e04a21Schristos 	usb_device_request_t req;
31888e04a21Schristos 
31988e04a21Schristos 	req.bmRequestType = UT_VENDOR;
32088e04a21Schristos 	req.bRequest = UR_SET_INTERFACE;
32188e04a21Schristos 	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
32288e04a21Schristos 	USETW(req.wIndex, UHF_PORT_CONNECTION);
32388e04a21Schristos 	USETW(req.wLength, 0);
32488e04a21Schristos 
32588e04a21Schristos 	(void) usbd_do_request(dev, &req, 0);
32688e04a21Schristos 
32788e04a21Schristos 	return UMATCH_HIGHEST; /* Match to prevent umass from attaching */
32888e04a21Schristos }
32988e04a21Schristos 
33088e04a21Schristos static int
u3g_4gsystems_reinit(struct usbd_device * dev)33188e04a21Schristos u3g_4gsystems_reinit(struct usbd_device *dev)
33288e04a21Schristos {
33388e04a21Schristos 	/* magic string adapted from usb_modeswitch database */
33488e04a21Schristos 	unsigned char cmd[31];
33588e04a21Schristos 
33688e04a21Schristos 	memset(cmd, 0, sizeof(cmd));
33788e04a21Schristos 	/* Byte 0..3: Command Block Wrapper (CBW) signature */
33888e04a21Schristos 	set_cbw(cmd);
33988e04a21Schristos 
34088e04a21Schristos 	cmd[4] = 0x12;
34188e04a21Schristos 	cmd[5] = 0x34;
34288e04a21Schristos 	cmd[6] = 0x56;
34388e04a21Schristos 	cmd[7] = 0x78;
34488e04a21Schristos 	cmd[8] = 0x80;
34588e04a21Schristos 	cmd[12] = 0x80;
34688e04a21Schristos 	cmd[14] = 0x06;
34788e04a21Schristos 	cmd[15] = 0x06;
34888e04a21Schristos 	cmd[16] = 0xf5;
34988e04a21Schristos 	cmd[17] = 0x04;
35088e04a21Schristos 	cmd[18] = 0x02;
35188e04a21Schristos 	cmd[19] = 0x52;
35288e04a21Schristos 	cmd[20] = 0x70;
35388e04a21Schristos 
35488e04a21Schristos 	return send_bulkmsg(dev, cmd, sizeof(cmd));
35588e04a21Schristos }
35688e04a21Schristos 
35788e04a21Schristos /*
35888e04a21Schristos  * First personality:
35988e04a21Schristos  *
36088e04a21Schristos  * Claim the entire device if a mode-switch is required.
36188e04a21Schristos  */
36288e04a21Schristos 
36388e04a21Schristos static int
umodeswitch_match(device_t parent,cfdata_t match,void * aux)36488e04a21Schristos umodeswitch_match(device_t parent, cfdata_t match, void *aux)
36588e04a21Schristos {
36688e04a21Schristos 	struct usb_attach_arg *uaa = aux;
36788e04a21Schristos 
36888e04a21Schristos 	/*
36988e04a21Schristos 	 * Huawei changes product when it is configured as a modem.
37088e04a21Schristos 	 */
37188e04a21Schristos 	switch (uaa->uaa_vendor) {
37288e04a21Schristos 	case USB_VENDOR_HUAWEI:
37388e04a21Schristos 		if (uaa->uaa_product == USB_PRODUCT_HUAWEI_K3765)
37488e04a21Schristos 			return UMATCH_NONE;
37588e04a21Schristos 
37688e04a21Schristos 		switch (uaa->uaa_product) {
37788e04a21Schristos 		case USB_PRODUCT_HUAWEI_E1750INIT:
37888e04a21Schristos 		case USB_PRODUCT_HUAWEI_K3765INIT:
37988e04a21Schristos 			return u3g_huawei_k3765_reinit(uaa->uaa_device);
38088e04a21Schristos 			break;
38188e04a21Schristos 		case USB_PRODUCT_HUAWEI_E171INIT:
38288e04a21Schristos 			return u3g_huawei_e171_reinit(uaa->uaa_device);
38388e04a21Schristos 			break;
38488e04a21Schristos 		case USB_PRODUCT_HUAWEI_E353INIT:
38588e04a21Schristos 			return u3g_huawei_e353_reinit(uaa->uaa_device);
38688e04a21Schristos 			break;
38788e04a21Schristos 		default:
38888e04a21Schristos 			return u3g_huawei_reinit(uaa->uaa_device);
38988e04a21Schristos 			break;
39088e04a21Schristos 		}
39188e04a21Schristos 		break;
39288e04a21Schristos 
39388e04a21Schristos 	case USB_VENDOR_NOVATEL2:
39488e04a21Schristos 		switch (uaa->uaa_product){
39588e04a21Schristos 		case USB_PRODUCT_NOVATEL2_MC950D_DRIVER:
39688e04a21Schristos 		case USB_PRODUCT_NOVATEL2_U760_DRIVER:
39788e04a21Schristos 			return u3g_bulk_scsi_eject(uaa->uaa_device);
39888e04a21Schristos 			break;
39988e04a21Schristos 		default:
40088e04a21Schristos 			break;
40188e04a21Schristos 		}
40288e04a21Schristos 		break;
40388e04a21Schristos 
4042b406972Smsaitoh 	case USB_VENDOR_LG:
4052b406972Smsaitoh 		if (uaa->uaa_product == USB_PRODUCT_LG_NTT_DOCOMO_L02C_STORAGE)
40688e04a21Schristos 			return u3g_bulk_scsi_eject(uaa->uaa_device);
40788e04a21Schristos 		break;
40888e04a21Schristos 
409828ce1d2Skhorben 	case USB_VENDOR_RALINK:
410828ce1d2Skhorben 		switch (uaa->uaa_product){
411828ce1d2Skhorben 		case USB_PRODUCT_RALINK_RT73:
412828ce1d2Skhorben 			return u3g_bulk_scsi_eject(uaa->uaa_device);
413828ce1d2Skhorben 			break;
414828ce1d2Skhorben 		}
415828ce1d2Skhorben 		break;
416828ce1d2Skhorben 
41747c45097Skhorben 	case USB_VENDOR_SIERRA:
41847c45097Skhorben 		if (uaa->uaa_product == USB_PRODUCT_SIERRA_INSTALLER)
41947c45097Skhorben 			return u3g_sierra_reinit(uaa->uaa_device);
42047c45097Skhorben 		break;
42147c45097Skhorben 
42288e04a21Schristos 	case USB_VENDOR_ZTE:
42388e04a21Schristos 		switch (uaa->uaa_product){
42488e04a21Schristos 		case USB_PRODUCT_ZTE_INSTALLER:
42588e04a21Schristos 		case USB_PRODUCT_ZTE_MF820D_INSTALLER:
42688e04a21Schristos 			(void)u3g_bulk_ata_eject(uaa->uaa_device);
42788e04a21Schristos 			(void)u3g_bulk_scsi_eject(uaa->uaa_device);
42888e04a21Schristos 			return UMATCH_HIGHEST;
42988e04a21Schristos 		default:
43088e04a21Schristos 			break;
43188e04a21Schristos 		}
43288e04a21Schristos 		break;
43388e04a21Schristos 
4342b406972Smsaitoh 	case USB_VENDOR_LONGCHEER:
4352b406972Smsaitoh 		if (uaa->uaa_product == USB_PRODUCT_LONGCHEER_XSSTICK_P14_INSTALLER)
43688e04a21Schristos 			return u3g_4gsystems_reinit(uaa->uaa_device);
43788e04a21Schristos 		break;
43888e04a21Schristos 
43917ba5223Smanu 	case USB_VENDOR_DLINK:
44017ba5223Smanu 		switch (uaa->uaa_product) {
44117ba5223Smanu 		case USB_PRODUCT_DLINK_DWM157E_CD:
44217ba5223Smanu 		case USB_PRODUCT_DLINK_DWM157_CD:
443*a4d9265cSmanu 		case USB_PRODUCT_DLINK_DWM222_CD:
44417ba5223Smanu 			(void)u3g_bulk_ata_eject(uaa->uaa_device);
44517ba5223Smanu 			(void)u3g_bulk_scsi_eject(uaa->uaa_device);
44617ba5223Smanu 			return UMATCH_HIGHEST;
44717ba5223Smanu 		default:
44817ba5223Smanu 			break;
44917ba5223Smanu 		}
45017ba5223Smanu 
45188e04a21Schristos 	default:
45288e04a21Schristos 		break;
45388e04a21Schristos 	}
45488e04a21Schristos 
45588e04a21Schristos 	return UMATCH_NONE;
45688e04a21Schristos }
45788e04a21Schristos 
45888e04a21Schristos static void
umodeswitch_attach(device_t parent,device_t self,void * aux)45988e04a21Schristos umodeswitch_attach(device_t parent, device_t self, void *aux)
46088e04a21Schristos {
46188e04a21Schristos 	struct usb_attach_arg *uaa = aux;
46288e04a21Schristos 
46388e04a21Schristos 	aprint_naive("\n");
46488e04a21Schristos 	aprint_normal(": Switching off umass mode\n");
46588e04a21Schristos 
46688e04a21Schristos 	if (uaa->uaa_vendor == USB_VENDOR_NOVATEL2) {
46788e04a21Schristos 		switch (uaa->uaa_product) {
46888e04a21Schristos 	    	case USB_PRODUCT_NOVATEL2_MC950D_DRIVER:
46988e04a21Schristos 	    	case USB_PRODUCT_NOVATEL2_U760_DRIVER:
47088e04a21Schristos 			/* About to disappear... */
47188e04a21Schristos 			return;
47288e04a21Schristos 			break;
47388e04a21Schristos 		default:
47488e04a21Schristos 			break;
47588e04a21Schristos 		}
47688e04a21Schristos 	}
47788e04a21Schristos 
47888e04a21Schristos 	/* Move the device into the configured state. */
47988e04a21Schristos 	(void) usbd_set_config_index(uaa->uaa_device, 0, 1);
48088e04a21Schristos }
48188e04a21Schristos 
48288e04a21Schristos static int
umodeswitch_detach(device_t self,int flags)48388e04a21Schristos umodeswitch_detach(device_t self, int flags)
48488e04a21Schristos {
48588e04a21Schristos 
48688e04a21Schristos 	return 0;
48788e04a21Schristos }
488