1 1.6 manu /* $NetBSD: umodeswitch.c,v 1.6 2023/08/04 13:25:17 manu Exp $ */ 2 1.1 christos 3 1.1 christos /*- 4 1.1 christos * Copyright (c) 2009, 2017 The NetBSD Foundation, Inc. 5 1.1 christos * All rights reserved. 6 1.1 christos * 7 1.1 christos * This code is derived from software contributed to The NetBSD Foundation. 8 1.1 christos * 9 1.1 christos * Redistribution and use in source and binary forms, with or without 10 1.1 christos * modification, are permitted provided that the following conditions 11 1.1 christos * are met: 12 1.1 christos * 1. Redistributions of source code must retain the above copyright 13 1.1 christos * notice, this list of conditions and the following disclaimer. 14 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 christos * notice, this list of conditions and the following disclaimer in the 16 1.1 christos * documentation and/or other materials provided with the distribution. 17 1.1 christos * 18 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19 1.1 christos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 1.1 christos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 1.1 christos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22 1.1 christos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 1.1 christos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 1.1 christos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 1.1 christos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 1.1 christos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 1.1 christos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 1.1 christos * POSSIBILITY OF SUCH DAMAGE. 29 1.1 christos */ 30 1.1 christos 31 1.1 christos 32 1.1 christos #include <sys/cdefs.h> 33 1.6 manu __KERNEL_RCSID(0, "$NetBSD: umodeswitch.c,v 1.6 2023/08/04 13:25:17 manu Exp $"); 34 1.1 christos 35 1.1 christos #include <sys/param.h> 36 1.1 christos #include <sys/systm.h> 37 1.1 christos #include <sys/kernel.h> 38 1.1 christos #include <sys/kmem.h> 39 1.1 christos #include <sys/bus.h> 40 1.1 christos #include <sys/conf.h> 41 1.1 christos #include <sys/tty.h> 42 1.1 christos 43 1.1 christos #include <dev/usb/usb.h> 44 1.1 christos #include <dev/usb/usbdi.h> 45 1.1 christos #include <dev/usb/usbdivar.h> 46 1.1 christos #include <dev/usb/usbdi_util.h> 47 1.1 christos 48 1.1 christos #include "usbdevs.h" 49 1.1 christos 50 1.1 christos /* 51 1.1 christos * This device driver handles devices that have two personalities. 52 1.1 christos * The first uses the 'usbdevif' 53 1.1 christos * interface attribute so that a match will claim the entire USB device 54 1.1 christos * for itself. This is used for when a device needs to be mode-switched 55 1.1 christos * and ensures any other interfaces present cannot be claimed by other 56 1.1 christos * drivers while the mode-switch is in progress. 57 1.1 christos */ 58 1.1 christos static int umodeswitch_match(device_t, cfdata_t, void *); 59 1.1 christos static void umodeswitch_attach(device_t, device_t, void *); 60 1.1 christos static int umodeswitch_detach(device_t, int); 61 1.1 christos 62 1.1 christos CFATTACH_DECL2_NEW(umodeswitch, 0, umodeswitch_match, 63 1.1 christos umodeswitch_attach, umodeswitch_detach, NULL, NULL, NULL); 64 1.1 christos 65 1.1 christos static int 66 1.1 christos send_bulkmsg(struct usbd_device *dev, void *cmd, size_t cmdlen) 67 1.1 christos { 68 1.1 christos struct usbd_interface *iface; 69 1.1 christos usb_interface_descriptor_t *id; 70 1.1 christos usb_endpoint_descriptor_t *ed; 71 1.1 christos struct usbd_pipe *pipe; 72 1.1 christos struct usbd_xfer *xfer; 73 1.1 christos int err, i; 74 1.1 christos 75 1.1 christos /* Move the device into the configured state. */ 76 1.1 christos err = usbd_set_config_index(dev, 0, 0); 77 1.1 christos if (err) { 78 1.1 christos aprint_error("%s: failed to set config index\n", __func__); 79 1.1 christos return UMATCH_NONE; 80 1.1 christos } 81 1.1 christos 82 1.1 christos err = usbd_device2interface_handle(dev, 0, &iface); 83 1.1 christos if (err != 0) { 84 1.1 christos aprint_error("%s: failed to get interface\n", __func__); 85 1.1 christos return UMATCH_NONE; 86 1.1 christos } 87 1.1 christos 88 1.1 christos id = usbd_get_interface_descriptor(iface); 89 1.1 christos ed = NULL; 90 1.1 christos for (i = 0 ; i < id->bNumEndpoints ; i++) { 91 1.1 christos ed = usbd_interface2endpoint_descriptor(iface, i); 92 1.1 christos if (ed == NULL) 93 1.1 christos continue; 94 1.1 christos if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_OUT) 95 1.1 christos continue; 96 1.1 christos if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK) 97 1.1 christos break; 98 1.1 christos } 99 1.1 christos 100 1.1 christos if (i == id->bNumEndpoints) 101 1.1 christos return UMATCH_NONE; 102 1.1 christos 103 1.1 christos err = usbd_open_pipe(iface, ed->bEndpointAddress, 104 1.1 christos USBD_EXCLUSIVE_USE, &pipe); 105 1.1 christos if (err != 0) { 106 1.1 christos aprint_error("%s: failed to open bulk transfer pipe %d\n", 107 1.1 christos __func__, ed->bEndpointAddress); 108 1.1 christos return UMATCH_NONE; 109 1.1 christos } 110 1.1 christos 111 1.1 christos int error = usbd_create_xfer(pipe, cmdlen, 0, 0, &xfer); 112 1.1 christos if (!error) { 113 1.1 christos 114 1.1 christos usbd_setup_xfer(xfer, NULL, cmd, cmdlen, 115 1.1 christos USBD_SYNCHRONOUS, USBD_DEFAULT_TIMEOUT, NULL); 116 1.1 christos 117 1.1 christos err = usbd_transfer(xfer); 118 1.1 christos 119 1.1 christos #if 0 /* XXXpooka: at least my huawei "fails" this always, but still detaches */ 120 1.1 christos if (err) 121 1.1 christos aprint_error("%s: transfer failed\n", __func__); 122 1.1 christos #else 123 1.1 christos err = 0; 124 1.1 christos #endif 125 1.1 christos usbd_destroy_xfer(xfer); 126 1.1 christos } else { 127 1.1 christos aprint_error("%s: failed to allocate xfer\n", __func__); 128 1.1 christos err = USBD_NOMEM; 129 1.1 christos } 130 1.1 christos 131 1.1 christos usbd_abort_pipe(pipe); 132 1.1 christos usbd_close_pipe(pipe); 133 1.1 christos 134 1.1 christos return err == USBD_NORMAL_COMPLETION ? UMATCH_HIGHEST : UMATCH_NONE; 135 1.1 christos } 136 1.1 christos 137 1.1 christos /* Byte 0..3: Command Block Wrapper (CBW) signature */ 138 1.1 christos static void 139 1.1 christos set_cbw(unsigned char *cmd) 140 1.1 christos { 141 1.1 christos cmd[0] = 0x55; 142 1.1 christos cmd[1] = 0x53; 143 1.1 christos cmd[2] = 0x42; 144 1.1 christos cmd[3] = 0x43; 145 1.1 christos } 146 1.1 christos 147 1.1 christos static int 148 1.1 christos u3g_bulk_scsi_eject(struct usbd_device *dev) 149 1.1 christos { 150 1.1 christos unsigned char cmd[31]; 151 1.1 christos 152 1.1 christos memset(cmd, 0, sizeof(cmd)); 153 1.1 christos /* Byte 0..3: Command Block Wrapper (CBW) signature */ 154 1.1 christos set_cbw(cmd); 155 1.1 christos /* 4..7: CBW Tag, has to unique, but only a single transfer used. */ 156 1.1 christos cmd[4] = 0x01; 157 1.1 christos /* 8..11: CBW Transfer Length, no data here */ 158 1.1 christos /* 12: CBW Flag: output, so 0 */ 159 1.1 christos /* 13: CBW Lun: 0 */ 160 1.1 christos /* 14: CBW Length */ 161 1.1 christos cmd[14] = 0x06; 162 1.1 christos 163 1.1 christos /* Rest is the SCSI payload */ 164 1.1 christos 165 1.1 christos /* 0: SCSI START/STOP opcode */ 166 1.1 christos cmd[15] = 0x1b; 167 1.1 christos /* 1..3 unused */ 168 1.1 christos /* 4 Load/Eject command */ 169 1.1 christos cmd[19] = 0x02; 170 1.1 christos /* 5: unused */ 171 1.1 christos 172 1.1 christos return send_bulkmsg(dev, cmd, sizeof(cmd)); 173 1.1 christos } 174 1.1 christos 175 1.1 christos static int 176 1.1 christos u3g_bulk_ata_eject(struct usbd_device *dev) 177 1.1 christos { 178 1.1 christos unsigned char cmd[31]; 179 1.1 christos 180 1.1 christos memset(cmd, 0, sizeof(cmd)); 181 1.1 christos /* Byte 0..3: Command Block Wrapper (CBW) signature */ 182 1.1 christos set_cbw(cmd); 183 1.1 christos /* 4..7: CBW Tag, has to unique, but only a single transfer used. */ 184 1.1 christos cmd[4] = 0x01; 185 1.1 christos /* 8..11: CBW Transfer Length, no data here */ 186 1.1 christos /* 12: CBW Flag: output, so 0 */ 187 1.1 christos /* 13: CBW Lun: 0 */ 188 1.1 christos /* 14: CBW Length */ 189 1.1 christos cmd[14] = 0x06; 190 1.1 christos 191 1.1 christos /* Rest is the SCSI payload */ 192 1.1 christos 193 1.1 christos /* 0: ATA pass-through */ 194 1.1 christos cmd[15] = 0x85; 195 1.1 christos /* 1..3 unused */ 196 1.1 christos /* 4 XXX What is this command? */ 197 1.1 christos cmd[19] = 0x24; 198 1.1 christos /* 5: unused */ 199 1.1 christos 200 1.1 christos return send_bulkmsg(dev, cmd, sizeof(cmd)); 201 1.1 christos } 202 1.1 christos 203 1.1 christos static int 204 1.1 christos u3g_huawei_reinit(struct usbd_device *dev) 205 1.1 christos { 206 1.1 christos /* 207 1.1 christos * The Huawei device presents itself as a umass device with Windows 208 1.1 christos * drivers on it. After installation of the driver, it reinits into a 209 1.1 christos * 3G serial device. 210 1.1 christos */ 211 1.1 christos usb_device_request_t req; 212 1.1 christos usb_config_descriptor_t *cdesc; 213 1.1 christos 214 1.1 christos /* Get the config descriptor */ 215 1.1 christos cdesc = usbd_get_config_descriptor(dev); 216 1.1 christos if (cdesc == NULL) { 217 1.1 christos usb_device_descriptor_t dd; 218 1.1 christos 219 1.1 christos if (usbd_get_device_desc(dev, &dd) != 0) 220 1.1 christos return UMATCH_NONE; 221 1.1 christos 222 1.1 christos if (dd.bNumConfigurations != 1) 223 1.1 christos return UMATCH_NONE; 224 1.1 christos 225 1.1 christos if (usbd_set_config_index(dev, 0, 1) != 0) 226 1.1 christos return UMATCH_NONE; 227 1.1 christos 228 1.1 christos cdesc = usbd_get_config_descriptor(dev); 229 1.1 christos 230 1.1 christos if (cdesc == NULL) 231 1.1 christos return UMATCH_NONE; 232 1.1 christos } 233 1.1 christos 234 1.1 christos /* 235 1.1 christos * One iface means umass mode, more than 1 (4 usually) means 3G mode. 236 1.1 christos * 237 1.1 christos * XXX: We should check the first interface's device class just to be 238 1.1 christos * sure. If it's a mass storage device, then we can be fairly certain 239 1.1 christos * it needs a mode-switch. 240 1.1 christos */ 241 1.1 christos if (cdesc->bNumInterface > 1) 242 1.1 christos return UMATCH_NONE; 243 1.1 christos 244 1.1 christos req.bmRequestType = UT_WRITE_DEVICE; 245 1.1 christos req.bRequest = UR_SET_FEATURE; 246 1.1 christos USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); 247 1.1 christos USETW(req.wIndex, UHF_PORT_SUSPEND); 248 1.1 christos USETW(req.wLength, 0); 249 1.1 christos 250 1.1 christos (void) usbd_do_request(dev, &req, 0); 251 1.1 christos 252 1.1 christos return UMATCH_HIGHEST; /* Prevent umass from attaching */ 253 1.1 christos } 254 1.1 christos 255 1.1 christos static int 256 1.1 christos u3g_huawei_k3765_reinit(struct usbd_device *dev) 257 1.1 christos { 258 1.1 christos unsigned char cmd[31]; 259 1.1 christos 260 1.1 christos /* magic string adapted from some webpage */ 261 1.1 christos memset(cmd, 0, sizeof(cmd)); 262 1.1 christos /* Byte 0..3: Command Block Wrapper (CBW) signature */ 263 1.1 christos set_cbw(cmd); 264 1.1 christos 265 1.1 christos cmd[15]= 0x11; 266 1.1 christos cmd[16]= 0x06; 267 1.1 christos 268 1.1 christos return send_bulkmsg(dev, cmd, sizeof(cmd)); 269 1.1 christos } 270 1.1 christos static int 271 1.1 christos u3g_huawei_e171_reinit(struct usbd_device *dev) 272 1.1 christos { 273 1.1 christos unsigned char cmd[31]; 274 1.1 christos 275 1.1 christos /* magic string adapted from some webpage */ 276 1.1 christos memset(cmd, 0, sizeof(cmd)); 277 1.1 christos /* Byte 0..3: Command Block Wrapper (CBW) signature */ 278 1.1 christos set_cbw(cmd); 279 1.1 christos 280 1.1 christos cmd[15]= 0x11; 281 1.1 christos cmd[16]= 0x06; 282 1.1 christos cmd[17]= 0x20; 283 1.1 christos cmd[20]= 0x01; 284 1.1 christos 285 1.1 christos return send_bulkmsg(dev, cmd, sizeof(cmd)); 286 1.1 christos } 287 1.1 christos 288 1.1 christos static int 289 1.1 christos u3g_huawei_e353_reinit(struct usbd_device *dev) 290 1.1 christos { 291 1.1 christos unsigned char cmd[31]; 292 1.1 christos 293 1.1 christos /* magic string adapted from some webpage */ 294 1.1 christos memset(cmd, 0, sizeof(cmd)); 295 1.1 christos /* Byte 0..3: Command Block Wrapper (CBW) signature */ 296 1.1 christos set_cbw(cmd); 297 1.1 christos 298 1.1 christos cmd[4] = 0x7f; 299 1.1 christos cmd[9] = 0x02; 300 1.1 christos cmd[12] = 0x80; 301 1.1 christos cmd[14] = 0x0a; 302 1.1 christos cmd[15] = 0x11; 303 1.1 christos cmd[16] = 0x06; 304 1.1 christos cmd[17] = 0x20; 305 1.1 christos cmd[23] = 0x01; 306 1.1 christos 307 1.1 christos return send_bulkmsg(dev, cmd, sizeof(cmd)); 308 1.1 christos } 309 1.1 christos 310 1.1 christos static int 311 1.1 christos u3g_sierra_reinit(struct usbd_device *dev) 312 1.1 christos { 313 1.1 christos /* Some Sierra devices presents themselves as a umass device with 314 1.1 christos * Windows drivers on it. After installation of the driver, it 315 1.1 christos * reinits into a * 3G serial device. 316 1.1 christos */ 317 1.1 christos usb_device_request_t req; 318 1.1 christos 319 1.1 christos req.bmRequestType = UT_VENDOR; 320 1.1 christos req.bRequest = UR_SET_INTERFACE; 321 1.1 christos USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); 322 1.1 christos USETW(req.wIndex, UHF_PORT_CONNECTION); 323 1.1 christos USETW(req.wLength, 0); 324 1.1 christos 325 1.1 christos (void) usbd_do_request(dev, &req, 0); 326 1.1 christos 327 1.1 christos return UMATCH_HIGHEST; /* Match to prevent umass from attaching */ 328 1.1 christos } 329 1.1 christos 330 1.1 christos static int 331 1.1 christos u3g_4gsystems_reinit(struct usbd_device *dev) 332 1.1 christos { 333 1.1 christos /* magic string adapted from usb_modeswitch database */ 334 1.1 christos unsigned char cmd[31]; 335 1.1 christos 336 1.1 christos memset(cmd, 0, sizeof(cmd)); 337 1.1 christos /* Byte 0..3: Command Block Wrapper (CBW) signature */ 338 1.1 christos set_cbw(cmd); 339 1.1 christos 340 1.1 christos cmd[4] = 0x12; 341 1.1 christos cmd[5] = 0x34; 342 1.1 christos cmd[6] = 0x56; 343 1.1 christos cmd[7] = 0x78; 344 1.1 christos cmd[8] = 0x80; 345 1.1 christos cmd[12] = 0x80; 346 1.1 christos cmd[14] = 0x06; 347 1.1 christos cmd[15] = 0x06; 348 1.1 christos cmd[16] = 0xf5; 349 1.1 christos cmd[17] = 0x04; 350 1.1 christos cmd[18] = 0x02; 351 1.1 christos cmd[19] = 0x52; 352 1.1 christos cmd[20] = 0x70; 353 1.1 christos 354 1.1 christos return send_bulkmsg(dev, cmd, sizeof(cmd)); 355 1.1 christos } 356 1.1 christos 357 1.1 christos /* 358 1.1 christos * First personality: 359 1.1 christos * 360 1.1 christos * Claim the entire device if a mode-switch is required. 361 1.1 christos */ 362 1.1 christos 363 1.1 christos static int 364 1.1 christos umodeswitch_match(device_t parent, cfdata_t match, void *aux) 365 1.1 christos { 366 1.1 christos struct usb_attach_arg *uaa = aux; 367 1.1 christos 368 1.1 christos /* 369 1.1 christos * Huawei changes product when it is configured as a modem. 370 1.1 christos */ 371 1.1 christos switch (uaa->uaa_vendor) { 372 1.1 christos case USB_VENDOR_HUAWEI: 373 1.1 christos if (uaa->uaa_product == USB_PRODUCT_HUAWEI_K3765) 374 1.1 christos return UMATCH_NONE; 375 1.1 christos 376 1.1 christos switch (uaa->uaa_product) { 377 1.1 christos case USB_PRODUCT_HUAWEI_E1750INIT: 378 1.1 christos case USB_PRODUCT_HUAWEI_K3765INIT: 379 1.1 christos return u3g_huawei_k3765_reinit(uaa->uaa_device); 380 1.1 christos break; 381 1.1 christos case USB_PRODUCT_HUAWEI_E171INIT: 382 1.1 christos return u3g_huawei_e171_reinit(uaa->uaa_device); 383 1.1 christos break; 384 1.1 christos case USB_PRODUCT_HUAWEI_E353INIT: 385 1.1 christos return u3g_huawei_e353_reinit(uaa->uaa_device); 386 1.1 christos break; 387 1.1 christos default: 388 1.1 christos return u3g_huawei_reinit(uaa->uaa_device); 389 1.1 christos break; 390 1.1 christos } 391 1.1 christos break; 392 1.1 christos 393 1.1 christos case USB_VENDOR_NOVATEL2: 394 1.1 christos switch (uaa->uaa_product){ 395 1.1 christos case USB_PRODUCT_NOVATEL2_MC950D_DRIVER: 396 1.1 christos case USB_PRODUCT_NOVATEL2_U760_DRIVER: 397 1.1 christos return u3g_bulk_scsi_eject(uaa->uaa_device); 398 1.1 christos break; 399 1.1 christos default: 400 1.1 christos break; 401 1.1 christos } 402 1.1 christos break; 403 1.1 christos 404 1.4 msaitoh case USB_VENDOR_LG: 405 1.4 msaitoh if (uaa->uaa_product == USB_PRODUCT_LG_NTT_DOCOMO_L02C_STORAGE) 406 1.2 khorben return u3g_bulk_scsi_eject(uaa->uaa_device); 407 1.2 khorben break; 408 1.2 khorben 409 1.3 khorben case USB_VENDOR_RALINK: 410 1.3 khorben switch (uaa->uaa_product){ 411 1.3 khorben case USB_PRODUCT_RALINK_RT73: 412 1.3 khorben return u3g_bulk_scsi_eject(uaa->uaa_device); 413 1.3 khorben break; 414 1.3 khorben } 415 1.3 khorben break; 416 1.3 khorben 417 1.1 christos case USB_VENDOR_SIERRA: 418 1.1 christos if (uaa->uaa_product == USB_PRODUCT_SIERRA_INSTALLER) 419 1.1 christos return u3g_sierra_reinit(uaa->uaa_device); 420 1.1 christos break; 421 1.1 christos 422 1.1 christos case USB_VENDOR_ZTE: 423 1.1 christos switch (uaa->uaa_product){ 424 1.1 christos case USB_PRODUCT_ZTE_INSTALLER: 425 1.1 christos case USB_PRODUCT_ZTE_MF820D_INSTALLER: 426 1.1 christos (void)u3g_bulk_ata_eject(uaa->uaa_device); 427 1.1 christos (void)u3g_bulk_scsi_eject(uaa->uaa_device); 428 1.1 christos return UMATCH_HIGHEST; 429 1.1 christos default: 430 1.1 christos break; 431 1.1 christos } 432 1.1 christos break; 433 1.1 christos 434 1.4 msaitoh case USB_VENDOR_LONGCHEER: 435 1.4 msaitoh if (uaa->uaa_product == USB_PRODUCT_LONGCHEER_XSSTICK_P14_INSTALLER) 436 1.1 christos return u3g_4gsystems_reinit(uaa->uaa_device); 437 1.1 christos break; 438 1.1 christos 439 1.5 manu case USB_VENDOR_DLINK: 440 1.5 manu switch (uaa->uaa_product) { 441 1.5 manu case USB_PRODUCT_DLINK_DWM157E_CD: 442 1.5 manu case USB_PRODUCT_DLINK_DWM157_CD: 443 1.6 manu case USB_PRODUCT_DLINK_DWM222_CD: 444 1.5 manu (void)u3g_bulk_ata_eject(uaa->uaa_device); 445 1.5 manu (void)u3g_bulk_scsi_eject(uaa->uaa_device); 446 1.5 manu return UMATCH_HIGHEST; 447 1.5 manu default: 448 1.5 manu break; 449 1.5 manu } 450 1.5 manu 451 1.1 christos default: 452 1.1 christos break; 453 1.1 christos } 454 1.1 christos 455 1.1 christos return UMATCH_NONE; 456 1.1 christos } 457 1.1 christos 458 1.1 christos static void 459 1.1 christos umodeswitch_attach(device_t parent, device_t self, void *aux) 460 1.1 christos { 461 1.1 christos struct usb_attach_arg *uaa = aux; 462 1.1 christos 463 1.1 christos aprint_naive("\n"); 464 1.1 christos aprint_normal(": Switching off umass mode\n"); 465 1.1 christos 466 1.1 christos if (uaa->uaa_vendor == USB_VENDOR_NOVATEL2) { 467 1.1 christos switch (uaa->uaa_product) { 468 1.1 christos case USB_PRODUCT_NOVATEL2_MC950D_DRIVER: 469 1.1 christos case USB_PRODUCT_NOVATEL2_U760_DRIVER: 470 1.1 christos /* About to disappear... */ 471 1.1 christos return; 472 1.1 christos break; 473 1.1 christos default: 474 1.1 christos break; 475 1.1 christos } 476 1.1 christos } 477 1.1 christos 478 1.1 christos /* Move the device into the configured state. */ 479 1.1 christos (void) usbd_set_config_index(uaa->uaa_device, 0, 1); 480 1.1 christos } 481 1.1 christos 482 1.1 christos static int 483 1.1 christos umodeswitch_detach(device_t self, int flags) 484 1.1 christos { 485 1.1 christos 486 1.1 christos return 0; 487 1.1 christos } 488