summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/usb_msctest.c
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2011-09-02 18:50:44 +0000
committerhselasky <hselasky@FreeBSD.org>2011-09-02 18:50:44 +0000
commit8335f4b89d8e67c285a31d6b11a370185ce0b949 (patch)
treec6f2687acbbed316803ce152679d1c321f8174e7 /sys/dev/usb/usb_msctest.c
parent56a7a5d12f3bd0a91a39b86bb619324e35c2c6b9 (diff)
downloadFreeBSD-src-8335f4b89d8e67c285a31d6b11a370185ce0b949.zip
FreeBSD-src-8335f4b89d8e67c285a31d6b11a370185ce0b949.tar.gz
This patch adds automatic detection of USB mass storage devices
which does not support the no synchronize cache SCSI command. The __FreeBSD_version version macro has been bumped and external kernel modules needs to be recompiled after this patch. Approved by: re (kib) MFC after: 1 week PR: usb/160299
Diffstat (limited to 'sys/dev/usb/usb_msctest.c')
-rw-r--r--sys/dev/usb/usb_msctest.c93
1 files changed, 89 insertions, 4 deletions
diff --git a/sys/dev/usb/usb_msctest.c b/sys/dev/usb/usb_msctest.c
index 0b6024b..061c3d3 100644
--- a/sys/dev/usb/usb_msctest.c
+++ b/sys/dev/usb/usb_msctest.c
@@ -96,6 +96,8 @@ static uint8_t scsi_huawei_eject[] = { 0x11, 0x06, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 };
static uint8_t scsi_tct_eject[] = { 0x06, 0xf5, 0x04, 0x02, 0x52, 0x70 };
+static uint8_t scsi_sync_cache[] = { 0x35, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00 };
#define BULK_SIZE 64 /* dummy */
#define ERR_CSW_FAILED -1
@@ -163,7 +165,7 @@ static void bbb_done(struct bbb_transfer *, int);
static void bbb_transfer_start(struct bbb_transfer *, uint8_t);
static void bbb_data_clear_stall_callback(struct usb_xfer *, uint8_t,
uint8_t);
-static uint8_t bbb_command_start(struct bbb_transfer *, uint8_t, uint8_t,
+static int bbb_command_start(struct bbb_transfer *, uint8_t, uint8_t,
void *, size_t, void *, size_t, usb_timeout_t);
static struct bbb_transfer *bbb_attach(struct usb_device *, uint8_t);
static void bbb_detach(struct bbb_transfer *);
@@ -454,7 +456,7 @@ bbb_status_callback(struct usb_xfer *xfer, usb_error_t error)
* 0: Success
* Else: Failure
*------------------------------------------------------------------------*/
-static uint8_t
+static int
bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun,
void *data_ptr, size_t data_len, void *cmd_ptr, size_t cmd_len,
usb_timeout_t data_timeout)
@@ -566,9 +568,10 @@ int
usb_iface_is_cdrom(struct usb_device *udev, uint8_t iface_index)
{
struct bbb_transfer *sc;
- usb_error_t err;
- uint8_t timeout, is_cdrom;
+ uint8_t timeout;
+ uint8_t is_cdrom;
uint8_t sid_type;
+ int err;
sc = bbb_attach(udev, iface_index);
if (sc == NULL)
@@ -595,6 +598,88 @@ usb_iface_is_cdrom(struct usb_device *udev, uint8_t iface_index)
}
usb_error_t
+usb_msc_auto_quirk(struct usb_device *udev, uint8_t iface_index)
+{
+ struct bbb_transfer *sc;
+ uint8_t timeout;
+ uint8_t is_no_direct;
+ uint8_t sid_type;
+ int err;
+
+ sc = bbb_attach(udev, iface_index);
+ if (sc == NULL)
+ return (0);
+
+ /*
+ * Some devices need a delay after that the configuration
+ * value is set to function properly:
+ */
+ usb_pause_mtx(NULL, hz);
+
+ is_no_direct = 1;
+ for (timeout = 4; timeout; timeout--) {
+ err = bbb_command_start(sc, DIR_IN, 0, sc->buffer,
+ SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry),
+ USB_MS_HZ);
+
+ if (err == 0 && sc->actlen > 0) {
+ sid_type = sc->buffer[0] & 0x1F;
+ if (sid_type == 0x00)
+ is_no_direct = 0;
+ break;
+ } else if (err != ERR_CSW_FAILED)
+ break; /* non retryable error */
+ usb_pause_mtx(NULL, hz);
+ }
+
+ if (is_no_direct) {
+ DPRINTF("Device is not direct access.\n");
+ goto done;
+ }
+
+ err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
+ &scsi_test_unit_ready, sizeof(scsi_test_unit_ready),
+ USB_MS_HZ);
+
+ if (err != 0) {
+
+ if (err != ERR_CSW_FAILED)
+ goto error;
+ }
+
+ err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
+ &scsi_sync_cache, sizeof(scsi_sync_cache),
+ USB_MS_HZ);
+
+ if (err != 0) {
+
+ if (err != ERR_CSW_FAILED)
+ goto error;
+
+ DPRINTF("Device doesn't handle synchronize cache\n");
+
+ usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE);
+ }
+
+done:
+ bbb_detach(sc);
+ return (0);
+
+error:
+ bbb_detach(sc);
+
+ DPRINTF("Device did not respond, enabling all quirks\n");
+
+ usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE);
+ usbd_add_dynamic_quirk(udev, UQ_MSC_NO_TEST_UNIT_READY);
+
+ /* Need to re-enumerate the device */
+ usbd_req_re_enumerate(udev, NULL);
+
+ return (USB_ERR_STALLED);
+}
+
+usb_error_t
usb_msc_eject(struct usb_device *udev, uint8_t iface_index, int method)
{
struct bbb_transfer *sc;
OpenPOWER on IntegriCloud