usb_msctest.c revision 185290
167754Smsmith/* $FreeBSD: head/sys/dev/usb2/core/usb2_msctest.c 185290 2008-11-25 08:04:40Z alfred $ */ 267754Smsmith/*- 367754Smsmith * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 467754Smsmith * 567754Smsmith * Redistribution and use in source and binary forms, with or without 667754Smsmith * modification, are permitted provided that the following conditions 7217365Sjkim * are met: 8278970Sjkim * 1. Redistributions of source code must retain the above copyright 970243Smsmith * notice, this list of conditions and the following disclaimer. 1067754Smsmith * 2. Redistributions in binary form must reproduce the above copyright 11217365Sjkim * notice, this list of conditions and the following disclaimer in the 12217365Sjkim * documentation and/or other materials provided with the distribution. 13217365Sjkim * 14217365Sjkim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15217365Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16217365Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17217365Sjkim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18217365Sjkim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19217365Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20217365Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21217365Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22217365Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23217365Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24217365Sjkim * SUCH DAMAGE. 2567754Smsmith */ 26217365Sjkim 27217365Sjkim/* 28217365Sjkim * The following file contains code that will detect USB autoinstall 2967754Smsmith * disks. 30217365Sjkim * 31217365Sjkim * TODO: Potentially we could add code to automatically detect USB 32217365Sjkim * mass storage quirks for not supported SCSI commands! 33217365Sjkim */ 34217365Sjkim 35217365Sjkim#include <dev/usb2/include/usb2_defs.h> 36217365Sjkim#include <dev/usb2/include/usb2_mfunc.h> 37217365Sjkim#include <dev/usb2/include/usb2_error.h> 38217365Sjkim#include <dev/usb2/include/usb2_standard.h> 39217365Sjkim#include <dev/usb2/include/usb2_devid.h> 40217365Sjkim 41217365Sjkim#define USB_DEBUG_VAR usb2_debug 42217365Sjkim 4367754Smsmith#include <dev/usb2/core/usb2_core.h> 44193341Sjkim#include <dev/usb2/core/usb2_busdma.h> 45193341Sjkim#include <dev/usb2/core/usb2_process.h> 46193341Sjkim#include <dev/usb2/core/usb2_transfer.h> 47193341Sjkim#include <dev/usb2/core/usb2_msctest.h> 48193341Sjkim#include <dev/usb2/core/usb2_debug.h> 49193341Sjkim#include <dev/usb2/core/usb2_busdma.h> 50193341Sjkim#include <dev/usb2/core/usb2_device.h> 51193341Sjkim#include <dev/usb2/core/usb2_request.h> 52193341Sjkim#include <dev/usb2/core/usb2_util.h> 5367754Smsmith#include <dev/usb2/core/usb2_lookup.h> 5467754Smsmith 55102550Siwasaki#include <dev/usb2/include/usb2_mfunc.h> 5667754Smsmith#include <dev/usb2/include/usb2_error.h> 57102550Siwasaki#include <dev/usb2/include/usb2_standard.h> 5891116Smsmith 5967754Smsmithenum { 60151937Sjkim ST_COMMAND, 6167754Smsmith ST_DATA_RD, 62151937Sjkim ST_DATA_RD_CS, 63151937Sjkim ST_DATA_WR, 64151937Sjkim ST_DATA_WR_CS, 65151937Sjkim ST_STATUS, 66151937Sjkim ST_MAX, 67151937Sjkim}; 68151937Sjkim 69151937Sjkimenum { 70249663Sjkim DIR_IN, 71249663Sjkim DIR_OUT, 72249663Sjkim DIR_NONE, 73249663Sjkim}; 74249663Sjkim 75249663Sjkim#define BULK_SIZE 64 /* dummy */ 76151937Sjkim 77220663Sjkim/* Command Block Wrapper */ 78220663Sjkimstruct bbb_cbw { 79220663Sjkim uDWord dCBWSignature; 80220663Sjkim#define CBWSIGNATURE 0x43425355 81220663Sjkim uDWord dCBWTag; 82220663Sjkim uDWord dCBWDataTransferLength; 83220663Sjkim uByte bCBWFlags; 84249663Sjkim#define CBWFLAGS_OUT 0x00 85220663Sjkim#define CBWFLAGS_IN 0x80 86220663Sjkim uByte bCBWLUN; 87220663Sjkim uByte bCDBLength; 88220663Sjkim#define CBWCDBLENGTH 16 89220663Sjkim uByte CBWCDB[CBWCDBLENGTH]; 90220663Sjkim} __packed; 91220663Sjkim 92220663Sjkim/* Command Status Wrapper */ 93220663Sjkimstruct bbb_csw { 94220663Sjkim uDWord dCSWSignature; 95220663Sjkim#define CSWSIGNATURE 0x53425355 96220663Sjkim uDWord dCSWTag; 97220663Sjkim uDWord dCSWDataResidue; 98220663Sjkim uByte bCSWStatus; 99228110Sjkim#define CSWSTATUS_GOOD 0x0 100228110Sjkim#define CSWSTATUS_FAILED 0x1 101220663Sjkim#define CSWSTATUS_PHASE 0x2 102220663Sjkim} __packed; 103220663Sjkim 104220663Sjkimstruct bbb_transfer { 105220663Sjkim struct mtx mtx; 106220663Sjkim struct cv cv; 107220663Sjkim struct bbb_cbw cbw; 108220663Sjkim struct bbb_csw csw; 109220663Sjkim 110220663Sjkim struct usb2_xfer *xfer[ST_MAX]; 111220663Sjkim 112220663Sjkim uint8_t *data_ptr; 113220663Sjkim 114220663Sjkim uint32_t data_len; /* bytes */ 115220663Sjkim uint32_t data_rem; /* bytes */ 116234623Sjkim uint32_t data_timeout; /* ms */ 117234623Sjkim uint32_t actlen; /* bytes */ 118220663Sjkim 119220663Sjkim uint8_t cmd_len; /* bytes */ 120220663Sjkim uint8_t dir; 121220663Sjkim uint8_t lun; 122220663Sjkim uint8_t state; 123220663Sjkim uint8_t error; 124151937Sjkim uint8_t status_try; 12567754Smsmith 12667754Smsmith uint8_t buffer[256]; 12767754Smsmith}; 12867754Smsmith 12967754Smsmithstatic usb2_callback_t bbb_command_callback; 13067754Smsmithstatic usb2_callback_t bbb_data_read_callback; 13167754Smsmithstatic usb2_callback_t bbb_data_rd_cs_callback; 13267754Smsmithstatic usb2_callback_t bbb_data_write_callback; 13367754Smsmithstatic usb2_callback_t bbb_data_wr_cs_callback; 134151937Sjkimstatic usb2_callback_t bbb_status_callback; 13567754Smsmith 136151937Sjkimstatic const struct usb2_config bbb_config[ST_MAX] = { 13767754Smsmith 13867754Smsmith [ST_COMMAND] = { 13967754Smsmith .type = UE_BULK, 14067754Smsmith .endpoint = UE_ADDR_ANY, 141252279Sjkim .direction = UE_DIR_OUT, 14267754Smsmith .mh.bufsize = sizeof(struct bbb_cbw), 14367754Smsmith .mh.flags = {}, 144284583Sjkim .mh.callback = &bbb_command_callback, 145252279Sjkim .mh.timeout = 4 * USB_MS_HZ, /* 4 seconds */ 14667754Smsmith }, 14767754Smsmith 14867754Smsmith [ST_DATA_RD] = { 14967754Smsmith .type = UE_BULK, 15067754Smsmith .endpoint = UE_ADDR_ANY, 15167754Smsmith .direction = UE_DIR_IN, 15267754Smsmith .mh.bufsize = BULK_SIZE, 15367754Smsmith .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,}, 15467754Smsmith .mh.callback = &bbb_data_read_callback, 15567754Smsmith .mh.timeout = 4 * USB_MS_HZ, /* 4 seconds */ 15667754Smsmith }, 15767754Smsmith 15867754Smsmith [ST_DATA_RD_CS] = { 15967754Smsmith .type = UE_CONTROL, 16067754Smsmith .endpoint = 0x00, /* Control pipe */ 16167754Smsmith .direction = UE_DIR_ANY, 162151937Sjkim .mh.bufsize = sizeof(struct usb2_device_request), 16367754Smsmith .mh.flags = {}, 16467754Smsmith .mh.callback = &bbb_data_rd_cs_callback, 16567754Smsmith .mh.timeout = 1 * USB_MS_HZ, /* 1 second */ 16683174Smsmith }, 16767754Smsmith 16867754Smsmith [ST_DATA_WR] = { 16999679Siwasaki .type = UE_BULK, 17067754Smsmith .endpoint = UE_ADDR_ANY, 17167754Smsmith .direction = UE_DIR_OUT, 17299679Siwasaki .mh.bufsize = BULK_SIZE, 17367754Smsmith .mh.flags = {.proxy_buffer = 1,}, 174151937Sjkim .mh.callback = &bbb_data_write_callback, 175151937Sjkim .mh.timeout = 4 * USB_MS_HZ, /* 4 seconds */ 17667754Smsmith }, 17799679Siwasaki 17899679Siwasaki [ST_DATA_WR_CS] = { 17999679Siwasaki .type = UE_CONTROL, 18067754Smsmith .endpoint = 0x00, /* Control pipe */ 18167754Smsmith .direction = UE_DIR_ANY, 18267754Smsmith .mh.bufsize = sizeof(struct usb2_device_request), 18367754Smsmith .mh.flags = {}, 18467754Smsmith .mh.callback = &bbb_data_wr_cs_callback, 18567754Smsmith .mh.timeout = 1 * USB_MS_HZ, /* 1 second */ 18667754Smsmith }, 187241973Sjkim 18867754Smsmith [ST_STATUS] = { 18967754Smsmith .type = UE_BULK, 19067754Smsmith .endpoint = UE_ADDR_ANY, 19167754Smsmith .direction = UE_DIR_IN, 19267754Smsmith .mh.bufsize = sizeof(struct bbb_csw), 19367754Smsmith .mh.flags = {.short_xfer_ok = 1,}, 19467754Smsmith .mh.callback = &bbb_status_callback, 19567754Smsmith .mh.timeout = 1 * USB_MS_HZ, /* 1 second */ 19667754Smsmith }, 19767754Smsmith}; 19867754Smsmith 199114237Snjlstatic void 200114237Snjlbbb_done(struct bbb_transfer *sc, uint8_t error) 20167754Smsmith{ 20267754Smsmith struct usb2_xfer *xfer; 20367754Smsmith 20487031Smsmith xfer = sc->xfer[sc->state]; 20567754Smsmith 206114237Snjl /* verify the error code */ 20767754Smsmith 20867754Smsmith if (error) { 20967754Smsmith switch (USB_GET_STATE(xfer)) { 21067754Smsmith case USB_ST_SETUP: 21167754Smsmith case USB_ST_TRANSFERRED: 21267754Smsmith error = 1; 21367754Smsmith break; 21467754Smsmith default: 21567754Smsmith error = 2; 21667754Smsmith break; 21767754Smsmith } 21867754Smsmith } 21967754Smsmith sc->error = error; 22067754Smsmith sc->state = ST_COMMAND; 221151937Sjkim sc->status_try = 1; 22267754Smsmith usb2_cv_signal(&sc->cv); 22367754Smsmith return; 22467754Smsmith} 22567754Smsmith 22667754Smsmithstatic void 22767754Smsmithbbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index) 22867754Smsmith{ 22967754Smsmith sc->state = xfer_index; 23067754Smsmith usb2_transfer_start(sc->xfer[xfer_index]); 23167754Smsmith return; 23267754Smsmith} 23367754Smsmith 23467754Smsmithstatic void 23567754Smsmithbbb_data_clear_stall_callback(struct usb2_xfer *xfer, 23667754Smsmith uint8_t next_xfer, uint8_t stall_xfer) 23767754Smsmith{ 23867754Smsmith struct bbb_transfer *sc = xfer->priv_sc; 23967754Smsmith 24067754Smsmith if (usb2_clear_stall_callback(xfer, sc->xfer[stall_xfer])) { 24167754Smsmith switch (USB_GET_STATE(xfer)) { 24267754Smsmith case USB_ST_SETUP: 24367754Smsmith case USB_ST_TRANSFERRED: 24467754Smsmith bbb_transfer_start(sc, next_xfer); 24567754Smsmith break; 246151937Sjkim default: 247151937Sjkim bbb_done(sc, 1); 24867754Smsmith break; 24967754Smsmith } 25067754Smsmith } 25167754Smsmith return; 25267754Smsmith} 25391116Smsmith 25467754Smsmithstatic void 25591116Smsmithbbb_command_callback(struct usb2_xfer *xfer) 25667754Smsmith{ 25791116Smsmith struct bbb_transfer *sc = xfer->priv_sc; 25891116Smsmith uint32_t tag; 25967754Smsmith 26067754Smsmith switch (USB_GET_STATE(xfer)) { 261151937Sjkim case USB_ST_TRANSFERRED: 262151937Sjkim bbb_transfer_start 26367754Smsmith (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD : 26467754Smsmith (sc->dir == DIR_OUT) ? ST_DATA_WR : 26567754Smsmith ST_STATUS)); 26667754Smsmith break; 267151937Sjkim 26867754Smsmith case USB_ST_SETUP: 26999679Siwasaki sc->status_try = 0; 27091116Smsmith tag = UGETDW(sc->cbw.dCBWTag) + 1; 27191116Smsmith USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE); 27291116Smsmith USETDW(sc->cbw.dCBWTag, tag); 27367754Smsmith USETDW(sc->cbw.dCBWDataTransferLength, sc->data_len); 27467754Smsmith sc->cbw.bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT); 275151937Sjkim sc->cbw.bCBWLUN = sc->lun; 276151937Sjkim sc->cbw.bCDBLength = sc->cmd_len; 27767754Smsmith if (sc->cbw.bCDBLength > sizeof(sc->cbw.CBWCDB)) { 27867754Smsmith sc->cbw.bCDBLength = sizeof(sc->cbw.CBWCDB); 27967754Smsmith DPRINTFN(0, "Truncating long command!\n"); 280241973Sjkim } 281151937Sjkim xfer->frlengths[0] = sizeof(sc->cbw); 28277424Smsmith 28391116Smsmith usb2_set_frame_data(xfer, &sc->cbw, 0); 28467754Smsmith usb2_start_hardware(xfer); 28591116Smsmith break; 28691116Smsmith 28791116Smsmith default: /* Error */ 28891116Smsmith bbb_done(sc, 1); 28967754Smsmith break; 29067754Smsmith } 291151937Sjkim return; 292151937Sjkim} 29367754Smsmith 29467754Smsmithstatic void 29567754Smsmithbbb_data_read_callback(struct usb2_xfer *xfer) 296241973Sjkim{ 297151937Sjkim struct bbb_transfer *sc = xfer->priv_sc; 29867754Smsmith uint32_t max_bulk = xfer->max_data_length; 29991116Smsmith 30067754Smsmith switch (USB_GET_STATE(xfer)) { 30191116Smsmith case USB_ST_TRANSFERRED: 30291116Smsmith sc->data_rem -= xfer->actlen; 30391116Smsmith sc->data_ptr += xfer->actlen; 30491116Smsmith sc->actlen += xfer->actlen; 305272444Sjkim 306272444Sjkim if (xfer->actlen < xfer->sumlen) { 307272444Sjkim /* short transfer */ 308272444Sjkim sc->data_rem = 0; 30967754Smsmith } 31067754Smsmith case USB_ST_SETUP: 31167754Smsmith DPRINTF("max_bulk=%d, data_rem=%d\n", 31267754Smsmith max_bulk, sc->data_rem); 31367754Smsmith 31467754Smsmith if (sc->data_rem == 0) { 31567754Smsmith bbb_transfer_start(sc, ST_STATUS); 31667754Smsmith break; 317241973Sjkim } 31891116Smsmith if (max_bulk > sc->data_rem) { 31967754Smsmith max_bulk = sc->data_rem; 32067754Smsmith } 32167754Smsmith xfer->timeout = sc->data_timeout; 32267754Smsmith xfer->frlengths[0] = max_bulk; 32367754Smsmith 32467754Smsmith usb2_set_frame_data(xfer, sc->data_ptr, 0); 32567754Smsmith usb2_start_hardware(xfer); 32667754Smsmith break; 32767754Smsmith 32867754Smsmith default: /* Error */ 32967754Smsmith if (xfer->error == USB_ERR_CANCELLED) { 33067754Smsmith bbb_done(sc, 1); 33167754Smsmith } else { 33267754Smsmith bbb_transfer_start(sc, ST_DATA_RD_CS); 333151937Sjkim } 334151937Sjkim break; 33567754Smsmith } 33667754Smsmith return; 33767754Smsmith} 33867754Smsmith 33967754Smsmithstatic void 34067754Smsmithbbb_data_rd_cs_callback(struct usb2_xfer *xfer) 34167754Smsmith{ 34269746Smsmith bbb_data_clear_stall_callback(xfer, ST_STATUS, 34369746Smsmith ST_DATA_RD); 344151937Sjkim return; 345151937Sjkim} 34669746Smsmith 34769746Smsmithstatic void 34867754Smsmithbbb_data_write_callback(struct usb2_xfer *xfer) 34967754Smsmith{ 35067754Smsmith struct bbb_transfer *sc = xfer->priv_sc; 35167754Smsmith uint32_t max_bulk = xfer->max_data_length; 35267754Smsmith 35367754Smsmith switch (USB_GET_STATE(xfer)) { 354241973Sjkim case USB_ST_TRANSFERRED: 355151937Sjkim sc->data_rem -= xfer->actlen; 356167802Sjkim sc->data_ptr += xfer->actlen; 35767754Smsmith sc->actlen += xfer->actlen; 35887031Smsmith 35987031Smsmith if (xfer->actlen < xfer->sumlen) { 36067754Smsmith /* short transfer */ 36187031Smsmith sc->data_rem = 0; 36287031Smsmith } 36367754Smsmith case USB_ST_SETUP: 364151937Sjkim DPRINTF("max_bulk=%d, data_rem=%d\n", 365151937Sjkim max_bulk, sc->data_rem); 36667754Smsmith 36767754Smsmith if (sc->data_rem == 0) { 36867754Smsmith bbb_transfer_start(sc, ST_STATUS); 369241973Sjkim return; 370151937Sjkim } 37187031Smsmith if (max_bulk > sc->data_rem) { 37267754Smsmith max_bulk = sc->data_rem; 37367754Smsmith } 37467754Smsmith xfer->timeout = sc->data_timeout; 37567754Smsmith xfer->frlengths[0] = max_bulk; 37667754Smsmith 37767754Smsmith usb2_set_frame_data(xfer, sc->data_ptr, 0); 37867754Smsmith usb2_start_hardware(xfer); 37967754Smsmith return; 38067754Smsmith 38167754Smsmith default: /* Error */ 38267754Smsmith if (xfer->error == USB_ERR_CANCELLED) { 38367754Smsmith bbb_done(sc, 1); 38467754Smsmith } else { 38567754Smsmith bbb_transfer_start(sc, ST_DATA_WR_CS); 38667754Smsmith } 38767754Smsmith return; 38867754Smsmith 38967754Smsmith } 39067754Smsmith} 39167754Smsmith 39267754Smsmithstatic void 39367754Smsmithbbb_data_wr_cs_callback(struct usb2_xfer *xfer) 39467754Smsmith{ 39567754Smsmith bbb_data_clear_stall_callback(xfer, ST_STATUS, 39667754Smsmith ST_DATA_WR); 39783174Smsmith return; 39867754Smsmith} 39967754Smsmith 40067754Smsmithstatic void 40167754Smsmithbbb_status_callback(struct usb2_xfer *xfer) 40267754Smsmith{ 40367754Smsmith struct bbb_transfer *sc = xfer->priv_sc; 40467754Smsmith 40567754Smsmith switch (USB_GET_STATE(xfer)) { 40667754Smsmith case USB_ST_TRANSFERRED: 40767754Smsmith 40867754Smsmith /* very simple status check */ 40967754Smsmith 41067754Smsmith if (xfer->actlen < sizeof(sc->csw)) { 41167754Smsmith bbb_done(sc, 1);/* error */ 41267754Smsmith } else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) { 41367754Smsmith bbb_done(sc, 0);/* success */ 41467754Smsmith } else { 41587031Smsmith bbb_done(sc, 1);/* error */ 41667754Smsmith } 417123315Snjl break; 418123315Snjl 419167802Sjkim case USB_ST_SETUP: 420167802Sjkim xfer->frlengths[0] = sizeof(sc->csw); 421167802Sjkim 42267754Smsmith usb2_set_frame_data(xfer, &sc->csw, 0); 42367754Smsmith usb2_start_hardware(xfer); 42467754Smsmith break; 42599679Siwasaki 42667754Smsmith default: 42799679Siwasaki DPRINTFN(0, "Failed to read CSW: %s, try %d\n", 42867754Smsmith usb2_errstr(xfer->error), sc->status_try); 42967754Smsmith 43067754Smsmith if ((xfer->error == USB_ERR_CANCELLED) || 43167754Smsmith (sc->status_try)) { 43267754Smsmith bbb_done(sc, 1); 43367754Smsmith } else { 43467754Smsmith sc->status_try = 1; 43567754Smsmith bbb_transfer_start(sc, ST_DATA_RD_CS); 43667754Smsmith } 43767754Smsmith break; 43867754Smsmith } 43967754Smsmith return; 44067754Smsmith} 44167754Smsmith 44267754Smsmith/*------------------------------------------------------------------------* 44367754Smsmith * bbb_command_start - execute a SCSI command synchronously 44467754Smsmith * 44567754Smsmith * Return values 44667754Smsmith * 0: Success 44799679Siwasaki * Else: Failure 44885756Smsmith *------------------------------------------------------------------------*/ 44967754Smsmithstatic uint8_t 45085756Smsmithbbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun, 451250838Sjkim void *data_ptr, uint32_t data_len, uint8_t cmd_len, 45267754Smsmith uint32_t data_timeout) 45367754Smsmith{ 45467754Smsmith sc->lun = lun; 45567754Smsmith sc->dir = data_len ? dir : DIR_NONE; 45667754Smsmith sc->data_ptr = data_ptr; 45767754Smsmith sc->data_len = data_len; 45867754Smsmith sc->data_rem = data_len; 45967754Smsmith sc->data_timeout = (data_timeout + USB_MS_HZ); 46085756Smsmith sc->actlen = 0; 461250838Sjkim sc->cmd_len = cmd_len; 46285756Smsmith 46385756Smsmith usb2_transfer_start(sc->xfer[sc->state]); 46485756Smsmith 46585756Smsmith while (usb2_transfer_pending(sc->xfer[sc->state])) { 46667754Smsmith usb2_cv_wait(&sc->cv, &sc->mtx); 467250838Sjkim } 46867754Smsmith return (sc->error); 46967754Smsmith} 47067754Smsmith 47167754Smsmith/*------------------------------------------------------------------------* 47267754Smsmith * usb2_test_autoinstall 47367754Smsmith * 47467754Smsmith * Return values: 47567754Smsmith * 0: This interface is an auto install disk (CD-ROM) 47667754Smsmith * Else: Not an auto install disk. 47767754Smsmith *------------------------------------------------------------------------*/ 47867754Smsmithusb2_error_t 47967754Smsmithusb2_test_autoinstall(struct usb2_device *udev, uint8_t iface_index, 480151937Sjkim uint8_t do_eject) 481151937Sjkim{ 482151937Sjkim struct usb2_interface *iface; 48367754Smsmith struct usb2_interface_descriptor *id; 484151937Sjkim usb2_error_t err; 485151937Sjkim uint8_t timeout; 486151937Sjkim uint8_t sid_type; 48767754Smsmith struct bbb_transfer *sc; 48867754Smsmith 48967754Smsmith if (udev == NULL) { 49067754Smsmith return (USB_ERR_INVAL); 49167754Smsmith } 49267754Smsmith iface = usb2_get_iface(udev, iface_index); 49367754Smsmith if (iface == NULL) { 49467754Smsmith return (USB_ERR_INVAL); 49567754Smsmith } 49667754Smsmith id = iface->idesc; 49767754Smsmith if (id == NULL) { 49867754Smsmith return (USB_ERR_INVAL); 49967754Smsmith } 50067754Smsmith if (id->bInterfaceClass != UICLASS_MASS) { 50167754Smsmith return (USB_ERR_INVAL); 50267754Smsmith } 503151937Sjkim switch (id->bInterfaceSubClass) { 504151937Sjkim case UISUBCLASS_SCSI: 50567754Smsmith case UISUBCLASS_UFI: 50667754Smsmith break; 50767754Smsmith default: 50867754Smsmith return (USB_ERR_INVAL); 50967754Smsmith } 51067754Smsmith 51167754Smsmith switch (id->bInterfaceProtocol) { 51267754Smsmith case UIPROTO_MASS_BBB_OLD: 51367754Smsmith case UIPROTO_MASS_BBB: 51467754Smsmith break; 51567754Smsmith default: 516117521Snjl return (USB_ERR_INVAL); 51767754Smsmith } 51867754Smsmith 51967754Smsmith sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO); 52067754Smsmith if (sc == NULL) { 52167754Smsmith return (USB_ERR_NOMEM); 52267754Smsmith } 52367754Smsmith mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF); 52467754Smsmith usb2_cv_init(&sc->cv, "WBBB"); 52567754Smsmith 52667754Smsmith err = usb2_transfer_setup(udev, 52767754Smsmith &iface_index, sc->xfer, bbb_config, 52867754Smsmith ST_MAX, sc, &sc->mtx); 52967754Smsmith 53067754Smsmith if (err) { 53167754Smsmith goto done; 53267754Smsmith } 533151937Sjkim mtx_lock(&sc->mtx); 534151937Sjkim 53567754Smsmith timeout = 4; /* tries */ 53667754Smsmith 53767754Smsmithrepeat_inquiry: 53867754Smsmith 53967754Smsmith sc->cbw.CBWCDB[0] = 0x12; /* INQUIRY */ 54067754Smsmith sc->cbw.CBWCDB[1] = 0; 54167754Smsmith sc->cbw.CBWCDB[2] = 0; 54267754Smsmith sc->cbw.CBWCDB[3] = 0; 54367754Smsmith sc->cbw.CBWCDB[4] = 0x24; /* length */ 54467754Smsmith sc->cbw.CBWCDB[5] = 0; 54567754Smsmith err = bbb_command_start(sc, DIR_IN, 0, 546117521Snjl sc->buffer, 0x24, 6, USB_MS_HZ); 54767754Smsmith 54867754Smsmith if ((sc->actlen != 0) && (err == 0)) { 54967754Smsmith sid_type = sc->buffer[0] & 0x1F; 55067754Smsmith if (sid_type == 0x05) { 55167754Smsmith /* CD-ROM */ 55267754Smsmith if (do_eject) { 55367754Smsmith /* 0: opcode: SCSI START/STOP */ 55467754Smsmith sc->cbw.CBWCDB[0] = 0x1b; 55567754Smsmith /* 1: byte2: Not immediate */ 55667754Smsmith sc->cbw.CBWCDB[1] = 0x00; 55767754Smsmith /* 2..3: reserved */ 55867754Smsmith sc->cbw.CBWCDB[2] = 0x00; 55967754Smsmith sc->cbw.CBWCDB[3] = 0x00; 56067754Smsmith /* 4: Load/Eject command */ 56167754Smsmith sc->cbw.CBWCDB[4] = 0x02; 56267754Smsmith /* 5: control */ 563151937Sjkim sc->cbw.CBWCDB[5] = 0x00; 564151937Sjkim err = bbb_command_start(sc, DIR_OUT, 0, 56567754Smsmith NULL, 0, 6, USB_MS_HZ); 56667754Smsmith 56767754Smsmith DPRINTFN(0, "Eject CD command " 56867754Smsmith "status: %s\n", usb2_errstr(err)); 569167802Sjkim } 57067754Smsmith err = 0; 571167802Sjkim goto done; 572167802Sjkim } 57367754Smsmith } else if ((err != 2) && --timeout) { 57467754Smsmith usb2_pause_mtx(&sc->mtx, USB_MS_HZ); 57567754Smsmith goto repeat_inquiry; 57667754Smsmith } 57767754Smsmith err = USB_ERR_INVAL; 57867754Smsmith goto done; 57967754Smsmith 58067754Smsmithdone: 58167754Smsmith mtx_unlock(&sc->mtx); 58267754Smsmith usb2_transfer_unsetup(sc->xfer, ST_MAX); 583123315Snjl mtx_destroy(&sc->mtx); 58467754Smsmith usb2_cv_destroy(&sc->cv); 58569746Smsmith free(sc, M_USB); 58669746Smsmith return (err); 587167802Sjkim} 58869746Smsmith 58967754Smsmith/* 59087031Smsmith * NOTE: The entries marked with XXX should be checked for the correct 591167802Sjkim * speed indication to set the buffer sizes. 59269746Smsmith */ 593167802Sjkimstatic const struct usb2_device_id u3g_devs[] = { 594167802Sjkim /* OEM: Option */ 595167802Sjkim {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, 596167802Sjkim {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, 597167802Sjkim {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, 598167802Sjkim {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, 59967754Smsmith {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAXHSUPA, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, 600167802Sjkim {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, 601209746Sjkim /* OEM: Qualcomm, Inc. */ 602117521Snjl {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_ZTE_STOR, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, 603167802Sjkim {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, 604167802Sjkim /* OEM: Huawei */ 605167802Sjkim {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, U3GINFO(U3GSP_HSDPA, U3GFL_HUAWEI_INIT))}, 606167802Sjkim {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220, U3GINFO(U3GSP_HSPA, U3GFL_HUAWEI_INIT))}, 607167802Sjkim /* OEM: Novatel */ 608167802Sjkim {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, 60967754Smsmith {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ 61067754Smsmith {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MC950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, 61167754Smsmith {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ 61267754Smsmith {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ 61367754Smsmith {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, 61467754Smsmith {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, 61567754Smsmith {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ 61667754Smsmith {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ 61767754Smsmith {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V640, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ 61867754Smsmith {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ 61967754Smsmith {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, 62067754Smsmith {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, 62167754Smsmith {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, 62267754Smsmith {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ZEROCD, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, 62367754Smsmith {USB_VPI(USB_VENDOR_DELL, USB_PRODUCT_DELL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, 62467754Smsmith /* OEM: Merlin */ 62567754Smsmith {USB_VPI(USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 626151937Sjkim /* OEM: Sierra Wireless: */ 627151937Sjkim {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 62867754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 62967754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 63067754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 63167754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 63267754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 63367754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 63467754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 63567754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 63667754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 63767754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 63867754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 63967754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 64069450Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 64167754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 64267754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 64399679Siwasaki {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 64467754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 64569450Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 64667754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 647123315Snjl {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 64867754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 64967754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 65067754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 65167754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ 65267754Smsmith /* Sierra TruInstaller device ID */ 65367754Smsmith {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_TRUINSTALL, U3GINFO(U3GSP_UMTS, U3GFL_SIERRA_INIT))}, 65467754Smsmith}; 65567754Smsmith 656117521Snjlstatic void 657117521Snjlu3g_sierra_init(struct usb2_device *udev) 658241973Sjkim{ 659117521Snjl struct usb2_device_request req; 660117521Snjl 661117521Snjl DPRINTFN(0, "\n"); 662123315Snjl 663117521Snjl req.bmRequestType = UT_VENDOR; 664117521Snjl req.bRequest = UR_SET_INTERFACE; 665117521Snjl USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); 666117521Snjl USETW(req.wIndex, UHF_PORT_CONNECTION); 667117521Snjl USETW(req.wLength, 0); 668241973Sjkim 669117521Snjl if (usb2_do_request_flags(udev, NULL, &req, 670241973Sjkim NULL, 0, NULL, USB_MS_HZ)) { 671117521Snjl /* ignore any errors */ 672117521Snjl } 673193267Sjkim return; 674117521Snjl} 675117521Snjl 676241973Sjkimstatic void 677241973Sjkimu3g_huawei_init(struct usb2_device *udev) 678241973Sjkim{ 679241973Sjkim struct usb2_device_request req; 680241973Sjkim 681117521Snjl DPRINTFN(0, "\n"); 682241973Sjkim 683197104Sjkim req.bmRequestType = UT_WRITE_DEVICE; 684117521Snjl req.bRequest = UR_SET_FEATURE; 685197104Sjkim USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); 686197104Sjkim USETW(req.wIndex, UHF_PORT_SUSPEND); 687197104Sjkim USETW(req.wLength, 0); 688197104Sjkim 689117521Snjl if (usb2_do_request_flags(udev, NULL, &req, 690241973Sjkim NULL, 0, NULL, USB_MS_HZ)) { 691241973Sjkim /* ignore any errors */ 692241973Sjkim } 693241973Sjkim return; 694241973Sjkim} 695241973Sjkim 696241973Sjkimint 697241973Sjkimusb2_lookup_huawei(struct usb2_attach_arg *uaa) 698241973Sjkim{ 699241973Sjkim /* Calling the lookup function will also set the driver info! */ 700241973Sjkim return (usb2_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa)); 701241973Sjkim} 702241973Sjkim 703241973Sjkim/* 704241973Sjkim * The following function handles 3G modem devices (E220, Mobile, 705241973Sjkim * etc.) with auto-install flash disks for Windows/MacOSX on the first 706241973Sjkim * interface. After some command or some delay they change appearance 707241973Sjkim * to a modem. 708241973Sjkim */ 709117521Snjlusb2_error_t 710197104Sjkimusb2_test_huawei(struct usb2_device *udev, struct usb2_attach_arg *uaa) 711197104Sjkim{ 712197104Sjkim struct usb2_interface *iface; 713117521Snjl struct usb2_interface_descriptor *id; 714197104Sjkim uint32_t flags; 715117521Snjl 716197104Sjkim if (udev == NULL) { 717117521Snjl return (USB_ERR_INVAL); 718241973Sjkim } 719241973Sjkim iface = usb2_get_iface(udev, 0); 720241973Sjkim if (iface == NULL) { 721241973Sjkim return (USB_ERR_INVAL); 722197104Sjkim } 723197104Sjkim id = iface->idesc; 724197104Sjkim if (id == NULL) { 725197104Sjkim return (USB_ERR_INVAL); 726209746Sjkim } 727197104Sjkim if (id->bInterfaceClass != UICLASS_MASS) { 728197104Sjkim return (USB_ERR_INVAL); 729197104Sjkim } 730197104Sjkim if (usb2_lookup_huawei(uaa)) { 731197104Sjkim /* no device match */ 732117521Snjl return (USB_ERR_INVAL); 733117521Snjl } 734117521Snjl flags = USB_GET_DRIVER_INFO(uaa); 735117521Snjl 736117521Snjl if (flags & U3GFL_HUAWEI_INIT) { 73767754Smsmith u3g_huawei_init(udev); 73867754Smsmith } else if (flags & U3GFL_SCSI_EJECT) { 73967754Smsmith return (usb2_test_autoinstall(udev, 0, 1)); 74067754Smsmith } else if (flags & U3GFL_SIERRA_INIT) { 74167754Smsmith u3g_sierra_init(udev); 74267754Smsmith } else { 74367754Smsmith /* no quirks */ 74467754Smsmith return (USB_ERR_INVAL); 74567754Smsmith } 74691116Smsmith return (0); /* success */ 74791116Smsmith} 74891116Smsmith