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
This commit is contained in:
Hans Petter Selasky 2011-09-02 18:50:44 +00:00
parent ea96972a8b
commit d46dc4ad74
11 changed files with 149 additions and 34 deletions

View File

@ -148,12 +148,10 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
UQ_MSC_FORCE_PROTO_SCSI),
USB_QUIRK(AIPTEK, POCKETCAM3M, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI),
USB_QUIRK(AIPTEK2, SUNPLUS_TECH, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(ALCOR, SDCR_6335, 0x0000, 0xffff, UQ_MSC_NO_TEST_UNIT_READY,
UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(ALCOR, SDCR_6362, 0x0000, 0xffff, UQ_MSC_NO_TEST_UNIT_READY,
UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(ALCOR, AU6390, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(ALCOR, UMCR_9361, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_GETMAXLUN),
USB_QUIRK(ALCOR, TRANSCEND, 0x0000, 0xffff, UQ_MSC_NO_GETMAXLUN,
@ -173,14 +171,12 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
USB_QUIRK(CENTURY, EX35QUAT, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_FORCE_SHORT_INQ,
UQ_MSC_NO_START_STOP, UQ_MSC_IGNORE_RESIDUE),
USB_QUIRK(CENTURY, EX35SW4_SB4, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(CYPRESS, XX6830XX, 0x0000, 0xffff, UQ_MSC_NO_GETMAXLUN,
UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(DESKNOTE, UCR_61S2B, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI),
USB_QUIRK(DMI, CFSM_RW, 0x0000, 0xffff, UQ_MSC_FORCE_PROTO_SCSI,
UQ_MSC_NO_GETMAXLUN),
USB_QUIRK(DMI, DISK, 0x000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(EPSON, STYLUS_875DC, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_CBI,
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_INQUIRY),
USB_QUIRK(EPSON, STYLUS_895, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
@ -188,7 +184,6 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
USB_QUIRK(FEIYA, 5IN1, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI),
USB_QUIRK(FREECOM, DVD, 0x0000, 0xffff, UQ_MSC_FORCE_PROTO_SCSI),
USB_QUIRK(FREECOM, HDD, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(FUJIPHOTO, MASS0100, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_CBI_I,
UQ_MSC_FORCE_PROTO_ATAPI, UQ_MSC_NO_RS_CLEAR_UA, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(GENESYS, GL641USB2IDE, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
@ -232,7 +227,6 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
USB_QUIRK(IOMEGA, ZIP100, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI,
UQ_MSC_NO_TEST_UNIT_READY), /* XXX ZIP drives can also use ATAPI */
USB_QUIRK(JMICRON, JM20336, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(JMICRON, JM20337, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI,
UQ_MSC_NO_SYNC_CACHE),
@ -279,8 +273,6 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
UQ_MSC_FORCE_PROTO_ATAPI),
USB_QUIRK(MYSON, HEDEN, 0x0000, 0xffff, UQ_MSC_IGNORE_RESIDUE,
UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(MYSON, HEDEN_8813, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(MYSON, STARREADER, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(NEODIO, ND3260, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_FORCE_SHORT_INQ),
USB_QUIRK(NETAC, CF_CARD, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
@ -317,7 +309,6 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
USB_QUIRK(PANASONIC, KXLCB35AN, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI),
USB_QUIRK(PANASONIC, LS120CAM, 0x0000, 0xffff, UQ_MSC_FORCE_PROTO_UFI),
USB_QUIRK(PHILIPS, SPE3030CC, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(PLEXTOR, 40_12_40U, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_TEST_UNIT_READY),
USB_QUIRK(PNY, ATTACHE2, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
@ -328,7 +319,6 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
USB_QUIRK_VP(USB_VENDOR_SAMSUNG_TECHWIN,
USB_PRODUCT_SAMSUNG_TECHWIN_DIGIMAX_410, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_INQUIRY),
USB_QUIRK(SAMSUNG, YP_U4, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(SANDISK, SDDR05A, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_CBI,
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_READ_CAP_OFFBY1,
UQ_MSC_NO_GETMAXLUN),
@ -448,12 +438,6 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
UQ_MSC_FORCE_PROTO_ATAPI),
USB_QUIRK(MEIZU, M6_SL, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_INQUIRY, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(ACTIONS, MP4, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(ASUS, GMSC, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(CHIPSBANK, USBMEMSTICK, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(CHIPSBANK, USBMEMSTICK1, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(NEWLINK, USB2IDEBRIDGE, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
/* Non-standard USB MIDI devices */
USB_QUIRK(ROLAND, UM1, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS),
@ -567,9 +551,9 @@ usb_test_quirk_by_info(const struct usbd_lookup_info *info, uint16_t quirk)
uint16_t x;
uint16_t y;
if (quirk == UQ_NONE) {
return (0);
}
if (quirk == UQ_NONE)
goto done;
mtx_lock(&usb_quirk_mtx);
for (x = 0; x != USB_DEV_QUIRKS_MAX; x++) {
@ -603,7 +587,8 @@ usb_test_quirk_by_info(const struct usbd_lookup_info *info, uint16_t quirk)
break;
}
mtx_unlock(&usb_quirk_mtx);
return (0);
done:
return (usb_test_quirk_w(info, quirk));
}
static struct usb_quirk_entry *

View File

@ -1025,12 +1025,6 @@ umass_attach(device_t dev)
sc->cam_scsi_sense.opcode = REQUEST_SENSE;
sc->cam_scsi_test_unit_ready.opcode = TEST_UNIT_READY;
/*
* some devices need a delay after that the configuration value is
* set to function properly:
*/
usb_pause_mtx(NULL, hz);
/* register the SIM */
err = umass_cam_attach_sim(sc);
if (err) {

View File

@ -1239,7 +1239,9 @@ static void
usb_init_attach_arg(struct usb_device *udev,
struct usb_attach_arg *uaa)
{
bzero(uaa, sizeof(*uaa));
uint8_t x;
memset(uaa, 0, sizeof(*uaa));
uaa->device = udev;
uaa->usb_mode = udev->flags.usb_mode;
@ -1254,6 +1256,9 @@ usb_init_attach_arg(struct usb_device *udev,
uaa->info.bDeviceProtocol = udev->ddesc.bDeviceProtocol;
uaa->info.bConfigIndex = udev->curr_config_index;
uaa->info.bConfigNum = udev->curr_config_no;
for (x = 0; x != USB_MAX_AUTO_QUIRK; x++)
uaa->info.autoQuirk[x] = udev->autoQuirk[x];
}
/*------------------------------------------------------------------------*
@ -1850,6 +1855,20 @@ repeat_set_config:
}
}
}
if (set_config_failed == 0 && config_index == 0 &&
usb_test_quirk(&uaa, UQ_MSC_NO_SYNC_CACHE) == 0) {
/*
* Try to figure out if there are any MSC quirks we
* should apply automatically:
*/
err = usb_msc_auto_quirk(udev, 0);
if (err != 0) {
set_config_failed = 1;
goto repeat_set_config;
}
}
config_done:
DPRINTF("new dev (addr %d), udev=%p, parent_hub=%p\n",
@ -2698,3 +2717,16 @@ usbd_set_pnpinfo(struct usb_device *udev, uint8_t iface_index, const char *pnpin
return (0); /* success */
}
usb_error_t
usbd_add_dynamic_quirk(struct usb_device *udev, uint16_t quirk)
{
uint8_t x;
for (x = 0; x != USB_MAX_AUTO_QUIRK; x++) {
if (udev->autoQuirk[x] == 0) {
udev->autoQuirk[x] = quirk;
return (0); /* success */
}
}
return (USB_ERR_NOMEM);
}

View File

@ -149,6 +149,7 @@ struct usb_device {
uint16_t power; /* mA the device uses */
uint16_t langid; /* language for strings */
uint16_t autoQuirk[USB_MAX_AUTO_QUIRK]; /* dynamic quirks */
uint8_t address; /* device addess */
uint8_t device_index; /* device index in "bus->devices" */

View File

@ -50,12 +50,12 @@
#include <dev/usb/usb_process.h>
#include <dev/usb/usb_device.h>
#include <dev/usb/usb_dynamic.h>
#include <dev/usb/quirk/usb_quirk.h>
/* function prototypes */
static usb_handle_req_t usb_temp_get_desc_w;
static usb_temp_setup_by_index_t usb_temp_setup_by_index_w;
static usb_temp_unsetup_t usb_temp_unsetup_w;
static usb_test_quirk_t usb_test_quirk_w;
static usb_quirk_ioctl_t usb_quirk_ioctl_w;
/* global variables */
@ -72,9 +72,19 @@ usb_temp_setup_by_index_w(struct usb_device *udev, uint16_t index)
return (USB_ERR_INVAL);
}
static uint8_t
uint8_t
usb_test_quirk_w(const struct usbd_lookup_info *info, uint16_t quirk)
{
uint8_t x;
if (quirk == UQ_NONE)
return (0); /* no match */
for (x = 0; x != USB_MAX_AUTO_QUIRK; x++) {
if (info->autoQuirk[x] == quirk)
return (1); /* match */
}
return (0); /* no match */
}

View File

@ -57,5 +57,6 @@ extern devclass_t usb_devclass_ptr;
void usb_temp_unload(void *);
void usb_quirk_unload(void *);
void usb_bus_unload(void *);
usb_test_quirk_t usb_test_quirk_w;
#endif /* _USB_DYNAMIC_H_ */

View File

@ -68,6 +68,8 @@
#define USB_EP0_BUFSIZE 1024 /* bytes */
#define USB_CS_RESET_LIMIT 20 /* failures = 20 * 50 ms = 1sec */
#define USB_MAX_AUTO_QUIRK 4 /* maximum number of dynamic quirks */
typedef uint32_t usb_timeout_t; /* milliseconds */
typedef uint32_t usb_frlength_t; /* bytes */
typedef uint32_t usb_frcount_t; /* units */

View File

@ -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)
@ -594,6 +597,88 @@ usb_iface_is_cdrom(struct usb_device *udev, uint8_t iface_index)
return (is_cdrom);
}
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)
{

View File

@ -40,5 +40,7 @@ int usb_iface_is_cdrom(struct usb_device *udev,
uint8_t iface_index);
usb_error_t usb_msc_eject(struct usb_device *udev,
uint8_t iface_index, int method);
usb_error_t usb_msc_auto_quirk(struct usb_device *udev,
uint8_t iface_index);
#endif /* _USB_MSCTEST_H_ */

View File

@ -353,6 +353,7 @@ struct usbd_lookup_info {
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint16_t autoQuirk[USB_MAX_AUTO_QUIRK];
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
@ -475,6 +476,8 @@ void device_set_usb_desc(device_t dev);
void usb_pause_mtx(struct mtx *mtx, int _ticks);
usb_error_t usbd_set_pnpinfo(struct usb_device *udev,
uint8_t iface_index, const char *pnpinfo);
usb_error_t usbd_add_dynamic_quirk(struct usb_device *udev,
uint16_t quirk);
const struct usb_device_id *usbd_lookup_id_by_info(
const struct usb_device_id *id, usb_size_t sizeof_id,

View File

@ -58,7 +58,7 @@
* in the range 5 to 9.
*/
#undef __FreeBSD_version
#define __FreeBSD_version 900042 /* Master, propagated to newvers */
#define __FreeBSD_version 900043 /* Master, propagated to newvers */
#ifdef _KERNEL
#define P_OSREL_SIGSEGV 700004